@skill-map/spec 0.22.0 → 0.24.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 +91 -0
- package/README.md +4 -4
- package/architecture.md +130 -119
- package/cli-contract.md +102 -96
- package/conformance/README.md +13 -13
- package/conformance/coverage.md +41 -38
- package/db-schema.md +45 -45
- package/index.json +40 -37
- package/interfaces/security-scanner.md +20 -20
- package/job-events.md +21 -21
- package/job-lifecycle.md +21 -21
- package/package.json +1 -1
- package/plugin-author-guide.md +133 -108
- package/plugin-kv-api.md +10 -10
- package/prompt-preamble.md +8 -8
- package/schemas/annotations.schema.json +3 -3
- package/schemas/api/rest-envelope.schema.json +10 -10
- package/schemas/conformance-result.schema.json +120 -0
- package/schemas/execution-record.schema.json +2 -2
- package/schemas/extensions/analyzer.schema.json +9 -0
- package/schemas/extensions/base.schema.json +4 -4
- package/schemas/extensions/extractor.schema.json +4 -4
- package/schemas/extensions/formatter.schema.json +1 -1
- package/schemas/extensions/hook.schema.json +3 -3
- package/schemas/extensions/provider.schema.json +5 -5
- package/schemas/frontmatter/base.schema.json +1 -1
- package/schemas/history-stats.schema.json +4 -4
- package/schemas/input-types.schema.json +3 -3
- package/schemas/issue.schema.json +1 -1
- package/schemas/job.schema.json +2 -2
- package/schemas/node.schema.json +6 -5
- package/schemas/plugins-doctor.schema.json +97 -0
- package/schemas/plugins-registry.schema.json +2 -2
- package/schemas/project-config.schema.json +9 -9
- package/schemas/refresh-report.schema.json +52 -0
- package/schemas/report-base-deterministic.schema.json +1 -1
- package/schemas/sidecar.schema.json +3 -3
- package/schemas/summaries/markdown.schema.json +1 -1
- package/schemas/summaries/skill.schema.json +1 -1
- package/schemas/view-slots.schema.json +7 -7
- package/versioning.md +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,96 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.24.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2b09ce8: Restrict `node.kind` to `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$` in `spec/schemas/node.schema.json`.
|
|
8
|
+
|
|
9
|
+
Reason (audit `app-hacker`, finding H1): the UI uses the kind name as a fragment of CSS custom-property identifiers (`--sm-kind-<name>`) injected into a global `<style>` tag. The previous `minLength: 1` floor let a Provider declare a kind containing `;`, `{`, `}`, or whitespace, which would close the declaration context and inject arbitrary CSS rules (defacement, redress, and CSS-based exfiltration via `url()`). The new pattern is a security boundary at the kernel and matches every kind declared by the built-in Claude / Gemini Providers; external Providers that already use ASCII letter / digit / `_` / `-` names are unaffected.
|
|
10
|
+
|
|
11
|
+
Breaking (minor pre-1.0): any external Provider emitting a kind name with characters outside this pattern is now rejected by AJV validation. Affected plugins must rename the kind to a conforming identifier.
|
|
12
|
+
|
|
13
|
+
## 0.23.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- c1ed77a: Add `IAnalyzer.recommendedActions` so an Analyzer can declare which per-node Actions resolve its findings.
|
|
18
|
+
|
|
19
|
+
`spec/schemas/extensions/analyzer.schema.json` gains an optional `recommendedActions: string[]` (qualified action ids, `^[a-z0-9-]+/[a-z0-9-]+$`, unique). Distinct from the existing `IActionPrecondition` (Action-side filter: "I apply to nodes matching X"); `recommendedActions` is Analyzer-side ("when I fire, these per-node Actions are the canonical resolution"). The UI consumes both: the node inspector renders "Applicable Actions" from `IActionPrecondition` matching and "Recommended for issues" from `recommendedActions` of the Analyzer that fired each Issue.
|
|
20
|
+
|
|
21
|
+
Actions are per-node by design (matches the shape of `IActionPrecondition`). Project-level cleanup operations (e.g. `sm job prune --orphan-files`) stay as CLI verbs and are NOT surfaced through this field — therefore `core/contribution-orphan` and `core/job-orphan-file` analyzers do NOT declare `recommendedActions`. Built-in pairing shipping with this change: `core/annotation-stale.recommendedActions = ['core/bump']` — a stale sidecar is resolved by bumping the node (refreshes the `for` hashes and stamps the audit block).
|
|
22
|
+
|
|
23
|
+
Side-cleanup: the two earlier project-level action stubs `core/relink-contributions` and `core/prune-orphan-files` are removed; they were miscategorized as Actions. The per-node Action stub `core/mark-superseded` stays (declarer for `supersededBy`). The kernel `IAnalyzer` TS interface gains the matching optional `recommendedActions?: readonly string[]` field. Built-in extensions count returns to 26.
|
|
24
|
+
|
|
25
|
+
Validation: the analyzer pass walks every declared `recommendedActions` entry and emits an `extension.error` event with `kind: 'recommended-action-missing'` for any qualified id that is not registered as an Action. The analyzer stays registered and continues emitting issues; only the recommendation hint is dropped. The driving adapter (CLI, BFF) surfaces the event through the standard `extension.error` channel so plugin authors see a dangling reference instead of a silently empty "Recommended for issues" list. Spec wording lands in `architecture.md` (new "Analyzer · `recommendedActions` hint" subsection) and `plugin-author-guide.md` (analyzer section).
|
|
26
|
+
|
|
27
|
+
## User-facing
|
|
28
|
+
|
|
29
|
+
Node inspector will split actions into "Applicable" (always available) and "Recommended" (per finding). First pairing: stale sidecar recommends running `bump`. UI hookup lands in the next iteration; the spec field ships first.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 608e6ae: BFF compliance audit follow-ups (`bff-ruler` on `src/server/`).
|
|
34
|
+
|
|
35
|
+
**Error envelope unification.** Three call sites that hand-rolled their own 4xx/5xx JSON shape now throw `HTTPException` (or a typed subclass) and drain through the single global `app.onError` formatter so every BFF error response carries the canonical `{ ok: false, error: { code, message, details } }` envelope:
|
|
36
|
+
|
|
37
|
+
- `routes/scan.ts` (`db-missing` on `POST /api/scan`): now `throw new DbMissingError(...)`; `details: null`.
|
|
38
|
+
- `routes/plugins.ts` (`db-missing` on bulk + project list): same `DbMissingError` path.
|
|
39
|
+
- `routes/contributions.ts` (`missing-path` 400, `unknown-contribution` 404): `HTTPException` throws with externalized messages.
|
|
40
|
+
- `loopback-gate.ts` (`host-not-allowed` / `origin-not-allowed` 403): now `throw new LoopbackGateError({ code, message })`. `formatError` shapes it to the canonical envelope with `details: null`. The pre-baked terse message keeps the gate opaque to probes.
|
|
41
|
+
- `routes/plugins.ts` bulk PATCH: `details: { id: <offender> }` now lives on `BulkValidationError` and is stamped centrally in `formatError` instead of inlined at each call site.
|
|
42
|
+
|
|
43
|
+
`TErrorCode` gains `'host-not-allowed'` and `'origin-not-allowed'`. `cli-contract.md` §Server documents the new envelope shape and adds matching rows to the HTTP status mapping + error-code source list.
|
|
44
|
+
|
|
45
|
+
**Input validation tightened.** `GET /api/contributions/:pluginId/:extensionId/:contributionId` now validates the three URL segments against the qualified-id alphabet `/^[A-Za-z0-9._-]+$/` and the `?path=` query string via a new `parseRequiredString` helper in `util/parse-query.ts`. `GET /api/graph` rejects `?format=` values longer than 32 chars or outside `/^[a-z0-9-]+$/` before the formatter registry lookup.
|
|
46
|
+
|
|
47
|
+
**Internal type renames** (workspace-internal, not part of the public API surface):
|
|
48
|
+
|
|
49
|
+
- `IKindRegistry` → `TKindRegistry`, `IContributionsRegistry` → `TContributionsRegistry` (they are `Record<>` aliases, not interfaces).
|
|
50
|
+
- `IContributionsRegistryEntry` declared twice with drift on `priority?`. One canonical declaration in `envelope.ts` with the field; `contributions-registry.ts` re-exports it.
|
|
51
|
+
- `ServerHandle` → `IServerHandle` (consistency with the rest of the `I*` interface convention).
|
|
52
|
+
|
|
53
|
+
**Misc.** `src/tsconfig.json` now lists `server/**/*` and `core/**/*` in `include` explicitly (they were previously type-checked only via transitive resolution from `cli/`). The seven em dashes in user-facing strings in `i18n/server.texts.ts` were replaced with commas / parentheses. The two `scan-guard-trip` literals in `routes/scan.ts` are now externalized to `SERVER_TEXTS`.
|
|
54
|
+
|
|
55
|
+
- c2152cc: Add `--json` output to four verbs that previously emitted only human-formatted text: `sm refresh` (and `sm refresh --stale`), `sm plugins doctor`, `sm conformance run`, plus `--format json` on `sm graph` (`sm graph` uses the formatter catalog rather than the global `--json` flag). Closes the spec drift where the global `--json` flag was advertised but ignored on these verbs, and unblocks CI / scripting consumers that parse the output.
|
|
56
|
+
|
|
57
|
+
New JSON schemas under `spec/schemas/`:
|
|
58
|
+
|
|
59
|
+
- `refresh-report.schema.json`, `{ ok: true, kind: 'refresh.report', refreshed, nodes[], elapsedMs }`. Error envelope codes: `not-found` (missing node), `db-missing` (absent project DB), `internal` (read / persist failure).
|
|
60
|
+
- `plugins-doctor.schema.json`, `{ ok: true, kind: 'plugins.doctor', counts, issues[], warnings[], elapsedMs }`. `counts` collapses the raw discovery enum into the four error buckets (`loaded` / `incompatible` / `invalid` / `loadError`) so consumers do not have to track the kernel-side label catalog.
|
|
61
|
+
- `conformance-result.schema.json`, `{ ok: true, kind: 'conformance.result', totals, scopes[], elapsedMs }`. Error envelope codes: `bad-query` (unknown scope), `internal` (missing binary). A run that surfaces failing cases still returns `ok: true`; failures live under `scopes[].cases[].status === 'fail'` and gate the exit code.
|
|
62
|
+
|
|
63
|
+
`sm graph` gains a built-in `json` formatter (`built-in-plugins/formatters/json/`) that stringifies the persisted `ScanResult` (`scan-result.schema.json`), byte-equivalent to `sm scan --json` modulo whitespace. The formatter is registered alongside `ascii` in `built-in-plugins/built-ins.ts`, picked up automatically by the BFF's `GET /api/graph?format=json` (which previously documented JSON but had no formatter to back it). `IFormatterContext` gains an optional `scanResult` field so formatters whose output mirrors a full `ScanResult` envelope read it verbatim; existing formatters (today: `ascii`) ignore it.
|
|
64
|
+
|
|
65
|
+
Built-in extension count: 26 → 27 (the new `core/json` formatter). Spec `coverage.md` matrix grows three rows (`refresh-report`, `plugins-doctor`, `conformance-result`).
|
|
66
|
+
|
|
67
|
+
## User-facing
|
|
68
|
+
|
|
69
|
+
`sm refresh`, `sm plugins doctor`, and `sm conformance run` now respect `--json` for machine-readable output. `sm graph --format json` is a new format that emits the full ScanResult. CI / scripts can parse these instead of the human text.
|
|
70
|
+
|
|
71
|
+
- 5f4de1c: Security audit sweep (cli-hacker follow-up). Three highs, three mediums, three lows, plus the shared prototype-pollution helper and a plugin-author doc note.
|
|
72
|
+
|
|
73
|
+
- **H1** — BFF rejects non-loopback `Host` and `Origin` headers on every request (port-agnostic hostname allow-list). Closes the DNS-rebinding lane where a malicious page in the operator's browser could weaponise the local API by resolving an attacker-controlled hostname to 127.0.0.1.
|
|
74
|
+
- **H2 / L2** — Sidecar `deepMerge` + `readSidecarFor` parse strip `__proto__` / `constructor` / `prototype` keys at every depth. Shared helper in `kernel/util/strip-prototype-pollution.ts` (also adopted by `kernel/config/loader.ts`).
|
|
75
|
+
- **H3** — Bumped `hono` to 4.12.18 and `kysely` to 0.28.17. Added a root `overrides.fast-uri: 3.1.2` to lift the transitive past the path-traversal advisories. Lockfile regenerated.
|
|
76
|
+
- **M1** — Settings + sidecar atomic writes now land mode 0o600 (matches `db restore`'s discipline).
|
|
77
|
+
- **M2** — `sm job prune` rejects `unlink()` on paths that don't stay inside `<scope>/.skill-map/jobs/`.
|
|
78
|
+
- **M3** — Orphan-files walker skips symlinks (parity with the scan + reference walkers).
|
|
79
|
+
- **L1** — Sidecar temp filename embeds `pid` + timestamp (cross-process race window).
|
|
80
|
+
- **L3** — `fetchLatestVersion` rejects registry responses whose `version` is not a semver-shaped string.
|
|
81
|
+
- **L5** — Two BFF error envelopes on `/api/contributions/*` sanitize URL params before interpolation.
|
|
82
|
+
- **L4** — Plugin author guide spells out that module top-level side effects survive an `import()` timeout, so plugins must do their work inside lifecycle methods.
|
|
83
|
+
|
|
84
|
+
## User-facing
|
|
85
|
+
|
|
86
|
+
`sm serve` now rejects browser requests whose `Host` or `Origin` is not a loopback name. Closes a DNS-rebinding lane where a malicious page could trigger scans or settings writes. `--dev-cors` still works for Vite-style dev UIs on a different loopback port.
|
|
87
|
+
|
|
88
|
+
- 639a95b: Strip em dashes (`—`) from spec prose and schema descriptions.
|
|
89
|
+
|
|
90
|
+
Stylistic sweep across `spec/*.md` (architecture, cli-contract, db-schema, job-events, job-lifecycle, plugin-author-guide, plugin-kv-api, prompt-preamble, versioning, view-slots, input-types, interfaces/security-scanner, conformance/README.md, conformance/coverage.md, README.md) and `spec/schemas/**/*.json` description fields. Each em dash is replaced with a comma, colon, semicolon, or parenthetical pair chosen to read naturally in context.
|
|
91
|
+
|
|
92
|
+
`spec/index.json` regenerated so the integrity hashes line up with the new content. No normative changes: schema keys, enum values, type definitions, required-field sets are all unchanged. Conformance fixtures and `CHANGELOG.md` historical snapshots are deliberately untouched.
|
|
93
|
+
|
|
3
94
|
## 0.22.0
|
|
4
95
|
|
|
5
96
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# skill-map spec
|
|
2
2
|
|
|
3
|
-
The **skill-map specification** defines a vendor-neutral standard for mapping, inspecting, and managing collections of interrelated Markdown files
|
|
3
|
+
The **skill-map specification** defines a vendor-neutral standard for mapping, inspecting, and managing collections of interrelated Markdown files, skills, agents, commands, hooks, and notes that compose AI-agent ecosystems (Claude Code, Codex, Gemini, docs sites, and any future platform).
|
|
4
4
|
|
|
5
5
|
This document is the **source of truth**. The reference implementation under `../src/` conforms to this spec. Third parties can build alternative implementations (any language, any UI, any CLI) using only `spec/`, without reading the reference source.
|
|
6
6
|
|
|
@@ -38,8 +38,8 @@ These are implementation decisions. The reference impl picks them (see [`../AGEN
|
|
|
38
38
|
|
|
39
39
|
Two analyzers govern every identifier in the spec. They are **normative**.
|
|
40
40
|
|
|
41
|
-
- **Filesystem artefacts use kebab-case.** Every file and directory in `spec/` (and in any conforming implementation)
|
|
42
|
-
- **JSON content uses camelCase.** Every key inside a JSON Schema, frontmatter block, config file, plugin manifest, action manifest, job record, report, event payload, or API response is camelCase: `whatItDoes`, `injectionDetected`, `expectedTools`, `conflictsWith`, `docsUrl`, `examplesUrl`, `ttlSeconds`, `runId`, `jobId`. This matches the JS/TS ecosystem the reference impl ships in and the Kysely `CamelCasePlugin` that bridges to the `snake_case` SQL layer
|
|
41
|
+
- **Filesystem artefacts use kebab-case.** Every file and directory in `spec/` (and in any conforming implementation), `scan-result.schema.json`, `job-lifecycle.md`, `report-base.schema.json`, `auto-rename-medium` (as an `issue.analyzerId` value), `direct-override` (as a `safety.injectionType` enum value), and so on, is kebab-case lowercase. Enum values and issue analyzer ids follow the same convention so they can be echoed back into URLs, filenames, and log keys without escaping.
|
|
42
|
+
- **JSON content uses camelCase.** Every key inside a JSON Schema, frontmatter block, config file, plugin manifest, action manifest, job record, report, event payload, or API response is camelCase: `whatItDoes`, `injectionDetected`, `expectedTools`, `conflictsWith`, `docsUrl`, `examplesUrl`, `ttlSeconds`, `runId`, `jobId`. This matches the JS/TS ecosystem the reference impl ships in and the Kysely `CamelCasePlugin` that bridges to the `snake_case` SQL layer, but the analyzer is spec-level, not implementation-level: an alternative implementation in any language still exposes camelCase JSON keys.
|
|
43
43
|
|
|
44
44
|
The SQL persistence layer is the sole exception: tables, columns, and migration filenames use `snake_case` (see `db-schema.md`). That boundary is crossed only inside a storage adapter; nothing that leaves the kernel should ever be `snake_case`.
|
|
45
45
|
|
|
@@ -131,7 +131,7 @@ Published to npm as [`@skill-map/spec`](https://www.npmjs.com/package/@skill-map
|
|
|
131
131
|
npm i @skill-map/spec
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
### Use
|
|
134
|
+
### Use, load a schema
|
|
135
135
|
|
|
136
136
|
```js
|
|
137
137
|
import specIndex from '@skill-map/spec';
|