@skill-map/spec 0.58.0 → 0.60.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 +13 -13
- package/cli-contract.md +3 -3
- package/conformance/cases/plugin-missing-ui-rejected.json +1 -1
- package/conformance/coverage.md +1 -1
- package/conformance/fixtures/orphan-markdown/ARCHITECTURE.md +1 -1
- package/conformance/fixtures/plugin-missing-ui/.skill-map/settings.json +1 -1
- package/db-schema.md +1 -1
- package/index.json +15 -15
- package/package.json +1 -1
- package/plugin-author-guide.md +2 -2
- package/schemas/api/rest-envelope.schema.json +12 -3
- package/schemas/extensions/provider.schema.json +27 -8
- package/schemas/frontmatter/base.schema.json +3 -4
- package/schemas/project-config.schema.json +1 -1
- package/telemetry.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.60.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- The lens selector now offers a single open lens, `agent-skills` ("Agent Skills"), promoted to stable and locked and made the universal default for projects with no vendor marker (replacing the old `markdown` default). The non-gated `core/markdown` becomes the invisible base: it still classifies every orphan `.md` but is no longer a selectable lens. A new `isLens` flag drives the dropdown, and `PATCH /api/active-provider` rejects non-lens ids.
|
|
8
|
+
|
|
9
|
+
## User-facing
|
|
10
|
+
|
|
11
|
+
The provider lens picker is simpler: one open "Agent Skills" lens (the default when no vendor like Claude or Codex is detected) replaces the old separate "Markdown" and "Open Skills" entries. Plain `.md` files are still mapped, same as before.
|
|
12
|
+
|
|
13
|
+
- The Codex lens now classifies open-standard Agent Skills (`.agents/skills/<name>/SKILL.md`, the layout OpenAI Codex actually reads) as `codex`/`skill`, by composing the `agent-skills` open-standard pieces over a new multi-rule `read`. A provider's `read` may now be an array of rules so one provider reads several file families with different parsers (Codex reads `.toml` agents and `.md` skills), and a `/skill-name` invocation in an agent prompt resolves to its skill.
|
|
14
|
+
|
|
15
|
+
## User-facing
|
|
16
|
+
|
|
17
|
+
OpenAI Codex projects now show their Agent Skills (`.agents/skills/<name>/SKILL.md`) on the map as skill nodes next to the Codex agents, and a slash invocation from an agent to a skill is drawn as a link.
|
|
18
|
+
|
|
19
|
+
- The provider / active-lens labels now follow one consistent naming pattern: vendor lenses use a possessive `<Vendor>'s <product>` form ("Anthropic's Claude", "OpenAI's Codex", "Google's Antigravity") and the vendor-neutral open standard uses a `Standard: <name>` prefix ("Standard: Agent skills"). The non-selectable `core/markdown` base keeps its internal "Markdown" label. The provider schema and kernel JSDoc document the pattern.
|
|
20
|
+
|
|
21
|
+
## User-facing
|
|
22
|
+
|
|
23
|
+
The provider lens names now read consistently: "Anthropic's Claude", "OpenAI's Codex", "Google's Antigravity", and "Standard: Agent skills". The change shows up in the lens dropdown, the topbar lens chip, and the per-node provider chips.
|
|
24
|
+
|
|
25
|
+
- The inspector now renders OpenAI Codex agents (`.codex/agents/*.toml`) like a Markdown node: the TOML `developer_instructions` field becomes the Body section (rendered as Markdown) and the other TOML keys the Definition/metadata card, instead of showing the raw TOML file. A new optional `bodyField` on each `providerRegistry` entry (projected from the provider's `read.bodyField`) drives the split, so it stays provider-driven with no hardcoded provider id.
|
|
26
|
+
|
|
27
|
+
## User-facing
|
|
28
|
+
|
|
29
|
+
Codex agents (`.codex/agents/*.toml`) now open in the inspector with a proper metadata section and a readable, Markdown-rendered body, instead of a wall of raw TOML.
|
|
30
|
+
|
|
31
|
+
- The OpenAI Codex provider is now beta (enabled by default): a `.codex/` directory auto-detects the codex lens and `.codex/agents/*.toml` files classify as agents. A Codex agent's prompt (the TOML `developer_instructions` field) flows through the link extractors via the new declarative `read.bodyField` knob, so `@mention` and `[link]` references inside it surface in the graph. `AGENTS.md` is no longer a detection marker (it is the vendor-neutral agents.md standard, common in non-Codex repos).
|
|
32
|
+
|
|
33
|
+
## User-facing
|
|
34
|
+
|
|
35
|
+
OpenAI Codex is now a built-in provider. Open a project with a `.codex/` folder and skill-map maps your Codex sub-agents plus the links inside their developer instructions, the same way it does for Claude. Pick it anytime from the provider lens.
|
|
36
|
+
|
|
37
|
+
- Make `name`/`description` per-kind requirements instead of universal ones: the frontmatter base only defines the two fields, and `required` moves to the kinds whose vendor mandates them (Claude agent, Codex agent, Agent Skills skill), leaving the `markdown` fallback and Claude skill/command optional. Per-kind schemas are re-certified against current vendor docs, and the redundant base check in `core/schema-violation` is dropped so each per-kind schema is the single source of truth.
|
|
38
|
+
|
|
39
|
+
## User-facing
|
|
40
|
+
|
|
41
|
+
**Frontmatter checks now follow each vendor's rules.** Plain Markdown files and Claude skills/commands without a `name` or `description` are no longer flagged, and Codex/Claude model fields accept current values like `xhigh` reasoning effort and the `fable` model alias.
|
|
42
|
+
|
|
43
|
+
- The OpenAI Codex provider and plugin id was renamed from `openai` to `codex`, aligning the id with its `.codex/` marker and the product-name scheme of the other built-ins. The lens value (`activeProvider`), `node.provider`, the conformance scope (`provider:codex`), and qualified extension ids (`codex/codex`) change accordingly. Breaking but greenfield (no released consumers); the displayed lens label "OpenAI's Codex" is unchanged.
|
|
44
|
+
|
|
45
|
+
## User-facing
|
|
46
|
+
|
|
47
|
+
The OpenAI Codex provider id is now `codex` (was `openai`). If you set it by hand, use `codex` in `sm config set activeProvider` or `sm plugins enable`. The name shown in the app is unchanged.
|
|
48
|
+
|
|
49
|
+
## 0.59.0
|
|
50
|
+
|
|
51
|
+
### Minor Changes
|
|
52
|
+
|
|
53
|
+
- The vendor-neutral open-skills Provider (`agent-skills`, lens "Open Skills") gains an open-standard base reserved-name catalog under `skill`: a user skill shadowing a universal built-in like `help`/`config` is now flagged by `core/name-reserved`, and Antigravity inherits the base by manifest composition and appends its own verbs. Its `skill` frontmatter schema now enforces the open-standard `name` pattern/length and `description` length. Shared primitives renamed to a `COMMONS_*` vocabulary.
|
|
54
|
+
|
|
55
|
+
## User-facing
|
|
56
|
+
|
|
57
|
+
With the Open Skills lens active, a skill you authored that shares a name with a built-in command (like `help` or `config`) now gets a warning, and skill names or descriptions that break the open-standard format (bad characters, too long) are flagged too.
|
|
58
|
+
|
|
3
59
|
## 0.58.0
|
|
4
60
|
|
|
5
61
|
### Minor Changes
|
package/architecture.md
CHANGED
|
@@ -70,9 +70,9 @@ The UI is **not** a driving adapter; it is an HTTP/WS client of the Server. Exac
|
|
|
70
70
|
|
|
71
71
|
A skill-map project sees its filesystem through exactly one **active provider lens** at any time: the provider whose extractors, classifiers, and resolution rules apply to the whole project during a scan. All other enabled providers stay registered but their provider-specific extractors are skipped.
|
|
72
72
|
|
|
73
|
-
The lens is project-scope state, living in `.skill-map/settings.json` as the `activeProvider` key (see [`project-config.schema.json`](./schemas/project-config.schema.json#/properties/activeProvider)). When absent, the kernel auto-detects on first scan from filesystem markers and persists the result; if the heuristic is ambiguous (several markers), the CLI and UI prompt the user to pick one enabled provider. There is no unlensed state: when no marker is present at all, the lens resolves to the
|
|
73
|
+
The lens is project-scope state, living in `.skill-map/settings.json` as the `activeProvider` key (see [`project-config.schema.json`](./schemas/project-config.schema.json#/properties/activeProvider)). When absent, the kernel auto-detects on first scan from filesystem markers and persists the result; if the heuristic is ambiguous (several markers), the CLI and UI prompt the user to pick one enabled provider. There is no unlensed state: when no vendor marker is present at all, the lens resolves to the open-standard `agent-skills` view, the universal default lens, which is NOT persisted, so a vendor marker added later still auto-detects on the next scan. The non-gated `core/markdown` base still classifies every unclaimed `.md` underneath, but it is not itself a selectable lens (see below). **The marker set is provider-owned**: each Provider declares its detection markers in its manifest `detect.markers` block (see [`provider.schema.json`](./schemas/extensions/provider.schema.json#/properties/detect)), e.g. `claude` → `.claude/`, `codex` → `.codex/`, `agent-skills` → `.agents/` (`AGENTS.md` is deliberately NOT an `codex` marker: it is the open agents.md standard, common in non-Codex repos and alongside `.claude/`, so keying detection off it would mis-route plain-markdown repos and force ambiguous prompts; a genuine Codex project is identified by `.codex/`). No central hardcoded detection table; the detectable set derives from registered Providers, so adding a Provider with a marker makes it auto-detectable without touching the resolver. When several markers match, the resolver returns the full candidate list in Provider iteration order, first match the default suggestion. A Provider with no `detect` block is never auto-suggested but can be selected manually. Google's Antigravity CLI (which replaced the retired Gemini CLI on 2026-05-19) adopted the open-standard `.agents/` rather than a vendor marker, so a Google project's `.agents/` files are owned by the `agent-skills` provider; the `antigravity` lens is set manually via `sm config set activeProvider antigravity`. `antigravity` ships `experimental` (disabled by default, see below), so it does not auto-detect until enabled; `agent-skills` is `stable` (the locked open default lens), so a project's `.agents/` marker auto-detects it and a project with no vendor marker falls back to it.
|
|
74
74
|
|
|
75
|
-
**Not-ready Providers ship disabled.** A Provider that is registered but not yet ready for end users declares `stability: 'experimental'` (see [`base.schema.json`](./schemas/extensions/base.schema.json#/properties/stability)), which ships it **disabled by default**: it does not classify, does not register, is never auto-detected, and is absent from the `selectable` set served by `GET /api/active-provider` until the operator opts in (`sm plugins enable <id>`, the Settings toggle, or a config override). There is no separate `comingSoon` flag: enabled/disabled is the single availability axis, and `stability: 'experimental'` is just the installed default flipped off. Today `
|
|
75
|
+
**Not-ready Providers ship disabled.** A Provider that is registered but not yet ready for end users declares `stability: 'experimental'` (see [`base.schema.json`](./schemas/extensions/base.schema.json#/properties/stability)), which ships it **disabled by default**: it does not classify, does not register, is never auto-detected, and is absent from the `selectable` set served by `GET /api/active-provider` until the operator opts in (`sm plugins enable <id>`, the Settings toggle, or a config override). There is no separate `comingSoon` flag: enabled/disabled is the single availability axis, and `stability: 'experimental'` is just the installed default flipped off. Today `antigravity` is experimental, so `claude` (stable), `codex` (beta), and `agent-skills` (stable, the locked open default) are selectable by default; enabling the experimental `antigravity` makes it both selectable and classifying. The non-gated `core/markdown` base is locked-enabled but is NOT a selectable lens, it is the substrate beneath whatever lens is active (see §Active-lens scope for providers). `stability: 'beta'` ships ENABLED like `stable` but renders a maturity badge (it is NOT a disabled state); this is distinct from `hideChip`, which only suppresses the per-card badge.
|
|
76
76
|
|
|
77
77
|
### Consequence: one graph per project at a time
|
|
78
78
|
|
|
@@ -90,17 +90,17 @@ A provider plugin MAY declare it reads source files belonging to ANOTHER provide
|
|
|
90
90
|
|
|
91
91
|
### Universal extractors and per-provider extractors
|
|
92
92
|
|
|
93
|
-
The lens does NOT gate the universal extractors under `core/` (markdown links, code-region file paths, external URLs, sidecar annotations); their semantics are provider-agnostic, so they run regardless of the active provider. Provider-specific extractors (Claude's `@`-directive parser, Cursor's picker-derived references, the future Codex AGENTS.md walker, future Antigravity parsers) declare `precondition: { provider: '<id>' }` on their manifest; the orchestrator invokes them on every node visited during the scan as long as the **active lens** is in the declared provider list, regardless of which provider's `classify()` claimed the node.
|
|
93
|
+
The lens does NOT gate the universal extractors under `core/` (markdown links, code-region file paths, external URLs, sidecar annotations); their semantics are provider-agnostic, so they run regardless of the active provider. Provider-specific extractors (Claude's `@`-directive parser, Cursor's picker-derived references, the future Codex AGENTS.md walker, future Antigravity parsers) declare `precondition: { provider: '<id>' }` on their manifest; the orchestrator invokes them on every node visited during the scan as long as the **active lens** is in the declared provider list, regardless of which provider's `classify()` claimed the node. The declared list MAY name more than one lens: the claude `@`-directive and `/command` extractors run under both `claude` and `codex` (`precondition: { provider: ['claude', 'codex'] }`), because OpenAI Codex sub-agents share the same `@<name>` mention and `/command` grammar; codex declares `invokes: ['skill']`, so under the codex lens a `/name` slash signal resolves to the provider's open-standard `.agents/skills/<name>/SKILL.md` skills (Codex reads its skills from the open `.agents/skills/` layout). A body-scoped extractor reads whatever the walker yielded as the node body: for most providers the text after the frontmatter fence, but for a provider declaring `read.bodyField` (see [`provider.schema.json`](./schemas/extensions/provider.schema.json#/properties/read)) the named frontmatter field instead, since Codex sub-agents are pure TOML whose markdown prompt is the `developer_instructions` field, the codex provider sets `bodyField: 'developer_instructions'` so that prompt flows through the same body pipeline (body hash, markdown-link / backtick-path / external-url, and the lens-gated `@` / `/`).
|
|
94
94
|
|
|
95
|
-
The gate is the active lens, not the node's provider. A `@handle` token in `CLAUDE.md` or `notes/todo.md` (files the `claude` provider disclaims to `core/markdown`) still gets parsed by `claude/at-directive` under the `claude` lens, because the lens represents the runtime grammar and the runtime reads markdown across the whole project, not only files it owns. The earlier double-check ("node's provider matches AND the lens") silently dropped that surface; dropping the node side restores it. Cross-lens isolation holds via the lens half alone: under `
|
|
95
|
+
The gate is the active lens, not the node's provider. A `@handle` token in `CLAUDE.md` or `notes/todo.md` (files the `claude` provider disclaims to `core/markdown`) still gets parsed by `claude/at-directive` under the `claude` lens, because the lens represents the runtime grammar and the runtime reads markdown across the whole project, not only files it owns. The earlier double-check ("node's provider matches AND the lens") silently dropped that surface; dropping the node side restores it. Cross-lens isolation holds via the lens half alone: under `codex`, claude extractors are silent on every node (including `.claude/*`) because lens authorisation is missing. Under the open-standard `agent-skills` default lens (a project with no vendor marker), the `claude` / `codex`-gated extractors stay silent because `agent-skills` is not in their declared provider allowlist; only the universal extractors run, alongside the open-standard `skill` classifier.
|
|
96
96
|
|
|
97
97
|
### Active-lens scope for providers (classification gate)
|
|
98
98
|
|
|
99
|
-
The active lens also gates **classification**. Each Provider declares `gatedByActiveLens` on its manifest (`extensions/provider.schema.json#/properties/gatedByActiveLens`, mirrored at `IProvider.gatedByActiveLens`). Vendor providers (`claude`, `
|
|
99
|
+
The active lens also gates **classification**. Each Provider declares `gatedByActiveLens` on its manifest (`extensions/provider.schema.json#/properties/gatedByActiveLens`, mirrored at `IProvider.gatedByActiveLens`). Vendor providers (`claude`, `codex`, `antigravity`) and the open-standard `agent-skills` provider set it `true`; their `classify()` only runs (and the walker only iterates their territory) when `provider.id === activeProvider`. The markdown fallback `core/markdown` (and any future format-based fallback) leaves the flag `false` (default) and runs on every scan as the single universal base. **A provider is a selectable lens iff it is gated** (`gatedByActiveLens: true`): the gated providers are exactly the lenses the operator can pick or auto-detect; the non-gated `core/markdown` base is never offered in the lens dropdown nor persisted as `activeProvider`, it is the substrate beneath whatever lens is active. So a project's `.agents/skills/*` is classified as `skill` under the `agent-skills` lens (the open default when no vendor marker is present, and the lens `antigravity` reuses by manifest composition); under a vendor lens (`claude`, `codex`) those files fall through to `core/markdown`.
|
|
100
100
|
|
|
101
|
-
Filtering happens in `walkAndExtract` (kernel, `src/kernel/orchestrator/walk.ts`) at the provider-iteration level: a gated-off Provider does NOT walk its territory at all (the cheap path). The predicate: include the Provider when `!gatedByActiveLens || provider.id === activeProvider`. There is no unlensed branch: the resolver always yields a concrete lens (a vendor id, or `
|
|
101
|
+
Filtering happens in `walkAndExtract` (kernel, `src/kernel/orchestrator/walk.ts`) at the provider-iteration level: a gated-off Provider does NOT walk its territory at all (the cheap path). The predicate: include the Provider when `!gatedByActiveLens || provider.id === activeProvider`. There is no unlensed branch: the resolver always yields a concrete lens (a vendor id, or `agent-skills` as the open default when no vendor marker is present), so a gated vendor Provider participates only when it is the active lens; under the `agent-skills` default the open-standard classifier runs alongside the universal `core/markdown` base.
|
|
102
102
|
|
|
103
|
-
Consequence: under `activeProvider = 'claude'`, a `.codex/agents/foo.toml` is not classified by the `
|
|
103
|
+
Consequence: under `activeProvider = 'claude'`, a `.codex/agents/foo.toml` is not classified by the `codex` Provider (gated off); whether it becomes a node depends on whether a universal Provider claims its extension. Today no universal claims `.toml`, so the file is silently absent, matching runtime reality (Claude Code never consumes `.codex/`). The same path under `activeProvider = 'codex'` becomes `codex/agent`. The `core/markdown` fallback claims every unclaimed `.md` regardless of lens, so a `.claude/agents/foo.md` under `codex` lens reverts to `markdown` (no claude territory under that lens).
|
|
104
104
|
|
|
105
105
|
This gate affects **classification only**. Extractors keep filtering through their own `precondition.provider` allowlist (previous section); a gated-off vendor Provider contributes no classified nodes, but its bundled extractors still skip uniformly under the wrong lens via the extractor-side rule. The two gates are independent and complementary.
|
|
106
106
|
|
|
@@ -159,7 +159,7 @@ The loader enforces two id-uniqueness analyzers during discovery (see [`plugin-a
|
|
|
159
159
|
|
|
160
160
|
The loader also **qualifies every extension** with its owning plugin id before registering it, storing extensions under the qualified id `<plugin-id>/<extension-id>` (e.g. `core/slash-command`, `core/reference-broken`, `my-plugin/my-extractor`). Authors declare the short `id` in each extension manifest; the loader composes the qualified form from `manifest.id` at load time. Built-in extensions declare their `pluginId` directly in `built-ins.ts`: `core/` for kernel-internal primitives (every analyzer, the formatter, the cross-vendor extractors `annotations` / `slash` / `at-directive` / `markdown-link` / `backtick-path` / `external-url-counter` / `stability`) and vendor plugins such as `claude/` for platform-bound Provider integrations. A `pluginId` field on an extension that disagrees with `plugin.json`'s `id` yields `invalid-manifest` with a directed reason.
|
|
161
161
|
|
|
162
|
-
Every extension (built-in or drop-in) is independently toggle-able by its qualified id `<plugin>/<ext-id>`. The plugin row is a presentational grouping; the granular toggle target is the extension, while toggling a bare plugin id is the **bundle** (aggregate) macro fanning across every extension. The loader's pre-import `resolveEnabled(pluginId)` short-circuit only fires when EVERY extension of the plugin is disabled (the plugin "starts as disabled"); partial enables let imports proceed and the runtime composer (`composeScanExtensions` / `composeFormatters` in `src/core/runtime/plugin-runtime/composer.ts`) drops the per-extension disabled rows before they reach the orchestrator. The `core` plugin exercises the per-extension axis explicitly (every kernel built-in is removable, satisfying §Boot invariant); most operators leave every extension of the vendor Provider plugins (`claude`, `antigravity`, `
|
|
162
|
+
Every extension (built-in or drop-in) is independently toggle-able by its qualified id `<plugin>/<ext-id>`. The plugin row is a presentational grouping; the granular toggle target is the extension, while toggling a bare plugin id is the **bundle** (aggregate) macro fanning across every extension. The loader's pre-import `resolveEnabled(pluginId)` short-circuit only fires when EVERY extension of the plugin is disabled (the plugin "starts as disabled"); partial enables let imports proceed and the runtime composer (`composeScanExtensions` / `composeFormatters` in `src/core/runtime/plugin-runtime/composer.ts`) drops the per-extension disabled rows before they reach the orchestrator. The `core` plugin exercises the per-extension axis explicitly (every kernel built-in is removable, satisfying §Boot invariant); most operators leave every extension of the vendor Provider plugins (`claude`, `antigravity`, `codex`, `agent-skills`) enabled, but the same per-extension toggle surface applies. See [`plugin-author-guide.md` §Toggle model](./plugin-author-guide.md#toggle-model).
|
|
163
163
|
|
|
164
164
|
### `RunnerPort`
|
|
165
165
|
|
|
@@ -321,16 +321,16 @@ The `ui` block is required (not optional) by design: making it optional would fo
|
|
|
321
321
|
|
|
322
322
|
The kernel ships every Provider's per-kind `ui` block to the BFF at boot; the BFF aggregates them into a `kindRegistry` map embedded 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.
|
|
323
323
|
|
|
324
|
-
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. (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) on the same envelopes. The UI consumes `providerRegistry` to render the active-lens dropdown, topbar lens chip, and per-node provider chip on cards from the real registered-Provider set, never a hardcoded list. `hideChip: true` (set by the universal `markdown`
|
|
324
|
+
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. (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) on the same envelopes. The UI consumes `providerRegistry` to render the active-lens dropdown, topbar lens chip, and per-node provider chip on cards from the real registered-Provider set, never a hardcoded list. Each entry carries an `isLens` flag projected from the Provider's `gatedByActiveLens`: the dropdown lists only lens entries (gated Providers), so the non-gated `core/markdown` base never appears there even though it keeps a registry entry for chip lookups. `hideChip: true` (set by the universal `markdown` base) suppresses the per-card chip; combined with `isLens: false` the base shows on no lens surface at all. Unlike kind colors (normalised across Providers so every `agent` paints the same), Provider colors are deliberately distinct so the chip tells the user which platform a node came from.
|
|
325
325
|
|
|
326
326
|
### Provider · dispatch order and the universal markdown fallback
|
|
327
327
|
|
|
328
|
-
`sm scan` iterates Providers in **registration order**, vendor-specific Providers first (built-in: `claude` → `antigravity` → `
|
|
328
|
+
`sm scan` iterates Providers in **registration order**, vendor-specific Providers first (built-in: `claude` → `antigravity` → `codex` → `agent-skills`; user-installed plugins follow in load order), then the built-in `core/markdown` Provider LAST. Each Provider's walker enumerates the full project tree for its declared `read.extensions` (a Provider with a multi-rule `read` array runs one pass per rule, e.g. `codex` walks `.toml` sub-agents then `.md` open-standard skills); for every emitted file the orchestrator calls `provider.classify(path, frontmatter)`. The kernel maintains a per-scan `Set<path>` of already-classified files so each path is offered to AT MOST one Provider's `classify`: the first Provider whose `classify` returns non-null claims the file; subsequent Providers see the path as taken and skip.
|
|
329
329
|
|
|
330
330
|
The dispatch contract has two consequences implementations MUST honour:
|
|
331
331
|
|
|
332
332
|
1. **First-claim-wins**. A vendor Provider that classifies a file inside its territory (e.g. claude's `.claude/agents/foo.md` → `agent`) is authoritative; later Providers cannot reclassify it. This locks vendor ownership of vendor paths and removes the historical `provider-ambiguous` failure mode for non-overlapping territories.
|
|
333
|
-
2. **`core/markdown` is the universal fallback for unclaimed `.md` files**. Its `classify` returns `'markdown'` unconditionally (it does NOT inspect the path). Combined with the dedup guarantee above and its terminal position, it picks up exactly the `.md` files no vendor Provider claimed: a `.md` at the project root, under `.claude/hooks/`, `notes/`, `CLAUDE.md`, `GEMINI.md`, or anywhere outside a known vendor territory. The fallback is **not privileged kernel code**: it ships as a regular built-in Provider under the `core` plugin,
|
|
333
|
+
2. **`core/markdown` is the universal fallback for unclaimed `.md` files**. Its `classify` returns `'markdown'` unconditionally (it does NOT inspect the path). Combined with the dedup guarantee above and its terminal position, it picks up exactly the `.md` files no vendor Provider claimed: a `.md` at the project root, under `.claude/hooks/`, `notes/`, `CLAUDE.md`, `GEMINI.md`, or anywhere outside a known vendor territory. The fallback is **not privileged kernel code**: it ships as a regular built-in Provider under the `core` plugin. It is locked-enabled, though (`core/markdown` in the host lock-list, alongside the open default lens `agent-skills/agent-skills`): disabling the universal base would make every orphan `.md` silently invisible, a foot-gun the host does not expose.
|
|
334
334
|
|
|
335
335
|
The fallback exists because the format-named generic kind `markdown` is provider-agnostic: no vendor owns the universal markdown format. Keeping it as a Provider (not a kernel-level special case) preserves the boot invariant that no extension is privileged; a future vendor Provider (Codex, Cursor, Roo) slots into the iteration order before `core/markdown` and the fallback semantics stay invariant.
|
|
336
336
|
|
|
@@ -371,7 +371,7 @@ The rules below describe both halves together. The kernel seeds `confidence: 1.0
|
|
|
371
371
|
|
|
372
372
|
The matrix is **per-link-kind, per-Provider**, strict: a `claude` Provider declaring `resolution: { mentions: ['agent'], invokes: ['command', 'skill'] }` does NOT resolve a `/foo` slash matching an agent named `foo` (slash → agent is a kind mismatch surfaced by `link-kind-conflict` / `kind-mismatch` analyzers, not silently treated as a resolution). The strictness is the load-bearing difference from the kind-agnostic `core/reference-broken`: `broken-ref`'s scope is "the name exists somewhere", post-walk resolution is "the name exists AS A VALID resolution for this link.kind". The `not-broken` + `not-resolved` combination is the documented edge case: the trigger resolves to a real node but the link's kind cannot legitimately point there, so no built-in detector touches it and it keeps the 1.0 baseline.
|
|
373
373
|
|
|
374
|
-
The lookup uses the ACTIVE PROVIDER LENS deliberately, mirroring the extractor gate (§Universal extractors and per-provider extractors): the lens grammar applies across the project's surface, not only files the matching `classify()` claimed. A `@handle` in `notes/todo.md` (classified by `core/markdown`) under the `claude` lens parses as a claude mention (extractor gate authorises it) and resolves against claude's `resolution.mentions` (resolver gate mirrors the authority). The same body under `
|
|
374
|
+
The lookup uses the ACTIVE PROVIDER LENS deliberately, mirroring the extractor gate (§Universal extractors and per-provider extractors): the lens grammar applies across the project's surface, not only files the matching `classify()` claimed. A `@handle` in `notes/todo.md` (classified by `core/markdown`) under the `claude` lens parses as a claude mention (extractor gate authorises it) and resolves against claude's `resolution.mentions` (resolver gate mirrors the authority). The same body under `codex` follows codex's resolution map, or short-circuits if codex declares no entry for that `link.kind`. Under the open-standard `agent-skills` default lens (a project with no vendor marker), the resolver consults `agent-skills`'s `resolution` map (`invokes: ['skill']`) rather than short-circuiting; path-match still applies, and the non-gated `core/markdown` base declares no resolution map of its own.
|
|
375
375
|
|
|
376
376
|
**Distinct from the Signal IR `resolverRules` (§Resolver phase).** `resolverRules` rank candidates INSIDE a Signal (Phase 3+, no Provider declares it today); `resolution` runs against the merged Link graph post-walk and is the contract Extractors EMITTING Links rely on. The two surfaces share no mechanism and do not compose; when a Signal IR materialises into a Link, the `resolution` matrix runs unchanged against the resulting Link.
|
|
377
377
|
|
|
@@ -381,7 +381,7 @@ Each Provider MAY declare an optional `reservedNames: Record<kind, string[]>` ma
|
|
|
381
381
|
|
|
382
382
|
The kernel intersects each Provider's `reservedNames[kind]` catalog with the scanned graph at orchestrator time. For every node the post-walk pipeline derives its normalised identifiers via the §Provider · kind identifiers contract, then tests them against the reserved set of the node's OWN Provider (**self scope**): `reservedNames[node.kind]` of `node.provider`. Claude classifies `.claude/commands/help.md` as `claude`/`command` and reserves `help` under `command`, so the file is flagged.
|
|
383
383
|
|
|
384
|
-
A runtime that adopts the open `.agents/skills/` standard instead of a vendor directory **reuses the `agent-skills` classifier + `skill` kind in its OWN manifest** (plain manifest composition, no kernel rule)
|
|
384
|
+
A runtime that adopts the open `.agents/skills/` standard instead of a vendor directory **reuses the `agent-skills` classifier + `skill` kind in its OWN manifest** (plain manifest composition, no kernel rule), and with them the **base reserved-name catalog the neutral `agent-skills` Provider owns** under `skill`: the universal slash commands an agent CLI ships built-in regardless of vendor (`help`, `config`, `mcp`, `model`, `clear`, `exit`, …). The open standard itself documents no reserved names, so this base is skill-map's curated cross-vendor common subset, not a clause of the standard; the neutral `agent-skills` lens enforces exactly this base, so a user `.agents/skills/help/SKILL.md` is flagged under it. A vendor that adopts the standard **spreads the base and appends its OWN runtime-specific verbs**: Google's Antigravity reuses the classifier and, on top of the inherited base, reserves the rest of `agy`'s built-in slash commands under `skill`, so when `activeProvider === 'antigravity'` a user `.agents/skills/goal/SKILL.md` (classified as `antigravity`/`skill`) is flagged because `/goal` is a built-in, while the neutral base never carries `agy`-specific verbs (a future Codex lens that adopts the standard inherits the same base, not Antigravity's extras). There is no cross-provider "lens scope": each lens classifies its own territory and self scope tests it against that Provider's own (base + extras) catalog.
|
|
385
385
|
|
|
386
386
|
A node landing in the reserved set joins a per-scan `Set<nodePath>` consumed by the score-phase `core/name-reserved` analyzer, which co-locates two effects in one pass (detection still lives in the orchestrator, so the same set drives both):
|
|
387
387
|
|
package/cli-contract.md
CHANGED
|
@@ -109,11 +109,11 @@ The project sees its filesystem through exactly one **active provider lens** at
|
|
|
109
109
|
|
|
110
110
|
CLI surfaces:
|
|
111
111
|
|
|
112
|
-
- **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/`
|
|
112
|
+
- **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/` → `codex`, `.agents/` → `agent-skills`; `AGENTS.md` is deliberately not a marker, it is the vendor-neutral agents.md standard). The marker set is provider-owned, not hardcoded. On unambiguous match, the result is persisted to `settings.json` and the scan proceeds; on no match, the lens defaults to the open-standard `agent-skills` view (the universal default lens) without persisting it, and the scan proceeds silently (a vendor marker added later still auto-detects on the next scan); on ambiguous match (multiple detected), it prompts interactively (or fails with exit code 2 under `--yes` if no default is configured). Google's Antigravity CLI declares no vendor-specific marker (it adopted the open-standard `.agents/` layout, auto-detected as `agent-skills`); the `antigravity` lens is set manually via `sm config set activeProvider antigravity`.
|
|
113
113
|
- **Manual override**: `sm config set activeProvider <id>` switches the lens, drops the `scan_*` zone atomically (see [`db-schema.md`](./db-schema.md#zones)), and triggers an immediate rescan under the new lens. `state_*` and `config_*` zones survive.
|
|
114
114
|
- **No per-scan flag**: there is no `sm scan --provider=<id>` flag. The lens is a project-level decision; the drop+rescan cost makes per-invocation switching the wrong default UX.
|
|
115
115
|
|
|
116
|
-
**UI lens-selection surface.** `GET /api/active-provider` returns `{ activeProvider, detected, source, selectable }`. `selectable` is the set of registered
|
|
116
|
+
**UI lens-selection surface.** `GET /api/active-provider` returns `{ activeProvider, detected, source, selectable }`. `selectable` is the set of registered **lens** Provider ids (gated Providers, `gatedByActiveLens: true`) enabled right now, resolved against the live per-extension enabled resolver (`config_plugins` layered over `settings.json#/plugins`, same as `GET /api/plugins`), the subset of `providerRegistry` eligible to become the lens. The non-gated `core/markdown` base is never in `selectable` (it is the substrate, not a lens). A disabled lens Provider is dropped from `selectable` but stays in `providerRegistry` (the static boot catalog keeps it so already-scanned nodes still render their chip / icon). Each `providerRegistry` entry carries an `isLens` flag projected from `gatedByActiveLens`; the SPA's active-lens dropdown lists only the `isLens` Providers and renders those absent from `selectable` disabled (greyed, not selectable), so the markdown base never appears in the dropdown and a disabled lens can never be picked. `PATCH /api/active-provider` rejects an `activeProvider` that is not a selectable lens. This mirrors the scan-time contract that a lens pointing at a disabled Provider runs none of its extractors (the runtime soft-warns on that drift); the dropdown closes the loop by refusing to create the drift.
|
|
117
117
|
|
|
118
118
|
---
|
|
119
119
|
|
|
@@ -191,7 +191,7 @@ The destination is the selected Provider's `scaffold.skillDir` (e.g. `.claude/sk
|
|
|
191
191
|
- Without `--for`, the default is the first scaffold-capable Provider in catalog order (Claude). The verb requires an empty cwd (see below), so there is no marker to detect: provider auto-detection does not apply.
|
|
192
192
|
- Without `--for`, on interactive stdin the verb prompts with a numbered list of Providers declaring `scaffold.skillDir`, marking the default (Claude); an empty answer accepts it. Each option shows the Provider label plus any `scaffold.aka` agents in parentheses (e.g. the open standard lists Antigravity and OpenAI Codex). The `aka` strings are display-only, NOT accepted by `--for`.
|
|
193
193
|
- Without `--for`, on non-interactive stdin (pipes, CI) the verb selects the default without prompting, staying scriptable.
|
|
194
|
-
- `--experimental` includes Providers flagged `stability: 'experimental'` (today `
|
|
194
|
+
- `--experimental` includes Providers flagged `stability: 'experimental'` (today `agent-skills`) as scaffold destinations and enables them in the seeded fixture so the demo scan classifies their nodes. Without it, experimental Providers are omitted from the prompt and `--for <experimental-id>` is a usage error (they ship disabled by default). Default behaviour offers only stable Providers (Claude).
|
|
195
195
|
|
|
196
196
|
Behaviour:
|
|
197
197
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{ "type": "json-path", "path": "$.providers.length", "equals": 5 },
|
|
14
14
|
{ "type": "json-path", "path": "$.providers[0]", "equals": "claude" },
|
|
15
15
|
{ "type": "json-path", "path": "$.providers[1]", "equals": "antigravity" },
|
|
16
|
-
{ "type": "json-path", "path": "$.providers[2]", "equals": "
|
|
16
|
+
{ "type": "json-path", "path": "$.providers[2]", "equals": "codex" },
|
|
17
17
|
{ "type": "json-path", "path": "$.providers[3]", "equals": "agent-skills" },
|
|
18
18
|
{ "type": "json-path", "path": "$.providers[4]", "equals": "markdown" },
|
|
19
19
|
{ "type": "json-path", "path": "$.nodes.length", "equals": 1 }
|
package/conformance/coverage.md
CHANGED
|
@@ -18,7 +18,7 @@ Hand-maintained. A CI check before spec release compares the schema inventory ag
|
|
|
18
18
|
| 8 | `job.schema.json` |, | 🔴 missing | Blocked by Step 10 (job system). Needs a case that submits a local action (no LLM), inspects `sm job show --json`. |
|
|
19
19
|
| 9 | `report-base.schema.json` |, | 🔴 missing | Indirect coverage once any summarizer case lands. Direct contract case: validate a handcrafted minimal report ({confidence, safety}) against the base schema. |
|
|
20
20
|
| 10 | `conformance-case.schema.json` |, | 🔴 missing | Self-referential: every `*.json` under `cases/` MUST validate against this schema. Add a meta-case that enumerates + validates all cases. |
|
|
21
|
-
| 11 | `frontmatter/base.schema.json` | `orphan-markdown-fallback` | 🟢 covered | Universal frontmatter shape, `name` + `description` only, `additionalProperties: true`. Per-kind schemas live with the Provider that emits them: vendor kinds (`skill` / `agent` / `command`) under `src/built-in-plugins/providers/{claude,
|
|
21
|
+
| 11 | `frontmatter/base.schema.json` | `orphan-markdown-fallback` | 🟢 covered | Universal frontmatter shape, `name` + `description` only, `additionalProperties: true`. Per-kind schemas live with the Provider that emits them: vendor kinds (`skill` / `agent` / `command`) under `src/built-in-plugins/providers/{claude,codex,agent-skills}/schemas/`; the format-named generic `markdown` kind under `src/built-in-plugins/providers/core-markdown/schemas/` (spec 0.18.0, markdown is provider-agnostic). All extend this base via `$ref`-by-`$id`. `orphan-markdown-fallback` exercises base-only frontmatter end-to-end via the `ARCHITECTURE.md` fixture file (no kind-specific extras). |
|
|
22
22
|
| 12 | `summaries/skill.schema.json` |, | 🔴 missing | Blocked by Step 10 (`skill-summarizer`). Case: submit summarizer, validate report. |
|
|
23
23
|
| 13 | `summaries/agent.schema.json` |, | 🔴 missing | Blocked by Step 11. |
|
|
24
24
|
| 14 | `summaries/command.schema.json` |, | 🔴 missing | Blocked by Step 11. |
|
|
@@ -4,7 +4,7 @@ description: Top-level markdown that no vendor Provider claims. Picked up by cor
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
This file lives at the project root with no platform-specific path
|
|
7
|
-
prefix. The claude /
|
|
7
|
+
prefix. The claude / codex / agent-skills Providers all return null
|
|
8
8
|
on it; the built-in `core/markdown` Provider claims it as kind
|
|
9
9
|
`markdown`. Without the universal fallback it would be silently
|
|
10
10
|
dropped from the scan.
|
package/db-schema.md
CHANGED
|
@@ -73,7 +73,7 @@ One row per detected node, matching [`schemas/node.schema.json`](./schemas/node.
|
|
|
73
73
|
| Column | Type | Constraint | Notes |
|
|
74
74
|
|---|---|---|---|
|
|
75
75
|
| `path` | TEXT | PRIMARY KEY | Relative path from scope root. Canonical node identifier. |
|
|
76
|
-
| `kind` | TEXT | NOT NULL | Open-by-design (`node.schema.json#/properties/kind`): whatever the classifying Provider declares. Built-in catalogs: `claude` ships `skill` / `agent` / `command` / `mcp`; `
|
|
76
|
+
| `kind` | TEXT | NOT NULL | Open-by-design (`node.schema.json#/properties/kind`): whatever the classifying Provider declares. Built-in catalogs: `claude` ships `skill` / `agent` / `command` / `mcp`; `codex` ships `agent`; `agent-skills` ships `skill`; `core/markdown` ships the format-named generic fallback `markdown` (universal, picks up any `.md` no vendor Provider claims, see `architecture.md` §Provider · dispatch order). The `antigravity` Provider reuses the `agent-skills` `skill` kind + classifier (manifest composition), so under its lens `.agents/skills/<n>/SKILL.md` rows carry `provider: 'antigravity'`, `kind: 'skill'`. External Providers MAY emit their own. |
|
|
77
77
|
| `provider` | TEXT | NOT NULL | Provider extension id. |
|
|
78
78
|
| `title` | TEXT | NULL | |
|
|
79
79
|
| `description` | TEXT | NULL | |
|
package/index.json
CHANGED
|
@@ -174,14 +174,14 @@
|
|
|
174
174
|
}
|
|
175
175
|
]
|
|
176
176
|
},
|
|
177
|
-
"specPackageVersion": "0.
|
|
177
|
+
"specPackageVersion": "0.60.0",
|
|
178
178
|
"integrity": {
|
|
179
179
|
"algorithm": "sha256",
|
|
180
180
|
"files": {
|
|
181
|
-
"CHANGELOG.md": "
|
|
181
|
+
"CHANGELOG.md": "280befaa7de6239b81a8fe11068835b4688e2fc76892e5826ed7f929514dc085",
|
|
182
182
|
"README.md": "a790cd010b46d47883d1f37e3893cea9d7aa69ec4750c0202e6a0c99991e7980",
|
|
183
|
-
"architecture.md": "
|
|
184
|
-
"cli-contract.md": "
|
|
183
|
+
"architecture.md": "88f1af51a2d822535c813241c87a80b3889ecd60bac4dcc84318e00e1946ab5f",
|
|
184
|
+
"cli-contract.md": "3494cd18c15e4d85d694b4a7cbb4e364dad1385bd809ec3656e2f48f8238c41b",
|
|
185
185
|
"conformance/README.md": "dcbef7249f161acf597552a05dcadc813cd0ced430dcd3f813fcf5e1c876335d",
|
|
186
186
|
"conformance/cases/backtick-path-extraction.json": "4620e7f8bc161fc57cb44001e9d99879c7e22b4865a0c27a20dc28969cd936d9",
|
|
187
187
|
"conformance/cases/extractor-collision-detection.json": "179a02c61892f0d26492de0c4e2c327fa6b4986d1265a8f119e871df6afe4658",
|
|
@@ -189,22 +189,22 @@
|
|
|
189
189
|
"conformance/cases/kernel-empty-boot.json": "9b51b85ff62479cd0eee37cad260245208d94f6d79644f7ee40945a934960913",
|
|
190
190
|
"conformance/cases/no-global-scope.json": "1c83343422144be2ad9e3d27d2062e61af87c7c1c1f3b051b6b9f687d845ac7b",
|
|
191
191
|
"conformance/cases/orphan-markdown-fallback.json": "506119323ddde85c1fb4c986c7f6f40a345d44adb06de8d84002591df0e479ee",
|
|
192
|
-
"conformance/cases/plugin-missing-ui-rejected.json": "
|
|
192
|
+
"conformance/cases/plugin-missing-ui-rejected.json": "0d9954cc8b83b4991aa19811194934d414705a0fda802f087dfdf84acb0963f2",
|
|
193
193
|
"conformance/cases/score-phase-confidence.json": "aa3a06149d78ff056dd1a47852baaedc200e47b0d5b1e778d3459ae62f65f390",
|
|
194
194
|
"conformance/cases/sidecar-end-to-end.json": "06374e619df1691f1593b0847b3671299318525d4a7bf4ff9bfda3ab03032a5f",
|
|
195
195
|
"conformance/cases/view-action-button.json": "51331f725be1c3655351f8fca6fc9d3d301ae68ea1741ff6c79998332ba2dfeb",
|
|
196
196
|
"conformance/cases/view-contribution-payloads.json": "e8f54ed62e64a2a0f86729866e507abb1f4246683f0e60d538280536f7cd3ecc",
|
|
197
197
|
"conformance/cases/view-slots-all.json": "05284e0324dd2da72b6b21d397c11b355802229a68053e9dddc323f69b3a1eba",
|
|
198
|
-
"conformance/coverage.md": "
|
|
198
|
+
"conformance/coverage.md": "c4fe7c634a852862444cc7e7b0318296bc83ce6802febe405904a8ce35692e31",
|
|
199
199
|
"conformance/fixtures/backtick-path/docs/target.md": "a09ae2cb4c96358a2e0692215f172b0f8c48028b6b123e4e83424b28302e644c",
|
|
200
200
|
"conformance/fixtures/backtick-path/source.md": "217f78b12b3ff47a938a5cc9c1ff7d6989d6a1db82bd1ddf3656787f31efb902",
|
|
201
201
|
"conformance/fixtures/orphan-markdown/.claude/agents/reviewer.md": "7f062731106f2d9811e4fffcf6ab44b8dfff4cfb16536a469514cc0664e832bf",
|
|
202
|
-
"conformance/fixtures/orphan-markdown/ARCHITECTURE.md": "
|
|
202
|
+
"conformance/fixtures/orphan-markdown/ARCHITECTURE.md": "5bcbe539855b9cdcb35e9ff1f708f0ffb95d814aa493b4246eca52a7be7e11ee",
|
|
203
203
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/kinds/markdown/kind.json": "6676a89bae5197e23cf50f1c11d596db558ac80f7334a7208fe57d8b92422251",
|
|
204
204
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/kinds/markdown/schema.json": "42795e7f1759fa25115a426edf5cd1b0c91b091b408aeee3f4f9fbc8f89f32bc",
|
|
205
205
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/plugin.json": "15164a6bc9e3ad21cefa532af3d4edff1b10cf6140d7f576332dc38800512e35",
|
|
206
206
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/providers/bad-provider/index.js": "df0d9f200e401b6b13115d6e97e5a00a78074229bd86c22ec5321accf5673c01",
|
|
207
|
-
"conformance/fixtures/plugin-missing-ui/.skill-map/settings.json": "
|
|
207
|
+
"conformance/fixtures/plugin-missing-ui/.skill-map/settings.json": "430ef2ed79f4c17a37d08042a7f363c856d99d40a88b8a27275a571b65f22fb5",
|
|
208
208
|
"conformance/fixtures/plugin-missing-ui/notes/example.md": "55767f0aa1b6774546a99f28c58e7b732aa9cfa5dfce8d0326470f7f622f577e",
|
|
209
209
|
"conformance/fixtures/preamble-v1.txt": "1e0aeef224b64477bdc13a949c3ad402e68249caf499ecdba1302371677c068b",
|
|
210
210
|
"conformance/fixtures/score-phase-confidence/.skill-map/plugins/link-scorer/analyzers/demote/index.js": "713ea8ea9585e966fe07ccab281be426cbee08223d60af427ca808581787de60",
|
|
@@ -230,16 +230,16 @@
|
|
|
230
230
|
"conformance/fixtures/view-contribution-payloads/notes/example.md": "312b1919cd7fd0f233648b053acfb2975662ede3c65dd391cc508204b67ad6fb",
|
|
231
231
|
"conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/analyzers/everything/index.js": "ea0022fec7f0fd5a26ba12db1310335f434f2f820682206a3a9542d98db0d346",
|
|
232
232
|
"conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/plugin.json": "c48e8a0574947ade0b4eb189d6bc27a48e24f92f616aacdc177f2d22d472a599",
|
|
233
|
-
"db-schema.md": "
|
|
233
|
+
"db-schema.md": "4c0f4df57f98b0b91a97382a4570f6178179a2f5282744a1d39d3b123ad18445",
|
|
234
234
|
"interfaces/security-scanner.md": "0996dd782e2d39d4791f2e290da4bb1a68a5b30c1f79187977188ec8e3fe6ef2",
|
|
235
235
|
"job-events.md": "2c7017f5f0003b19653424111a07043487173cbe88b51e961598bb1693987059",
|
|
236
236
|
"job-lifecycle.md": "ce33bc8bb5090ea183f860e495bfccc2a4a0ac2e23f6ebad83b9c28aad59124e",
|
|
237
|
-
"plugin-author-guide.md": "
|
|
237
|
+
"plugin-author-guide.md": "c628ef0867dd8321f261d9e806954b17cda14f8080a92af5623dfa9377fd191e",
|
|
238
238
|
"plugin-kv-api.md": "5e095581020043af73ff028e272f56d42ca9eb6e506dd777d45703f9db796a5b",
|
|
239
239
|
"plugin-quickstart.md": "19092b278d80df357ea623dc3bd9f833d059582ee1356f317621913d91e50512",
|
|
240
240
|
"prompt-preamble.md": "5d0f836688aa23eafc32104c3174132340b268361f6060326eec84da17c6ad6d",
|
|
241
241
|
"schemas/annotations.schema.json": "09fcebc86e3b793bf9f03a35b38e5ca2a08d79ac3504f6f03895ac2ae1c2aded",
|
|
242
|
-
"schemas/api/rest-envelope.schema.json": "
|
|
242
|
+
"schemas/api/rest-envelope.schema.json": "28b9155476dbeca18b51ba019deb5cfa687503574f8b1d691830c60746ede9d4",
|
|
243
243
|
"schemas/bump-report.schema.json": "c763e1f89f2665c479d6a4985c1d324c65e5278331ebab82220287a07e4c4429",
|
|
244
244
|
"schemas/conformance-case.schema.json": "958b316d646d0c64a715a7a28cee66d2c2d2498a60dbfc5ae8970687c2a96954",
|
|
245
245
|
"schemas/conformance-result.schema.json": "14f983a8f4e62cd4ff964688c9b2b026a3bee3a0b762b64091c8c34db5b75777",
|
|
@@ -251,8 +251,8 @@
|
|
|
251
251
|
"schemas/extensions/formatter.schema.json": "880dc379ad545a62404403533a01eda5171edba0390561fc46ec6e986e0b9bd3",
|
|
252
252
|
"schemas/extensions/hook.schema.json": "f56aef59e9986ffdf7d86aa2e048dccccf217000a358b8c64737cbd911c48dad",
|
|
253
253
|
"schemas/extensions/provider-kind.schema.json": "499b2418bbe6d8a84a1608e26c56b52c2652a30ce314bc2989094418797dc1e6",
|
|
254
|
-
"schemas/extensions/provider.schema.json": "
|
|
255
|
-
"schemas/frontmatter/base.schema.json": "
|
|
254
|
+
"schemas/extensions/provider.schema.json": "8d2022ee0721233abcf35aa019168c41f69214035b718c1da1f9b15839370a4a",
|
|
255
|
+
"schemas/frontmatter/base.schema.json": "47f05ffa2a51f465f1b8df70cc7a1e7afe2c40f8d37826cd8a569977e9036b8d",
|
|
256
256
|
"schemas/history-stats.schema.json": "436aa0ffe744bdb699000447e86b45724fbd2cc4642781074eb1527522b9058c",
|
|
257
257
|
"schemas/input-types.schema.json": "93b27a1cbd1f131d42730eb9a89cf3af6889e9f17b20a48ce36133885503e01b",
|
|
258
258
|
"schemas/issue.schema.json": "840198acc36ed17f30957733dfaf4463d07d72911b13fb7f58b037a7dcf2d5b1",
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
"schemas/node.schema.json": "1ebba38e0c0ae022fccbc0cdf7c298da1720a68d4cb375f0baf9f0847998a0d8",
|
|
262
262
|
"schemas/plugins-doctor.schema.json": "03e2dc51c052a09bf0198c80e2c26e6129734ada4a748e483245de3dd8576c42",
|
|
263
263
|
"schemas/plugins-registry.schema.json": "211d081691fc83526e1593c79ed9741ad8a5dbd4db1a756f72141b0cced2ea15",
|
|
264
|
-
"schemas/project-config.schema.json": "
|
|
264
|
+
"schemas/project-config.schema.json": "67e4d83d69b336bc2017cc438ba6d562445fd97061e7e543a16d4e951b7047fc",
|
|
265
265
|
"schemas/refresh-report.schema.json": "47184d4f6b15e9b7671dc178b3b3886a64422da198898508ecdb2cb27876db04",
|
|
266
266
|
"schemas/report-base-deterministic.schema.json": "59785fe6f3ceb34814bbbd03d10fa7336a32835ce598946f2923d469b32aa32a",
|
|
267
267
|
"schemas/report-base.schema.json": "e4d25f055e24f18ae0f77c24661c1bddc87ff2e43b001b6a827fcb14f9753f44",
|
|
@@ -275,7 +275,7 @@
|
|
|
275
275
|
"schemas/summaries/skill.schema.json": "85d68056054bade62391948cc038fcfa70cdcf465e2b295f69cd520bbdba0134",
|
|
276
276
|
"schemas/user-settings.schema.json": "d155552ffca9c7dd4c6e31398aff4950dd9721d2a1f4b308cf0fe33000ca31b5",
|
|
277
277
|
"schemas/view-slots.schema.json": "886487a1f38fd7e4270fa6213653664c0cf906043e8aa9e832017149932bf6a2",
|
|
278
|
-
"telemetry.md": "
|
|
278
|
+
"telemetry.md": "1172bdee6812e7f5f657e36ad8e97ac3879eecfa4688faa7162b04c50d306b39",
|
|
279
279
|
"versioning.md": "142c9cd4521e001216114d477b635d8dff6e38a9a33105cef814b6ca2d2eecf2"
|
|
280
280
|
}
|
|
281
281
|
}
|
package/package.json
CHANGED
package/plugin-author-guide.md
CHANGED
|
@@ -92,7 +92,7 @@ Examples from the reference impl's built-in extensions:
|
|
|
92
92
|
Built-ins split between two namespaces:
|
|
93
93
|
|
|
94
94
|
- **`core/`**, kernel-internal primitives, platform-agnostic: every built-in analyzer, the ASCII formatter, the cross-vendor extractors (`annotations`, `markdown-link`, `backtick-path`, `external-url-counter`), the universal `markdown` Provider fallback, and the `update-check` hook.
|
|
95
|
-
- **`claude/`**, the Claude Code Provider plugin: the Provider plus the Claude-flavoured extractors (`slash-command`, `at-directive`). Other vendor plugins (`antigravity`, `
|
|
95
|
+
- **`claude/`**, the Claude Code Provider plugin: the Provider plus the Claude-flavoured extractors (`slash-command`, `at-directive`). Other vendor plugins (`antigravity`, `codex`, `agent-skills`) follow the same shape (Provider only).
|
|
96
96
|
|
|
97
97
|
### Extension id shape
|
|
98
98
|
|
|
@@ -108,7 +108,7 @@ Two id shapes resolve at the toggle surface:
|
|
|
108
108
|
|
|
109
109
|
- **Qualified id** (`<plugin>/<ext-id>`): flips exactly that extension. No prompt.
|
|
110
110
|
- **Bare plugin id** (`claude`, `core`): the **bundle (aggregate) macro form**, fans the toggle across every extension inside the plugin.
|
|
111
|
-
- Single-extension plugin (`
|
|
111
|
+
- Single-extension plugin (`codex`, `antigravity`, `agent-skills`): applies directly, no prompt.
|
|
112
112
|
- Multi-extension plugin (`claude`, `core`): requires `--yes` OR an interactive TTY confirm. CI / pipe contexts must pass `--yes`.
|
|
113
113
|
|
|
114
114
|
`--all` is the cascade variant: expands to every extension in every discovered plugin under the same `--yes` / TTY-confirm gate.
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"providers": {
|
|
68
68
|
"type": "object",
|
|
69
69
|
"minProperties": 1,
|
|
70
|
-
"description": "Per-provider visuals for this kind name. Keyed by Provider id (e.g. `'claude'`, `'
|
|
70
|
+
"description": "Per-provider visuals for this kind name. Keyed by Provider id (e.g. `'claude'`, `'codex'`). Lets the UI render a node painted with its own Provider's color via `entry.providers[node.provider]` when the kind name is shared across Providers.",
|
|
71
71
|
"additionalProperties": {
|
|
72
72
|
"type": "object",
|
|
73
73
|
"required": ["label", "color"],
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"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 + the enabled `selectable` set) is NOT here; it is served by `GET /api/active-provider`, a per-project dynamic surface orthogonal to this static boot catalog.",
|
|
143
143
|
"additionalProperties": {
|
|
144
144
|
"type": "object",
|
|
145
|
-
"required": ["label", "color"],
|
|
145
|
+
"required": ["label", "color", "isLens"],
|
|
146
146
|
"additionalProperties": false,
|
|
147
147
|
"properties": {
|
|
148
148
|
"label": { "type": "string", "minLength": 1 },
|
|
@@ -171,9 +171,18 @@
|
|
|
171
171
|
}
|
|
172
172
|
]
|
|
173
173
|
},
|
|
174
|
+
"isLens": {
|
|
175
|
+
"type": "boolean",
|
|
176
|
+
"description": "True when this Provider is a selectable lens, projected from its `gatedByActiveLens` flag. The active-lens dropdown lists only `isLens: true` entries; the non-gated `core/markdown` base is `false` and never appears there. Independent of `selectable` (which marks which lenses are ENABLED right now): a disabled lens stays `isLens: true` and renders greyed."
|
|
177
|
+
},
|
|
174
178
|
"hideChip": {
|
|
175
179
|
"type": "boolean",
|
|
176
|
-
"description": "When `true`, the UI suppresses this Provider's per-card chip (reserved for the universal `markdown`
|
|
180
|
+
"description": "When `true`, the UI suppresses this Provider's per-card chip (reserved for the universal `markdown` base). Combined with `isLens: false` the base appears on no lens surface at all."
|
|
181
|
+
},
|
|
182
|
+
"bodyField": {
|
|
183
|
+
"type": "string",
|
|
184
|
+
"minLength": 1,
|
|
185
|
+
"description": "Name of the parsed-frontmatter field that carries this Provider's node body, projected from its `read.bodyField` (see `provider.schema.json`). Present only for Providers whose prompt lives inside structured frontmatter rather than after a fence (OpenAI Codex sub-agents are pure TOML whose markdown prompt is `developer_instructions`). The UI uses it to render that field as the node body and to omit it from the metadata dump; absent for ordinary frontmatter-fence Providers."
|
|
177
186
|
}
|
|
178
187
|
}
|
|
179
188
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"label": {
|
|
20
20
|
"type": "string",
|
|
21
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 (
|
|
22
|
+
"description": "Human-readable Provider name shown in the active-lens dropdown, the topbar lens chip, and the per-node provider chip. Vendor lenses use a possessive `<Vendor>'s <product>` form (`\"Anthropic's Claude\"`, `\"OpenAI's Codex\"`, `\"Google's Antigravity\"`); the vendor-neutral open standard uses a `'Standard: <name>'` prefix (`'Standard: Agent skills'`). The non-gated `core/markdown` base keeps a `'Markdown'` label for internal lookups even though it is never a selectable lens."
|
|
23
23
|
},
|
|
24
24
|
"color": {
|
|
25
25
|
"type": "string",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"hideChip": {
|
|
72
72
|
"type": "boolean",
|
|
73
|
-
"description": "When `true`, the UI does NOT paint this Provider's chip on node cards. Reserved for the universal
|
|
73
|
+
"description": "When `true`, the UI does NOT paint this Provider's chip on node cards. Reserved for the universal base 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 markdown base is non-gated (`gatedByActiveLens: false`, `isLens: false`), so it is not a selectable lens and never appears in the active-lens dropdown or topbar lens chip either; `hideChip` additionally suppresses its per-card badge. Defaults to `false` (chip shown)."
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
},
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"type": "object",
|
|
98
98
|
"required": ["skillDir"],
|
|
99
99
|
"additionalProperties": false,
|
|
100
|
-
"description": "Authoring targets for verbs that MATERIALISE files into this Provider's on-disk territory (today only `sm tutorial`, which drops a skill folder where the Provider's runtime will discover it). Distinct from `detect` (which READS markers to suggest a lens) and from `classify` (which READS paths during a scan): `scaffold` is the WRITE side, the directory a generator drops new content into so the target runtime picks it up. Optional: a Provider with no `scaffold` block is never offered as a destination by a materialising verb (e.g. `
|
|
100
|
+
"description": "Authoring targets for verbs that MATERIALISE files into this Provider's on-disk territory (today only `sm tutorial`, which drops a skill folder where the Provider's runtime will discover it). Distinct from `detect` (which READS markers to suggest a lens) and from `classify` (which READS paths during a scan): `scaffold` is the WRITE side, the directory a generator drops new content into so the target runtime picks it up. Optional: a Provider with no `scaffold` block is never offered as a destination by a materialising verb (e.g. `codex` until Codex skills land, `antigravity` whose skills live under the open-standard `agent-skills` territory, `core/markdown` which owns no authoring convention). The skill-folder convention is uniform across hosts (`<skillDir>/<name>/SKILL.md`), so a single `skillDir` is enough today; a future verb that scaffolds agents or commands adds a sibling field (`agentDir`, `commandDir`) without breaking this one.",
|
|
101
101
|
"properties": {
|
|
102
102
|
"skillDir": {
|
|
103
103
|
"type": "string",
|
|
@@ -108,14 +108,14 @@
|
|
|
108
108
|
"aka": {
|
|
109
109
|
"type": "array",
|
|
110
110
|
"minItems": 1,
|
|
111
|
-
"description": "Display-only hints naming the agents that consume this Provider's scaffold territory, shown in parentheses next to the Provider label in the `sm tutorial` destination prompt (e.g. the open-standard `.agents/skills` is read by Antigravity and OpenAI Codex, so its `agent-skills` Provider lists them here). Purely presentational: these strings are NOT matched by `--for` (only registered Provider ids are) and have no runtime effect. Optional; absent means the prompt shows the bare Provider label.",
|
|
111
|
+
"description": "Display-only hints naming the agents that consume this Provider's scaffold territory, shown in parentheses next to the Provider label in the `sm tutorial` destination prompt (e.g. the open-standard `.agents/skills` is read by Google's Antigravity and OpenAI's Codex, so its `agent-skills` Provider lists them here). Purely presentational: these strings are NOT matched by `--for` (only registered Provider ids are) and have no runtime effect. Optional; absent means the prompt shows the bare Provider label.",
|
|
112
112
|
"items": { "type": "string", "minLength": 1 }
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
116
|
"gatedByActiveLens": {
|
|
117
117
|
"type": "boolean",
|
|
118
|
-
"description": "Lens gating flag
|
|
118
|
+
"description": "Lens gating flag. When `true`, this Provider is a **lens**: its `classify()` only runs (and the walker only iterates its territory) if `provider.id === activeProvider` (the project's active lens), and it is offered in the active-lens dropdown / `selectable` set (projected to the envelope entry as `isLens: true`). When `false` or omitted (default), the Provider is a universal **base**: it classifies unconditionally on every scan and is never offered as a selectable lens (`isLens: false`). Vendor providers (`claude`, `codex`, `antigravity`) and the open-standard `agent-skills` provider MUST set this `true`: the actual runtimes never read each other's on-disk formats (Claude Code does not consume `.codex/`; Codex CLI does not consume `.claude/`), and offering every file to every provider fabricates cross-vendor graph edges the runtimes themselves reject. Only the markdown fallback `core/markdown` (and any future format-based fallback) keeps this `false`, the single universal base beneath whatever lens is active, never offered as a selectable lens. The resolver always yields a concrete lens (a vendor id, or `agent-skills` as the open default when no vendor marker is present), so there is no unlensed branch. Affects classification ONLY; extractors continue to filter via their own `precondition.provider` allowlist."
|
|
119
119
|
},
|
|
120
120
|
"resolverRules": {
|
|
121
121
|
"type": "object",
|
|
@@ -133,15 +133,29 @@
|
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
135
|
"read": {
|
|
136
|
+
"description": "Declarative file-discovery config consumed by the kernel walker. Either a SINGLE rule (the common case) or an ARRAY of rules (a Provider that reads several file families, each with its own parser). For each rule the kernel walks every root, includes files whose extension matches the rule's `extensions`, parses each with the rule's `parser`, and yields raw nodes the orchestrator consumes; an array runs one walk pass per rule. When absent, the kernel applies the default `{ extensions: ['.md'], parser: 'frontmatter-yaml' }` so the most common Provider shape needs no configuration. When a Provider also declares the runtime `walk()` method (TypeScript-only, never appears in this manifest), `walk()` wins and `read` is ignored: the runtime field is the escape hatch for Providers with non-standard discovery requirements. The multi-rule array is the declarative path for a mixed-format Provider, so an escape-hatch `walk()` is not needed: the OpenAI Codex provider reads `.toml` sub-agents (parser `toml`, `bodyField: 'developer_instructions'`) AND `.md` open-standard skills (parser `frontmatter-yaml`) as two rules. Rules SHOULD declare disjoint `extensions`; if two rules (or a rule and another Provider) match the same path, the orchestrator's first-wins claim dedups it. Built-in parsers ship with the kernel (`frontmatter-yaml`, `plain`, `toml`); the set is closed by design and user plugins cannot register their own.",
|
|
137
|
+
"oneOf": [
|
|
138
|
+
{ "$ref": "#/$defs/providerReadRule" },
|
|
139
|
+
{
|
|
140
|
+
"type": "array",
|
|
141
|
+
"minItems": 1,
|
|
142
|
+
"description": "Multiple read rules, one walk pass each (see `#/$defs/providerReadRule`).",
|
|
143
|
+
"items": { "$ref": "#/$defs/providerReadRule" }
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"$defs": {
|
|
149
|
+
"providerReadRule": {
|
|
136
150
|
"type": "object",
|
|
137
151
|
"required": ["extensions", "parser"],
|
|
138
152
|
"additionalProperties": false,
|
|
139
|
-
"description": "
|
|
153
|
+
"description": "One declarative read rule: an extension set, the parser to apply to those files, and an optional `bodyField`. A Provider's `read` is either one of these or an array of them.",
|
|
140
154
|
"properties": {
|
|
141
155
|
"extensions": {
|
|
142
156
|
"type": "array",
|
|
143
157
|
"minItems": 1,
|
|
144
|
-
"description": "File extensions the walker yields. Strings include the leading dot. Lowercase-only by convention, match is case-sensitive and Providers MUST list every casing they want recognised.",
|
|
158
|
+
"description": "File extensions the walker yields for this rule. Strings include the leading dot. Lowercase-only by convention, match is case-sensitive and Providers MUST list every casing they want recognised.",
|
|
145
159
|
"items": {
|
|
146
160
|
"type": "string",
|
|
147
161
|
"pattern": "^\\.[a-z0-9]+$"
|
|
@@ -150,7 +164,12 @@
|
|
|
150
164
|
"parser": {
|
|
151
165
|
"type": "string",
|
|
152
166
|
"minLength": 1,
|
|
153
|
-
"description": "Identifier of a parser registered in the kernel-internal registry. Built-ins: `frontmatter-yaml` (markdown with `--- … ---` YAML frontmatter, prototype-pollution-safe, `js-yaml` JSON_SCHEMA-pinned), `plain` (entire body, empty frontmatter, for files carrying no frontmatter convention; the Provider derives `name` from the path inside `classify()`). Unknown ids surface as `UnknownParserError` from the walker; the orchestrator translates the error into a Provider issue with status `invalid-manifest`."
|
|
167
|
+
"description": "Identifier of a parser registered in the kernel-internal registry. Built-ins: `frontmatter-yaml` (markdown with `--- … ---` YAML frontmatter, prototype-pollution-safe, `js-yaml` JSON_SCHEMA-pinned), `plain` (entire body, empty frontmatter, for files carrying no frontmatter convention; the Provider derives `name` from the path inside `classify()`), `toml` (whole-file TOML parsed as structured frontmatter, e.g. Codex `.codex/agents/*.toml`). Unknown ids surface as `UnknownParserError` from the walker; the orchestrator translates the error into a Provider issue with status `invalid-manifest`."
|
|
168
|
+
},
|
|
169
|
+
"bodyField": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"minLength": 1,
|
|
172
|
+
"description": "Optional name of a parsed-frontmatter field that holds the node's markdown body. When set and `frontmatter[bodyField]` is a string, the kernel walker yields that string as the node `body` (so the body hash, byte counts, and every body-scoped extractor, markdown-link / at-directive / slash / backtick-path / external-url, see it) instead of the parser's own `body` output. Use it for formats whose prompt lives inside structured frontmatter rather than after a fence: OpenAI Codex sub-agents are pure TOML (`parser: 'toml'`) whose markdown prompt is the triple-quoted `developer_instructions` field, so the codex provider declares `bodyField: 'developer_instructions'`. The field remains in `frontmatter` as well, so frontmatter-scoped extractors are unaffected. When absent or not a string, the parser's own `body` is used unchanged (the default for `.md` providers)."
|
|
154
173
|
}
|
|
155
174
|
}
|
|
156
175
|
}
|
|
@@ -2,20 +2,19 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
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`
|
|
5
|
+
"description": "Universal frontmatter shape every Provider's per-kind schema extends via `allOf` + `$ref` to this `$id`. It DEFINES the two cross-vendor fields, `name` and `description`, but deliberately does NOT require them: whether either is mandatory is a per-kind decision. The kinds whose vendor mandates them add `required` on their own extension (Claude agent, OpenAI Codex agent, the Agent Skills skill); the generic `markdown` fallback and Claude skill/command leave them optional, because no normative Markdown standard mandates frontmatter fields and Claude's merged skill/command contract treats `name` (defaults to the directory/file name) and `description` (defaults to the first paragraph) as optional. `description` is the field every format carries and `name` is universal among formats with explicit identifiers, so both live here as shared definitions with `minLength: 1` (a present value cannot be empty). 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.) `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
|
-
"required": ["name", "description"],
|
|
8
7
|
"additionalProperties": true,
|
|
9
8
|
"properties": {
|
|
10
9
|
"name": {
|
|
11
10
|
"type": "string",
|
|
12
11
|
"minLength": 1,
|
|
13
|
-
"description": "Short human-readable identifier.
|
|
12
|
+
"description": "Short human-readable identifier. When present it must be non-empty; whether it is required is decided per kind (see each per-kind schema's `required`)."
|
|
14
13
|
},
|
|
15
14
|
"description": {
|
|
16
15
|
"type": "string",
|
|
17
16
|
"minLength": 1,
|
|
18
|
-
"description": "One-to-three-sentence description.
|
|
17
|
+
"description": "One-to-three-sentence description. When present it must be non-empty; whether it is required is decided per kind (see each per-kind schema's `required`)."
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
20
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"activeProvider": {
|
|
20
20
|
"type": "string",
|
|
21
|
-
"description": "The active provider lens for this project. Exactly one provider id (from the registered providers) sees the project at any time. All extractors, classifiers, and resolution rules belonging to other providers are skipped during scan. Changing this triggers an atomic drop of the `scan_*` DB zone followed by a fresh scan under the new lens; `state_*` and `config_*` zones survive the switch. When absent on a fresh project, the kernel auto-detects from filesystem markers (presence of `.claude/`, `.codex/`,
|
|
21
|
+
"description": "The active provider lens for this project. Exactly one provider id (from the registered providers) sees the project at any time. All extractors, classifiers, and resolution rules belonging to other providers are skipped during scan. Changing this triggers an atomic drop of the `scan_*` DB zone followed by a fresh scan under the new lens; `state_*` and `config_*` zones survive the switch. When absent on a fresh project, the kernel auto-detects from filesystem markers (presence of `.claude/`, `.codex/`, `.agents/`, `.cursor/`, etc.) and prompts via the CLI / UI when several markers match; when no vendor marker is present the lens resolves to the open-standard `agent-skills` view (the universal default lens) without persisting it, so a vendor marker added later still auto-detects. The field stays optional on disk (a fresh project may omit it); the runtime always resolves it to a concrete lens, never null. Google's Antigravity CLI has no vendor-specific marker and is selected manually. Stability: experimental."
|
|
22
22
|
},
|
|
23
23
|
"activeProviderMarkers": {
|
|
24
24
|
"type": "array",
|
package/telemetry.md
CHANGED
|
@@ -206,7 +206,7 @@ Rules:
|
|
|
206
206
|
scan do not), so the signal is "which extractors this project exercises",
|
|
207
207
|
aggregated across runs.
|
|
208
208
|
- **Third-party ids collapse.** Any extension id whose plugin is not a
|
|
209
|
-
built-in (`claude`, `antigravity`, `
|
|
209
|
+
built-in (`claude`, `antigravity`, `codex`, `agent-skills`, `core`) MUST be
|
|
210
210
|
replaced with `external_plugin` before the event leaves the machine.
|
|
211
211
|
- **No node paths, titles, or content** in any UI event; the view / feature is
|
|
212
212
|
the event name, from a closed set, and nothing else is attached.
|