@skill-map/spec 0.16.0 → 0.18.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.
@@ -2,9 +2,9 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://skill-map.dev/spec/v0/frontmatter/base.schema.json",
4
4
  "title": "FrontmatterBase",
5
- "description": "Base shape of the YAML frontmatter block on every node, across all kinds. Universal — common to every Provider's nodes regardless of platform. Per-kind schemas live in the Provider that emits the kind (declared via `provider.kinds[<kind>].schema`) and extend this base via `allOf` + `$ref` to its `$id`. camelCase keys throughout. `additionalProperties` is intentionally permissive; the deterministic `unknown-field` rule emits warnings for keys outside the catalog so that the JSON Schema remains shape-only and policy lives in rules.",
5
+ "description": "Truly universal frontmatter shape `name` + `description` are the only fields every node, on every Provider, MUST carry. Per-vendor schemas (Anthropic Claude, Cursor, Obsidian, …) live in the Provider that emits the kind (declared via `provider.kinds[<kind>].schema`) and extend this base via `allOf` + `$ref` to its `$id`. `additionalProperties: true` is intentional: skill-map AGGREGATES vendor specs, it does not curate them. Vendor-specific fields (`tools`, `allowedTools`, `model`, `metadata`, etc.) flow through validation silently because the per-kind extension declares them. The future home for skill-map-only annotation fields (provenance, cross-vendor metadata) is a deferred decision — tracked separately, not in this schema.",
6
6
  "type": "object",
7
- "required": ["name", "description", "metadata"],
7
+ "required": ["name", "description"],
8
8
  "additionalProperties": true,
9
9
  "properties": {
10
10
  "name": {
@@ -16,131 +16,6 @@
16
16
  "type": "string",
17
17
  "minLength": 1,
18
18
  "description": "One-to-three-sentence description. REQUIRED."
19
- },
20
- "type": {
21
- "type": "string",
22
- "description": "User-declared kind hint. Optional; the Provider may use it to tie-break classification. Free-form string so that new kinds introduced by Providers don't require a spec bump."
23
- },
24
- "author": {
25
- "type": "string",
26
- "description": "Primary author. Denormalized into `node.author` for query speed."
27
- },
28
- "authors": {
29
- "type": "array",
30
- "description": "Additional authors. MAY include the primary author; consumers dedupe on display.",
31
- "items": { "type": "string" }
32
- },
33
- "license": {
34
- "type": "string",
35
- "description": "SPDX license identifier (e.g. `MIT`, `Apache-2.0`)."
36
- },
37
- "tools": {
38
- "type": "array",
39
- "description": "Tools this node requires when executed by a host. Allowlist semantics: if present, the host MUST restrict the node to exactly these tools (matching Claude Code subagent `tools` frontmatter). Free-form strings — token vocabulary is platform-specific (e.g. `Read`, `Grep`, `Bash`, `Bash(git add *)`). Kind-specific interpretation: an `agent` node's allowlist constrains the spawned subagent; a `skill` node's allowlist is a hint for the host since skills typically inherit their parent's tools. Omit to inherit the host's default. Denormalized into `node.tools_json` for query.",
40
- "items": { "type": "string" }
41
- },
42
- "allowedTools": {
43
- "type": "array",
44
- "description": "Tools the host MAY use without triggering a per-use permission prompt while this node is active (matching Claude Code skill `allowed-tools` frontmatter). Pre-approval semantics, distinct from `tools` (allowlist): every tool not listed remains callable, but governed by the host's normal permission settings. Accepts argument-scoped patterns where the host supports them (`Bash(git add *)`). Free-form strings. Omit to defer to host defaults.",
45
- "items": { "type": "string" }
46
- },
47
- "metadata": {
48
- "type": "object",
49
- "description": "Structured metadata. REQUIRED. `version` inside is REQUIRED; all other fields are optional.",
50
- "required": ["version"],
51
- "additionalProperties": true,
52
- "properties": {
53
- "github": { "type": "string", "description": "GitHub handle or profile URL." },
54
- "homepage": { "type": "string", "format": "uri" },
55
- "linkedin": { "type": "string" },
56
- "twitter": { "type": "string" },
57
-
58
- "version": {
59
- "type": "string",
60
- "description": "Semver of this node's content. REQUIRED. Denormalized into `node.version`."
61
- },
62
- "specCompat": {
63
- "type": "string",
64
- "description": "Semver range of spec versions this node targets."
65
- },
66
- "stability": {
67
- "type": "string",
68
- "enum": ["experimental", "stable", "deprecated"],
69
- "description": "Maturity tag. Denormalized into `node.stability`."
70
- },
71
- "supersedes": {
72
- "type": "array",
73
- "description": "`node.path` values this node replaces. Feeds the `supersedes` link kind.",
74
- "items": { "type": "string" }
75
- },
76
- "supersededBy": {
77
- "type": "string",
78
- "description": "`node.path` that replaces this one. Inverse of `supersedes`."
79
- },
80
-
81
- "source": {
82
- "type": "string",
83
- "format": "uri",
84
- "description": "Canonical origin URL (e.g. GitHub blob). Consumed by enrichment plugins for hash verification."
85
- },
86
- "sourceVersion": {
87
- "type": "string",
88
- "description": "Tag, SHA, or branch at the source. Branches are resolved dynamically by enrichment."
89
- },
90
-
91
- "tags": {
92
- "type": "array",
93
- "items": { "type": "string" }
94
- },
95
- "category": { "type": "string" },
96
- "keywords": {
97
- "type": "array",
98
- "items": { "type": "string" }
99
- },
100
-
101
- "created": { "$ref": "#/$defs/IsoDate", "description": "ISO 8601 date (`YYYY-MM-DD`) or date-time." },
102
- "updated": { "$ref": "#/$defs/IsoDate", "description": "ISO 8601 date (`YYYY-MM-DD`) or date-time." },
103
- "released": { "$ref": "#/$defs/IsoDate", "description": "ISO 8601 date (`YYYY-MM-DD`) or date-time." },
104
-
105
- "requires": {
106
- "type": "array",
107
- "description": "`node.path` or plugin id values this node depends on.",
108
- "items": { "type": "string" }
109
- },
110
- "conflictsWith": {
111
- "type": "array",
112
- "description": "Nodes or plugin ids that MUST NOT be co-enabled with this one.",
113
- "items": { "type": "string" }
114
- },
115
- "provides": {
116
- "type": "array",
117
- "description": "Capability tokens this node advertises (free-form strings).",
118
- "items": { "type": "string" }
119
- },
120
- "related": {
121
- "type": "array",
122
- "description": "`node.path` values for see-also navigation. No strict semantics.",
123
- "items": { "type": "string" }
124
- },
125
-
126
- "icon": { "type": "string", "description": "Free-form icon hint (emoji, lucide name, URL)." },
127
- "color": { "type": "string", "description": "CSS color or predefined palette token." },
128
- "priority": { "type": "integer", "description": "Higher = surface earlier in UI listings." },
129
- "hidden": { "type": "boolean", "description": "UI hint: hide from default lists." },
130
-
131
- "docsUrl": { "type": "string", "format": "uri" },
132
- "readme": { "type": "string", "description": "Relative or absolute path to an extended README for this node." },
133
- "examplesUrl": { "type": "string", "format": "uri" }
134
- }
135
- }
136
- },
137
- "$defs": {
138
- "IsoDate": {
139
- "type": "string",
140
- "anyOf": [
141
- { "format": "date" },
142
- { "format": "date-time" }
143
- ]
144
19
  }
145
20
  }
146
21
  }
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://skill-map.dev/spec/v0/node.schema.json",
4
4
  "title": "Node",
5
- "description": "A single markdown file in the graph. Identified by its relative path from the scope root. The `kind` is whatever the classifying Provider declares — open by design; the **built-in Claude Provider** emits `skill` / `agent` / `command` / `hook` / `note` today, but external Providers (Cursor, Obsidian, …) MAY emit their own.",
5
+ "description": "A single markdown file in the graph. Identified by its relative path from the scope root. 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",
7
7
  "required": ["path", "kind", "provider", "bodyHash", "frontmatterHash", "bytes", "linksOutCount", "linksInCount", "externalRefsCount"],
8
8
  "additionalProperties": false,
@@ -14,7 +14,7 @@
14
14
  "kind": {
15
15
  "type": "string",
16
16
  "minLength": 1,
17
- "description": "Category assigned by the Provider. Open-by-design — any non-empty string an enabled Provider declares is valid (built-in Claude Provider catalog: `skill` / `agent` / `command` / `hook` / `note`; external Providers MAY declare their own). Per-kind frontmatter schemas live with the Provider that emits the kind. Stability: stable."
17
+ "description": "Category assigned by the Provider. Open-by-design — any non-empty string an enabled Provider declares is valid (built-in Claude Provider catalog: `skill` / `agent` / `command` / `markdown`; external Providers MAY declare their own). Per-kind frontmatter schemas live with the Provider that emits the kind. Stability: stable."
18
18
  },
19
19
  "provider": {
20
20
  "type": "string",
@@ -34,12 +34,9 @@
34
34
  "description": "Denormalized from `metadata.stability` for fast queries."
35
35
  },
36
36
  "version": {
37
- "type": ["string", "null"],
38
- "description": "Denormalized from `metadata.version` for fast queries. Must be a valid semver if present."
39
- },
40
- "author": {
41
- "type": ["string", "null"],
42
- "description": "Denormalized from `metadata.author` for fast queries."
37
+ "type": ["integer", "null"],
38
+ "minimum": 1,
39
+ "description": "Monotonic version counter denormalised from sidecar `annotations.version` (Step 9.6.2). Pre-9.6.2 this field was a semver string sourced from `frontmatter.metadata.version`; the new shape is a single integer monotonic counter, orthogonal to `stability`. Major bumps mean `create a new node, supersede the old one`, not increment. See Decision #125 and `annotations.schema.json#/properties/version`."
43
40
  },
44
41
  "frontmatter": {
45
42
  "type": "object",
@@ -78,6 +75,14 @@
78
75
  "type": "integer",
79
76
  "minimum": 0,
80
77
  "description": "http/https URLs in the body after normalization and exact-match dedup."
78
+ },
79
+ "sidecar": {
80
+ "$ref": "#/$defs/sidecarOverlay",
81
+ "description": "Step 9.6.2 — co-located `.sm` sidecar overlay. Carries presence flag, drift status (null when no sidecar), and the parsed `annotations:` block (null when absent or empty). The kernel re-derives `status` on every scan from the live hashes; clients should treat it as authoritative for the snapshot but never persist it across scans."
82
+ },
83
+ "isFavorite": {
84
+ "type": "boolean",
85
+ "description": "Per-node favorite flag set by the local user from the UI. Sourced from `state_node_favorites` (zone `state_`, persistent across scans). Decorated by the BFF on every `/api/nodes` response via in-memory Set lookup against the favorites table — no SQL JOIN against `scan_nodes`. Absent on emissions that don't carry per-user state (e.g. `sm export --json`); consumers that don't recognise the field MUST ignore it."
81
86
  }
82
87
  },
83
88
  "$defs": {
@@ -90,6 +95,32 @@
90
95
  "body": { "type": "integer", "minimum": 0 },
91
96
  "total": { "type": "integer", "minimum": 0 }
92
97
  }
98
+ },
99
+ "sidecarOverlay": {
100
+ "type": "object",
101
+ "required": ["present"],
102
+ "additionalProperties": false,
103
+ "properties": {
104
+ "present": {
105
+ "type": "boolean",
106
+ "description": "True when a `<basename>.sm` file accompanies the node on disk; false otherwise."
107
+ },
108
+ "status": {
109
+ "type": ["string", "null"],
110
+ "enum": ["fresh", "stale-body", "stale-frontmatter", "stale-both", null],
111
+ "description": "Drift status. NULL when no sidecar is present, or when the sidecar exists but failed to parse / validate (the row still records `present: true` so clients can distinguish absent from broken)."
112
+ },
113
+ "annotations": {
114
+ "type": ["object", "null"],
115
+ "additionalProperties": true,
116
+ "description": "Parsed `annotations:` block from the sidecar. NULL when no sidecar is present, when the block is absent / empty, or when the sidecar failed to parse / validate."
117
+ },
118
+ "root": {
119
+ "type": ["object", "null"],
120
+ "additionalProperties": true,
121
+ "description": "Parsed YAML root of the matching `.sm` sidecar. Mirrors the shape of `sidecar.schema.json` (top-level reserved blocks `for` / `annotations` / `settings` / `audit` plus opt-in `<plugin-id>:` namespaces). Surfaced for the UI inspector's audit panel, plugin-contributions panel, and debug panel; NULL when the sidecar is absent or failed to parse. Note the duplication with `annotations` at this overlay level — existing consumers read from `annotations`, new consumers read structured sub-fields off `root` (`root.for.*`, `root.audit.*`, etc.). The duplication is intentional and documented; do NOT remove the top-level `annotations` field."
122
+ }
123
+ }
93
124
  }
94
125
  }
95
126
  }
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://skill-map.dev/spec/v0/report-base-deterministic.schema.json",
4
+ "title": "ReportBaseDeterministic",
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
+ "type": "object",
7
+ "required": ["ok"],
8
+ "additionalProperties": true,
9
+ "properties": {
10
+ "ok": {
11
+ "type": "boolean",
12
+ "description": "True when the Action completed its logical work; false when it refused / errored. Silent no-ops (e.g. `force: true` against an already-fresh node) still report `ok: true` with an action-specific marker (e.g. bump's `noop: true`)."
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,96 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://skill-map.dev/spec/v0/sidecar.schema.json",
4
+ "title": "Sidecar",
5
+ "description": "Root shape of a co-located YAML sidecar (`<basename>.sm` next to `<basename>.md`). Carries skill-map's annotations, settings, audit, and plugin contributions about the markdown node it accompanies. Vendor file (`<basename>.md`) stays untouched. Top-level reserved blocks are `for`, `annotations`, `settings`, `audit`; everything else is treated as a plugin namespace (`<plugin-id>:`). Schema is `additionalProperties: true` so plugins can add namespaces without coordination; the built-in `unknown-field` rule 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
+ "type": "object",
7
+ "required": ["for"],
8
+ "additionalProperties": true,
9
+ "properties": {
10
+ "for": {
11
+ "$ref": "#/$defs/identity",
12
+ "description": "Identity link to the markdown node this sidecar annotates. Drift detection compares stored hashes against the current file at scan time; mismatch emits the built-in `annotation-stale` warning (soft mode, never blocking)."
13
+ },
14
+ "annotations": {
15
+ "$ref": "annotations.schema.json",
16
+ "description": "Skill-map annotation catalog. See `annotations.schema.json` for the curated 15-field surface; users / plugins MAY add custom keys (rides on `additionalProperties: true`)."
17
+ },
18
+ "settings": {
19
+ "type": "object",
20
+ "additionalProperties": true,
21
+ "description": "Reserved for skill-map's per-node settings. v0.18.0 ships the block empty; consumers ride on `additionalProperties: true`. Concrete fields land in later sub-steps as the runtime grows."
22
+ },
23
+ "audit": {
24
+ "$ref": "#/$defs/audit",
25
+ "description": "Skill-map's audit trail. Populated by the built-in `bump` Action starting in Step 9.6.3 (Decision #125). All fields are optional at the property level — the Action atomically fills `lastBumpedAt` + `lastBumpedBy` on every bump and `createdAt` + `createdBy` on first creation. `additionalProperties: true` so future fields land additively."
26
+ }
27
+ },
28
+ "$defs": {
29
+ "audit": {
30
+ "type": "object",
31
+ "additionalProperties": true,
32
+ "properties": {
33
+ "lastBumpedAt": {
34
+ "type": "string",
35
+ "format": "date-time",
36
+ "description": "ISO 8601 datetime of the last `bump` Action invocation that materialised a write to this sidecar. Atomically set together with `lastBumpedBy` on every bump."
37
+ },
38
+ "lastBumpedBy": {
39
+ "type": "string",
40
+ "minLength": 1,
41
+ "description": "Identity of the bump invoker. Literal `'cli'` for `sm bump` (we are not threading user identity yet); `'plugin:<plugin-id>'` when a plugin's deterministic Action triggered the bump. Tests / future invokers MAY pass any non-empty string."
42
+ },
43
+ "createdAt": {
44
+ "type": "string",
45
+ "format": "date-time",
46
+ "description": "ISO 8601 datetime of the first `bump` invocation that ever wrote this sidecar. Set exactly once (when the `.sm` file did not previously exist on disk); stable across subsequent bumps."
47
+ },
48
+ "createdBy": {
49
+ "type": "string",
50
+ "minLength": 1,
51
+ "description": "Identity of the invoker that created the sidecar (same value form as `lastBumpedBy`). Set exactly once at creation time and stable thereafter."
52
+ }
53
+ }
54
+ },
55
+ "identity": {
56
+ "type": "object",
57
+ "required": ["path", "bodyHash", "frontmatterHash"],
58
+ "additionalProperties": true,
59
+ "properties": {
60
+ "path": {
61
+ "type": "string",
62
+ "minLength": 1,
63
+ "description": "Relative path from the scope root to the `.md` file this sidecar annotates. Matches the canonical Node identifier (see `node.schema.json` #/properties/path). Survives content edits; breaks on file moves — `sm refresh` re-points the sidecar after a move."
64
+ },
65
+ "bodyHash": {
66
+ "type": "string",
67
+ "pattern": "^[a-f0-9]{64}$",
68
+ "description": "sha256 of the body content (post-frontmatter), hex-encoded lowercase, captured at the moment this sidecar was last bumped. Compared against the current body hash to detect drift."
69
+ },
70
+ "frontmatterHash": {
71
+ "type": "string",
72
+ "pattern": "^[a-f0-9]{64}$",
73
+ "description": "sha256 of the canonical frontmatter (per the kernel's `canonicalFrontmatter` rule), hex-encoded lowercase, captured at the moment this sidecar was last bumped. Compared against the current frontmatter hash to detect drift."
74
+ },
75
+ "resolvedAs": {
76
+ "type": "object",
77
+ "required": ["provider", "kind"],
78
+ "additionalProperties": false,
79
+ "properties": {
80
+ "provider": {
81
+ "type": "string",
82
+ "minLength": 1,
83
+ "description": "Provider id that classifies this node (e.g. `claude`)."
84
+ },
85
+ "kind": {
86
+ "type": "string",
87
+ "minLength": 1,
88
+ "description": "Kind within the Provider (e.g. `agent`, `skill`)."
89
+ }
90
+ },
91
+ "description": "Optional override of the Provider/kind classification. Set when path-based + content-based matching are ambiguous (a single `.md` matched by multiple Providers). When present, locks the classification to (`provider`, `kind`); when absent, the kernel uses its normal classification pipeline."
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://skill-map.dev/spec/v0/summaries/note.schema.json",
4
- "title": "SummaryNote",
5
- "description": "Report produced by `note-summarizer` for a single `note` node. Extends `report-base.schema.json`. Stability: experimental.",
3
+ "$id": "https://skill-map.dev/spec/v0/summaries/markdown.schema.json",
4
+ "title": "SummaryMarkdown",
5
+ "description": "Report produced by `markdown-summarizer` for a single `markdown` node (the format-named generic fallback in the Claude Provider catalog). Extends `report-base.schema.json`. Stability: experimental.",
6
6
  "allOf": [
7
7
  { "$ref": "../report-base.schema.json" }
8
8
  ],
@@ -14,7 +14,7 @@
14
14
  "safety": true,
15
15
  "whatItCovers": {
16
16
  "type": "string",
17
- "description": "One-sentence summary of the note's subject matter. REQUIRED. Named distinctly from `whatItDoes` since notes describe, not act."
17
+ "description": "One-sentence summary of the file's subject matter. REQUIRED. Named distinctly from `whatItDoes` since markdown notes describe, not act."
18
18
  },
19
19
  "topics": {
20
20
  "type": "array",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "keyFacts": {
25
25
  "type": "array",
26
- "description": "Discrete claims or data points the note asserts. Useful for search and diffing over time.",
26
+ "description": "Discrete claims or data points the file asserts. Useful for search and diffing over time.",
27
27
  "items": { "type": "string" }
28
28
  },
29
29
  "relatedNodes": {