@skill-map/spec 0.38.0 → 0.40.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 +48 -2302
- package/README.md +2 -2
- package/architecture.md +11 -14
- package/cli-contract.md +5 -1
- package/conformance/README.md +1 -1
- package/conformance/cases/extractor-emits-signal.json +1 -1
- package/conformance/cases/kernel-empty-boot.json +1 -1
- package/conformance/cases/no-global-scope.json +1 -1
- package/conformance/cases/orphan-markdown-fallback.json +1 -1
- package/conformance/cases/plugin-missing-ui-rejected.json +1 -1
- package/conformance/cases/sidecar-end-to-end.json +1 -1
- package/conformance/cases/signal-collision-detection.json +1 -1
- package/conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/providers/bad-provider/index.js +4 -0
- package/conformance/fixtures/sidecar-example/agent-example.sm +3 -3
- package/db-schema.md +19 -5
- package/index.json +55 -55
- package/package.json +2 -2
- package/schemas/annotations.schema.json +2 -2
- package/schemas/api/rest-envelope.schema.json +57 -12
- package/schemas/bump-report.schema.json +1 -1
- package/schemas/conformance-case.schema.json +1 -1
- package/schemas/conformance-result.schema.json +1 -1
- package/schemas/execution-record.schema.json +1 -1
- package/schemas/extensions/action.schema.json +1 -1
- package/schemas/extensions/analyzer.schema.json +1 -1
- package/schemas/extensions/base.schema.json +1 -1
- package/schemas/extensions/extractor.schema.json +1 -1
- package/schemas/extensions/formatter.schema.json +1 -1
- package/schemas/extensions/hook.schema.json +1 -1
- package/schemas/extensions/provider-kind.schema.json +1 -1
- package/schemas/extensions/provider.schema.json +80 -2
- package/schemas/frontmatter/base.schema.json +2 -7
- package/schemas/history-stats.schema.json +1 -1
- package/schemas/input-types.schema.json +1 -1
- package/schemas/issue.schema.json +1 -1
- package/schemas/job.schema.json +1 -1
- package/schemas/link.schema.json +1 -1
- package/schemas/node.schema.json +1 -1
- package/schemas/plugins-doctor.schema.json +1 -1
- package/schemas/plugins-registry.schema.json +1 -1
- package/schemas/project-config.schema.json +1 -1
- package/schemas/refresh-report.schema.json +1 -1
- package/schemas/report-base-deterministic.schema.json +1 -1
- package/schemas/report-base.schema.json +1 -1
- package/schemas/scan-result.schema.json +1 -1
- package/schemas/sidecar.schema.json +1 -1
- package/schemas/signal.schema.json +1 -1
- package/schemas/summaries/agent.schema.json +1 -1
- package/schemas/summaries/command.schema.json +1 -1
- package/schemas/summaries/hook.schema.json +1 -1
- package/schemas/summaries/markdown.schema.json +1 -1
- package/schemas/summaries/skill.schema.json +1 -1
- package/schemas/user-settings.schema.json +1 -1
- package/schemas/view-slots.schema.json +1 -1
- package/versioning.md +2 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/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
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/bump-report.schema.json",
|
|
4
4
|
"title": "BumpReport",
|
|
5
5
|
"description": "Report shape produced by the built-in deterministic `node-bump` Action (Step 9.6.3, Decision #125). Extends `report-base-deterministic.schema.json` (the deterministic counterpart to `report-base.schema.json`, which carries the LLM-only `confidence` + `safety` fields). The `node-bump` Action returns one of three concrete shapes, distinguished by `ok` / `noop` / `reason`: success-with-write (`{ ok: true, version }`), silent-no-op under `force` (`{ ok: true, noop: true }`), or refusal (`{ ok: false, reason: 'fresh' }`).",
|
|
6
6
|
"allOf": [{ "$ref": "report-base-deterministic.schema.json" }],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/conformance-case.schema.json",
|
|
4
4
|
"title": "ConformanceCase",
|
|
5
5
|
"description": "Shape of a single declarative test case under `spec/conformance/cases/<id>.json`. Consumed by language-neutral conformance runners. See `spec/conformance/README.md` for the runner contract.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/conformance-result.schema.json",
|
|
4
4
|
"title": "ConformanceResult",
|
|
5
5
|
"description": "Machine-readable output of `sm conformance run --json`. Aggregates pass / fail totals across the selected scope set plus per-scope and per-case breakdowns. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/execution-record.schema.json",
|
|
4
4
|
"title": "ExecutionRecord",
|
|
5
5
|
"description": "A single row in the execution history (`state_executions`). One record per action invocation, regardless of whether the runner was CLI, Skill, or in-process.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/action.schema.json",
|
|
4
4
|
"title": "ExtensionAction",
|
|
5
5
|
"description": "Manifest shape for an `Action` extension. An action operates on one or more nodes in one of two modes: `deterministic` (code runs in-process, returns a report JSON directly) or `probabilistic` (kernel renders a prompt, a runner executes it against an LLM, the callback closes the job). **Structure-as-truth files**: every Action carries `<action-dir>/report.schema.json` (the JSON Schema for the report, MUST extend `report-base.schema.json`); probabilistic Actions additionally carry `<action-dir>/prompt.md` (the prompt template). The kernel resolves both by convention; missing or mis-placed files surface as `load-error`. A deterministic Action with a `prompt.md` in its folder is also `load-error` (config inconsistent). **`prob*` prefix convention**: manifest fields that only apply when `mode=probabilistic` start with `prob`; if a deterministic-only field ever appears, it starts with `det`.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/analyzer.schema.json",
|
|
4
4
|
"title": "ExtensionAnalyzer",
|
|
5
5
|
"description": "Manifest shape for an `Analyzer` extension. An analyzer consumes the full graph (nodes + links) after all extractors have run, emits `Issue[]`, and MAY emit view contributions to project findings into the UI. Analyzers are dual-mode: `deterministic` analyzers MUST be byte-for-byte reproducible (same graph in → same issues out; time, random, and network are forbidden) and run synchronously inside `sm check` / `sm scan`; `probabilistic` analyzers invoke an LLM through the kernel's `RunnerPort` and execute only as queued jobs (`sm job submit analyzer:<id>`); their output MAY vary across runs and they NEVER participate in `sm scan`. Each issue emitted is tagged with `analyzer_id = <plugin-id>/<extension-id>` by default (the extension's qualified id, derived from structure); analyzers that need to discriminate sub-types append `:<sub-id>` at emit time. Severity is set per-emit (no manifest-level default).",
|
|
6
6
|
"allOf": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/base.schema.json",
|
|
4
4
|
"title": "ExtensionBase",
|
|
5
5
|
"description": "Base manifest shape common to every extension kind. Kind-specific schemas (`provider`, `extractor`, `analyzer`, `action`, `formatter`, `hook`) extend this via `allOf` and add a kind-specific shape. Both `id` and `kind` are derived from the filesystem structure (`<plugin>/<kind-plural>/<id>/index.ts`, where the parent folder dictates the kind and the leaf folder dictates the id), so they are NOT manifest fields. Manifests carrying `id` or `kind` are rejected as `invalid-manifest`. Closed-content enforcement (unknown keys = bug) lives on the kind schemas via `unevaluatedProperties: false`; those see base's evaluated keys through the `allOf` composition.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/extractor.schema.json",
|
|
4
4
|
"title": "ExtensionExtractor",
|
|
5
5
|
"description": "Manifest shape for an `Extractor` extension. An extractor consumes a parsed node (frontmatter + body) and emits output through three context-supplied callbacks rather than returning a value: `ctx.emitLink(link)` writes to the kernel's `links` table (validated against the global closed enum of link kinds before persistence; per-extractor whitelisting was retired with structure-as-truth, the global enum is the contract), `ctx.enrichNode(partial)` merges author-canonical properties into the kernel's enrichment layer (separate from the author-supplied frontmatter), `ctx.emitContribution(id, payload)` emits per-node view contributions validated against the slot payload schema, and `ctx.store` persists into the plugin's own KV namespace or dedicated tables. The runtime method is `extract(ctx) → void`. Extractors run in isolation: they MUST NOT read other nodes, the graph, or the DB. Cross-node reasoning lives in Analyzers. Extractors are deterministic-only: pure code, runs synchronously inside `sm scan`, same input → same output every run. LLM-driven enrichment of a node is an Action concern (queued as a job), not an Extractor concern.",
|
|
6
6
|
"allOf": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/formatter.schema.json",
|
|
4
4
|
"title": "ExtensionFormatter",
|
|
5
5
|
"description": "Manifest shape for a `Formatter` extension. A formatter serializes the graph (or a filtered subgraph) into a string in a declared format. The format id comes from the formatter's folder name (structure-as-truth, `<plugin>/formatters/<formatId>/index.ts`), it is NOT a manifest field. Invoked by `sm graph --format <formatId>` and `sm export`. Formatters are deterministic-only, they sit at the graph-to-string boundary and their output MUST be byte-deterministic for the same input graph (the snapshot-test suite relies on this). The `mode` field MUST NOT appear in formatter manifests. Probabilistic narrators of the graph are a valid product but they live in jobs and emit Findings, not in formatters. All formatters accept the `--filter` expression; opting out is no longer supported.",
|
|
6
6
|
"allOf": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/hook.schema.json",
|
|
4
4
|
"title": "ExtensionHook",
|
|
5
5
|
"description": "Manifest shape for a `Hook` extension. Subscribes declaratively to a curated set of kernel lifecycle events. **Hooks are deterministic-only** since the structure-as-truth refactor: the `mode` field was removed; LLM-dependent lifecycle behaviour is modeled as a deterministic hook that enqueues a probabilistic Action via `ctx.queue('<plugin>/<action>', payload)`. Hooks react to events; they cannot block or alter the main pipeline. The set of hookable triggers is intentionally small, ten events out of the full job-events catalog. Eight are pipeline-driven (emitted from inside `runScan`); two (`boot`, `shutdown`) are CLI-process-driven (emitted by the driving binary before / after the verb runs, fire-and-forget so `process.exit` is never blocked). Other events (per-node `scan.progress`, `model.delta`, `run.*`, internal job lifecycle) are deliberately not hookable: too verbose for a reactive surface, internal to the runner, or covered elsewhere. Declaring a trigger outside the hookable set yields `invalid-manifest` at load time.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/provider-kind.schema.json",
|
|
4
4
|
"title": "ProviderKindMetadata",
|
|
5
5
|
"description": "Per-kind UI metadata written as `<plugin>/kinds/<kindName>/kind.json`. Lives next to the kind's frontmatter `schema.json` under the kind folder; together they are the structure-as-truth replacement for the old `kinds` map inside the Provider manifest. Reaches the UI via the `kindRegistry` field embedded in REST envelopes (`api/rest-envelope.schema.json`). The kind name is the folder name; it is NOT repeated as a field here.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,15 +1,93 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/extensions/provider.schema.json",
|
|
4
4
|
"title": "ExtensionProvider",
|
|
5
5
|
"description": "Manifest shape for a `Provider` extension. A Provider declares its own universe: the platform it recognises (Claude Code, Codex, Antigravity, Obsidian vault, generic MD), the catalog of node `kind`s it emits, and the per-kind frontmatter schema each kind follows. **Structure-as-truth**: exactly one Provider lives in each plugin that carries one, declared as `<plugin>/provider.ts`. The kinds catalog lives as folders under `<plugin>/kinds/<kindName>/` and the loader discovers each entry by walking that directory; the manifest itself NO LONGER carries a `kinds` map. Each kind folder MUST contain `schema.json` (the kind's frontmatter JSON Schema, extending `frontmatter/base.schema.json` via `allOf` + `$ref`) and `kind.json` (UI metadata under `{ ui: {...} }`). The kernel resolves these at boot time and registers each schema with AJV for scan-time validation. Exactly zero or one Provider MUST match any given file; multiple matches → `provider-ambiguous` issue, file unclassified. **`roots` is enforcement-grade**: a Provider declaring `roots` only receives files matching at least one glob; a Provider without `roots` acts as a fallback for files unmatched by any other Provider's roots. Providers are deterministic-only, they sit at the filesystem boundary and run during boot; probabilistic classification would make boot slow, costly, and non-reproducible. The `mode` field MUST NOT appear in Provider manifests. If you need LLM-assisted classification, write a probabilistic Action that runs as a queued job and writes back through the enrichment layer; Extractors are deterministic-only and Providers stay on the deterministic boot path. Distinct from the **hexagonal-architecture** 'adapter' (`RunnerPort.adapter`, `StoragePort.adapter`, etc.), which is an internal driven-adapter implementing a port, Providers live in the extension surface, hexagonal adapters live in `src/kernel/adapters/`.",
|
|
6
6
|
"allOf": [
|
|
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.",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/frontmatter/base.schema.json",
|
|
4
4
|
"title": "FrontmatterBase",
|
|
5
|
-
"description": "Universal frontmatter shape every Provider's per-kind schema extends via `allOf` + `$ref` to this `$id`. `name` and `description` are the
|
|
5
|
+
"description": "Universal frontmatter shape every Provider's per-kind schema extends via `allOf` + `$ref` to this `$id`. `name` and `description` are the only universal fields, confirmed by cross-vendor research: `description` is the single field every format carries, and `name` is universal among formats with explicit identifiers. Everything else is vendor idiosyncrasy and lives on the per-vendor per-kind schema, NOT here. (Taxonomy `tags`, for example, is a skill-map concept with no vendor frontmatter analog, so it lives in the `.sm` sidecar `annotations.tags`, not here.) Per-vendor schemas (Anthropic Claude, Cursor, Obsidian, the Agent Skills open standard, …) declare their own fields on top via the per-kind extension. `additionalProperties: true` is intentional: skill-map AGGREGATES vendor specs, it does not curate them. Vendor-specific fields (`tools`, `allowedTools`, `model`, etc.) flow through validation silently because the per-kind extension declares them.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["name", "description"],
|
|
8
8
|
"additionalProperties": true,
|
|
@@ -16,11 +16,6 @@
|
|
|
16
16
|
"type": "string",
|
|
17
17
|
"minLength": 1,
|
|
18
18
|
"description": "One-to-three-sentence description. REQUIRED."
|
|
19
|
-
},
|
|
20
|
-
"tags": {
|
|
21
|
-
"type": "array",
|
|
22
|
-
"items": { "type": "string", "minLength": 1 },
|
|
23
|
-
"description": "**Author-supplied** taxonomy tags written in the markdown frontmatter, what the file's author considers the node's intrinsic categories. Skill-map's tag system is **dual-source**: this field carries author tags; `sidecar.annotations.tags` carries user tags (post-hoc, written by whoever curates the project from their `.sm` sidecar). Both surfaces are first-class, searches and listings (`sm list --tag`, UI faceted search) match the union and the UI distinguishes them visually so the attribution stays explicit. Empty array and missing field are equivalent."
|
|
24
19
|
}
|
|
25
20
|
}
|
|
26
21
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/history-stats.schema.json",
|
|
4
4
|
"title": "HistoryStats",
|
|
5
5
|
"description": "Machine-readable output of `sm history stats --json`. Aggregates over `state_executions` within a time window. camelCase keys throughout. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time), distinct from `totals.durationMsTotal`, which is the sum of every execution record's duration.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/input-types.schema.json",
|
|
4
4
|
"title": "InputTypes",
|
|
5
5
|
"description": "Closed catalog of input-types for plugin settings. The plugin author declares each user-configurable setting in the manifest's `settings` map by picking an `input-type` from this catalog; the kernel knows the schema for each type, the UI ships a generated form per type, and the CLI's `sm plugins config <id>` command exposes the same surface. Plugin authors NEVER write JSON Schema for settings, they pick a type by name and supply per-type parameters (label, default, min/max, options for enums, etc.). Closed catalog by design: every new input-type requires spec + UI form + CLI prompter + tests. Versioned via the manifest field `catalogCompat` (semver against the catalog as a whole). For the rationale and open issues, see ROADMAP.md §UI contribution system.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/issue.schema.json",
|
|
4
4
|
"title": "Issue",
|
|
5
5
|
"description": "Deterministic finding emitted by a analyzer when evaluating the graph. Not to be confused with `Finding`, which is probabilistic (LLM-produced).",
|
|
6
6
|
"type": "object",
|
package/schemas/job.schema.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/job.schema.json",
|
|
4
4
|
"title": "Job",
|
|
5
5
|
"description": "Row in `state_jobs`. Non-terminal state until it reaches `completed` or `failed`, at which point an `ExecutionRecord` is also written.",
|
|
6
6
|
"type": "object",
|
package/schemas/link.schema.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/link.schema.json",
|
|
4
4
|
"title": "Link",
|
|
5
5
|
"description": "Directed relation between two nodes, produced by one or more extractors during a scan.",
|
|
6
6
|
"type": "object",
|
package/schemas/node.schema.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/node.schema.json",
|
|
4
4
|
"title": "Node",
|
|
5
5
|
"description": "A single entity in the graph. Typically a file on disk (a markdown skill, an agent, a TOML sub-agent definition, a plain-markdown note), but MAY also be a **virtual / derived** entity that lives only in memory and is reconstructed from one or more source files on every scan (e.g. an MCP server node derived from `settings.json` / `mcp.json` / `config.toml`). Virtual nodes carry `virtual: true` and use a synthetic `path` scheme (`mcp://<name>`, etc.). The `kind` is whatever the classifying Provider declares, open by design; the **built-in Claude Provider** emits `skill` / `agent` / `command` / `markdown` today, but external Providers (Cursor, Obsidian, …) MAY emit their own. Format-named kinds (`markdown`, future `toml`, future `json`) are reserved for the generic fallback only, when a file matches a specific role (agent / command / skill) that classification prevails over format naming.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/plugins-doctor.schema.json",
|
|
4
4
|
"title": "PluginsDoctorReport",
|
|
5
5
|
"description": "Machine-readable output of `sm plugins doctor --json`. Aggregates per-status counts across built-in and drop-in plugins plus the structured issue / warning lists the human renderer produces. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/plugins-registry.schema.json",
|
|
4
4
|
"title": "PluginsRegistry",
|
|
5
5
|
"description": "Two shapes in one file: (1) the per-plugin manifest that authors ship as `plugin.json` (see `$defs/PluginManifest`); (2) the aggregate registry the implementation produces on disk (`<cwd>/.skill-map/plugins.json`), which lists all discovered plugins with their compat status. Both shapes are normative. camelCase keys throughout.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/project-config.schema.json",
|
|
4
4
|
"title": "ProjectConfig",
|
|
5
5
|
"description": "Shape of `.skill-map/settings.json` (and its `.skill-map/settings.local.json` partner) inside a scope. Loaded by the layered config hierarchy (library defaults → user → user-local → project → project-local → env/flags) and deep-merged per key. All fields optional; defaults apply when absent. camelCase keys throughout, consistent with the rest of the spec.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/refresh-report.schema.json",
|
|
4
4
|
"title": "RefreshReport",
|
|
5
5
|
"description": "Machine-readable output of `sm refresh <node.path> --json` and `sm refresh --stale --json`. Reports the count of enrichment rows persisted across the targeted node set (universal enrichment layer per `architecture.md` §A.8). The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/report-base-deterministic.schema.json",
|
|
4
4
|
"title": "ReportBaseDeterministic",
|
|
5
5
|
"description": "Universal base for deterministic Action reports. Every deterministic Action's report MUST extend this base via `allOf` + `$ref`. Symmetric with `report-base.schema.json` (the probabilistic / LLM base, which carries `confidence` + `safety`); deterministic vs probabilistic is the orthogonal axis declared by the Action manifest's `mode` field. Fields: `ok` (boolean, did the Action complete its logical work?), plus action-specific keys via `additionalProperties: true`. Action-specific shapes (e.g. bump's `version` / `noop` / `reason`) ride on the open extension.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/report-base.schema.json",
|
|
4
4
|
"title": "ReportBase",
|
|
5
5
|
"description": "Base shape for any probabilistic report produced by an LLM-backed action (summarizers, `sm what`, `sm cluster-triggers`, etc.). All per-kind summary schemas under `summaries/` extend this. Kernel validates the `confidence` and `safety` fields regardless of action-specific extensions.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/scan-result.schema.json",
|
|
4
4
|
"title": "ScanResult",
|
|
5
5
|
"description": "Canonical output of `sm scan --json` (and the data shape sent over WebSocket scan events). Self-describing and versioned; consumers MUST check `schemaVersion` before parsing.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/sidecar.schema.json",
|
|
4
4
|
"title": "Sidecar",
|
|
5
5
|
"description": "Root shape of a co-located YAML sidecar (`<basename>.sm` next to `<basename>.md`). The `.sm` file IS the annotations file, every key under it is, conceptually, an annotation on the node. The YAML root organizes those annotations into structural blocks: `identity` (anchor + drift-detection hashes), `annotations` (the curated catalog of conventional fields), `audit` (timestamps), `settings` (reserved), and arbitrary `<plugin-id>:` namespaces for plugin-contributed data. Vendor file (`<basename>.md`) stays untouched. Schema is `additionalProperties: true` so plugins can add namespaces without coordination; the built-in `unknown-field` analyzer warns on truly unrecognized root keys (typo guard). Format is YAML, comments via `#`, multiline strings via `|` / `>`, permissive types per the YAML 1.2 spec. See `architecture.md` §Annotation system and ROADMAP §Step 9.6 for the design rationale.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/signal.schema.json",
|
|
4
4
|
"title": "Signal",
|
|
5
5
|
"description": "Intermediate Representation (IR) emitted by extractors during a scan. A Signal is a *candidate* detection: zero, one, or many interpretations of the same piece of source text or structured data. The kernel's resolver phase consumes `Signal[]` and produces final `Link[]` by selecting a winning candidate per Signal (or rejecting all and emitting none) using the active Provider's resolution rules. Opt-in for plugin authors: an extractor MAY emit `Signal`s via `ctx.emitSignal()` when the detection carries genuine ambiguity (multiple plausible kinds, multiple plausible targets, byte-range awareness for collision detection), OR continue calling `ctx.emitLink()` directly when its detection is unambiguous. The two paths coexist; resolved Link rows look identical regardless of origin. Stability: experimental.",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/summaries/agent.schema.json",
|
|
4
4
|
"title": "SummaryAgent",
|
|
5
5
|
"description": "Report produced by `agent-summarizer` for a single `agent` node. Extends `report-base.schema.json`. Stability: experimental.",
|
|
6
6
|
"allOf": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/summaries/command.schema.json",
|
|
4
4
|
"title": "SummaryCommand",
|
|
5
5
|
"description": "Report produced by `command-summarizer` for a single `command` node. Extends `report-base.schema.json`. Stability: experimental.",
|
|
6
6
|
"allOf": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/summaries/hook.schema.json",
|
|
4
4
|
"title": "SummaryHook",
|
|
5
5
|
"description": "Report produced by `hook-summarizer` for a single `hook` node. Extends `report-base.schema.json`. Stability: experimental.",
|
|
6
6
|
"allOf": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/summaries/markdown.schema.json",
|
|
4
4
|
"title": "SummaryMarkdown",
|
|
5
5
|
"description": "Report produced by `markdown-summarizer` for a single `markdown` node (the format-named generic fallback owned by the built-in `core/markdown` Provider, see `architecture.md` §Provider · dispatch order). Extends `report-base.schema.json`. Stability: experimental.",
|
|
6
6
|
"allOf": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://skill-map.
|
|
3
|
+
"$id": "https://skill-map.ai/spec/v0/summaries/skill.schema.json",
|
|
4
4
|
"title": "SummarySkill",
|
|
5
5
|
"description": "Report produced by `skill-summarizer` for a single `skill` node. Extends `report-base.schema.json`. Stability: experimental, field set may tighten as real summarizer output stabilizes.",
|
|
6
6
|
"allOf": [
|