@skill-map/spec 0.55.0 → 0.56.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 +34 -0
- package/architecture.md +3 -1
- package/cli-contract.md +6 -1
- package/db-schema.md +3 -0
- package/index.json +10 -10
- package/package.json +1 -1
- package/plugin-author-guide.md +1 -1
- package/schemas/api/rest-envelope.schema.json +4 -0
- package/schemas/extensions/provider.schema.json +4 -0
- package/schemas/project-config.schema.json +6 -1
- package/schemas/scan-result.schema.json +9 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.56.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Splits the scan cap into two knobs: `scan.maxScan` (corpus ceiling, default 50000) bounds what the walk parses and reference-validates, while `scan.maxNodes` (default 256) now caps only the graph render. References resolve across the whole corpus, so large repos no longer flag links to unrendered files as broken. Adds the `--max-scan` flag and the `/api/folders`, `/api/branch`, and `/api/scan?meta=1` endpoints that back the lazy folders tree and branch-scoped map.
|
|
8
|
+
|
|
9
|
+
## User-facing
|
|
10
|
+
|
|
11
|
+
Large repos now scan and validate references across the whole tree; check folders (with per-folder issue counts) to choose what the map shows. Map palettes count what is shown; a Reset filters button clears it all; the refresh button spins while any scan runs.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Restores the files rail's per-row stale-clock icon, dropped when the rail switched to building from the lightweight `GET /api/folders` payload (which carried the error / warn counts but not the sidecar drift status). The endpoint now emits a `sidecarStatus` field (the persisted `scan_nodes.sidecar_status`, `null` when there is no parseable sidecar), threaded from the kernel loader through the BFF into the rail so staleness flags corpus-wide in demo and `sm serve` mode.
|
|
16
|
+
|
|
17
|
+
## User-facing
|
|
18
|
+
|
|
19
|
+
The files rail again flags out-of-date nodes with the clock icon, so you can see at a glance which files have drifted since their last review.
|
|
20
|
+
|
|
21
|
+
- Body extractors now strip raw HTML (comments and tag tokens) before matching, alongside the existing code-region strip. A markdown link commented out as `<!-- [x](old.md) -->` or hidden in an attribute value (`<img alt="[x](y.md)">`) no longer produces a phantom edge. The strip is bounded to comments and tag tokens, so markdown nested inside a `<div>` block still resolves; `core/backtick-path` is unaffected (HTML is not a code region).
|
|
22
|
+
|
|
23
|
+
## User-facing
|
|
24
|
+
|
|
25
|
+
Scanning `.md` files that contain HTML no longer creates phantom links or false broken-reference warnings from links that were commented out or tucked inside HTML attributes.
|
|
26
|
+
|
|
27
|
+
## 0.55.1
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Add a `comingSoon` flag to a Provider's `presentation` (spec + kernel). A coming-soon Provider ships in the registry (node chips still render) but is never selectable as the active lens: auto-detect skips its markers, the BFF drops it from `GET /api/active-provider`'s `selectable` set, and the UI greys it with a `(coming soon)` suffix. `openai`, `antigravity`, and `agent-skills` are marked coming-soon, so only `claude` is selectable today.
|
|
32
|
+
|
|
33
|
+
## User-facing
|
|
34
|
+
|
|
35
|
+
Only the Claude provider is selectable for now. Codex, Antigravity and Open Skills appear greyed out as "coming soon" in the provider lens, and projects auto-detect Claude without a lens prompt.
|
|
36
|
+
|
|
3
37
|
## 0.55.0
|
|
4
38
|
|
|
5
39
|
### Minor Changes
|
package/architecture.md
CHANGED
|
@@ -72,6 +72,8 @@ A skill-map project sees its filesystem through exactly one **active provider le
|
|
|
72
72
|
|
|
73
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 or empty, the CLI and UI prompt the user to pick one enabled provider. **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/`, `openai` → `.codex/` or root `AGENTS.md`, `agent-skills` → `.agents/`. 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 auto-detects as the universal `agent-skills` lens and the `antigravity` lens is set manually via `sm config set activeProvider antigravity`.
|
|
74
74
|
|
|
75
|
+
**Coming-soon Providers are not selectable.** A Provider may set `presentation.comingSoon: true` (see [`provider.schema.json`](./schemas/extensions/provider.schema.json#/properties/presentation/properties/comingSoon)) to declare it is registered but not yet ready for end users. Such a Provider is skipped by the auto-detect heuristic (its markers never produce a candidate, so a project that carries only coming-soon markers alongside an enabled one resolves to the enabled one with no ambiguous prompt) and is dropped from the `selectable` set served by `GET /api/active-provider`, so neither the CLI nor the UI ever offers it as the lens. It still ships in `providerRegistry` (node chips render) and the UI greys it with a `(coming soon)` suffix. Today `openai`, `antigravity`, and `agent-skills` are coming-soon; only `claude` is selectable. This is a product-availability statement, distinct from the reversible operator toggle `plugins[<id>].enabled = false`.
|
|
76
|
+
|
|
75
77
|
### Consequence: one graph per project at a time
|
|
76
78
|
|
|
77
79
|
The persisted scan graph (`scan_*` zone) reflects the project as the active lens sees it; no cross-provider merging at storage time. A repo with both `.claude/` and `.codex/` does NOT show "everyone's nodes at once"; it shows the active lens's view.
|
|
@@ -422,7 +424,7 @@ The Signal's `range` field (byte offsets in the source) powers two cross-extract
|
|
|
422
424
|
|
|
423
425
|
### Extractor · code-region file references (`core/backtick-path`)
|
|
424
426
|
|
|
425
|
-
Every body extractor strips fenced code blocks
|
|
427
|
+
Every body extractor strips fenced code blocks, inline code spans, and raw HTML (comments and tag tokens) before matching (the strip policy): invocation tokens (`@handle`, `/command`, URLs) inside backticks, and any reference inside HTML, are literal payload the runtime never follows. The HTML half closes two authoring papercuts: a markdown link commented out as `<!-- [x](old.md) -->` no longer emits a phantom edge, and a `[x](y.md)`-shaped token hiding in an attribute value (`<img alt="[see](ref.md)">`) no longer false-matches. No supported runtime renders raw HTML in a `.md` body to follow `<a href>` or load `<img src>`; the file reaches the LLM as text, so an HTML reference is at most LLM-interpreted, the same tier as a backticked `@handle`. The HTML strip is deliberately bounded to comments and tag tokens, never the content between an open and close tag, so markdown nested inside a `<div>` block survives; replicating CommonMark's full HTML-block algorithm is out of scope. **Relative file paths are the documented exception.** The Agent Skills open standard mandates that a skill references its bundled resources by relative path and that "agents load these on demand"; prose like ``Read `references/rules.md` `` is an instruction the consuming LLM runtime follows. The `core/backtick-path` extractor surfaces exactly that class of references, ONLY inside code regions, the precise complement of the code-strip policy, so it can never collide with the prose-side extractors. The HTML strip is a separate, prose-side transform and never feeds the code-region inverse mask: HTML is not a code region, so a path inside an HTML tag is not a `points` target.
|
|
426
428
|
|
|
427
429
|
The contract:
|
|
428
430
|
|
package/cli-contract.md
CHANGED
|
@@ -312,7 +312,9 @@ The three privacy-sensitive keys above PLUS `allowEditSmFiles` are members of `P
|
|
|
312
312
|
|
|
313
313
|
The watcher subscribes to the same roots `sm scan` walks and respects `.skillmapignore` plus `config.ignore` exactly as the one-shot scan does. Filesystem events are grouped using `scan.watch.debounceMs` (default 300ms) before the watcher re-runs the incremental scan and persists. `SIGINT` / `SIGTERM` close the watcher cleanly. Exit code on clean shutdown is 0.
|
|
314
314
|
|
|
315
|
-
**
|
|
315
|
+
**Scan ceiling** (`--max-scan <N>`): on `sm scan`, `sm watch` (alias `sm scan --watch`), and `sm serve`, a hard ceiling on the number of files the walker accepts after `.skillmapignore` filtering, before extractors run. Default from `scan.maxScan` (default 50000). The scan walks, parses, analyzes, and reference-validates every file up to this ceiling, so link resolution sees the whole corpus (a large monorepo) and references resolve across it regardless of how many nodes the map renders. The flag fully overrides the setting and is **bidirectional** (raise or lower). At the ceiling, additional files are dropped in stable provider-walker order and `scan_meta.scan_truncated` is set; the `ScanResult` envelope carries `scanCeiling` and `scanTruncated` so the UI raises a persistent banner pointing at the `.skillmapignore` editor in Settings → Project. The CLI prints a human-mode notice naming both escapes: edit `.skillmapignore` (preferred, trims permanently) or re-run with `--max-scan <N>` (force). `sm refresh` operates on a single already-classified node, so the ceiling does not apply there. Validation: integer ≥ 1, else exits `2` operational.
|
|
316
|
+
|
|
317
|
+
**Map render cap** (`--max-nodes <N>`): the maximum number of nodes the graph map renders onto the canvas at once. Default from `scan.maxNodes` (default 256). This does NOT bound the scan: the full corpus up to `scan.maxScan` is walked and reference-validated, and the folders tree shows all of it; the cap only bounds the Foblex graph projection so a large project stays readable. The effective value is recorded in `scan_meta.max_render_nodes` and carried on `ScanResult.maxRenderNodes`; the UI projects the selected folder branch capped at this number and raises an in-view banner (distinct from the scan-ceiling banner) when a branch exceeds it. The flag fully overrides the setting (bidirectional) and is honoured on `sm scan` / `sm watch` / `sm serve`; on headless `sm scan` it only records the value (nothing renders). Validation: integer ≥ 1, else exits `2` operational.
|
|
316
318
|
|
|
317
319
|
**File-size skip** (`scan.maxFileSizeBytes`, default 1 MiB): the walker checks each candidate file's on-disk size before reading and skips any larger than the limit. The skip happens at the source (never read, parsed, or indexed), so an accidental binary or generated artefact cannot poison the graph. Every skipped file is reported in the `ScanResult` envelope as `oversizedFiles` (each entry the root-relative, forward-slash path plus byte size) and counted in `stats.filesOversized`. When at least one file is skipped, `sm scan`, `sm watch` (per batch), and `sm serve` (initial scan and every batch) print a **WARN** terminal notice listing the skipped files with a human-readable size, plus a hint pointing at `scan.maxFileSizeBytes` and `.skillmapignore`; the UI raises a matching banner. Unlike the node cap, the limit is config-only (no per-invocation flag).
|
|
318
320
|
|
|
@@ -593,12 +595,15 @@ The reference implementation ships a Hono BFF rooted at `src/server/`. One Node
|
|
|
593
595
|
|---|---|---|
|
|
594
596
|
| `GET /api/health` | implemented | `{ ok: true, schemaVersion, specVersion, implVersion, db: 'present'\|'missing', cwd: string, dbPath: string }`. `cwd` is the absolute project root the BFF resolves against (`runtimeContext.cwd`); `dbPath` is the absolute project DB path (`IServerOptions.dbPath`). Both surfaced so the SPA's About panel can show "you are looking at <project>" + the DB location without a second endpoint. |
|
|
595
597
|
| `GET /api/scan` | implemented | latest persisted `ScanResult` (1:1 with `scan-result.schema.json`; byte-equal to `sm scan --json` modulo whitespace). DB absent → empty `ScanResult` shape (zero `nodes` / `links` / `issues`). |
|
|
598
|
+
| `GET /api/scan?meta=1` | implemented | metadata-only `ScanResult`: every scalar field plus `stats` (real `COUNT(*)`-derived counts) and `scanCeiling` / `scanTruncated` / `maxRenderNodes` / `tokenizer` / `oversizedFiles`, but `nodes` / `links` / `issues` are empty arrays. Reads only `scan_meta` + counts (never selects the node/link/issue rows), so the SPA hydrates its header and banners at boot without the full-corpus payload (paired with `/api/folders` for the tree and `/api/branch` for the map). DB absent → empty `ScanResult` shape. |
|
|
596
599
|
| `GET /api/scan?fresh=1` | implemented | runs an in-memory scan and returns the produced `ScanResult` without persistence. Rejects with `bad-query` (400) when the server was started with `--no-built-ins` or `--no-plugins` (would yield empty / partial results). |
|
|
597
600
|
| `POST /api/scan` | implemented | Run a fresh scan **and persist it** through the same `runScanWithRenames` + `persistScanResult` pipeline the watcher uses. Body empty (`{}` or none). Response: the persisted `ScanResult` inline (same shape as `GET /api/scan`). Side effects: broadcasts `scan.started` then `scan.completed` over `/ws` so other clients refresh; the per-batch sequence is identical to a watcher-driven batch. **Concurrency**: only one scan may run at a time across the whole BFF process. A POST arriving while a watcher batch (or another POST) is in flight is rejected with `409 scan-busy` so the caller can retry. **Pipeline gate**: rejected with `400 bad-query` when the server started with `--no-built-ins` or `--no-plugins` (a partial pipeline would persist a misleading DB the next watcher boot must reconcile). **DB gate**: rejected with `500 db-missing` when the project DB is absent; the read-side `/api/scan` degrades to the empty shape, but a write path cannot, so it fails fast. |
|
|
598
601
|
| `GET /api/nodes?kind=&hasIssues=&path=&limit=&offset=` | implemented | `RestEnvelope` (`kind: 'nodes'`), paginated, filtered list. Filters share the `kind=` / `has=issues` / `path=<glob>` grammar with `sm export`. `hasIssues=false` is a server-side post-filter (not representable in the kernel grammar). Pagination defaults `offset=0`, `limit=100`; max `limit=1000`. |
|
|
599
602
|
| `GET /api/nodes/:pathB64[?include=body]` | implemented | Single-node detail envelope: `{ schemaVersion, kind: 'node', item: Node, links: { incoming: Link[], outgoing: Link[] }, issues: Issue[] }`. `:pathB64` is base64url (RFC 4648 §5, no padding) of `node.path`. Missing node or malformed `pathB64` → 404 `not-found`. **`?include=body`** (Step 14.5.a), opt-in flag adding `item.body: string \| null` to the response. The body is read from disk on demand at request time (the kernel persists `bodyHash` only). `null` means the source file was missing / unreadable when the request landed (the watcher re-emits `scan.completed` on catch-up). Without the flag, `item.body` is `undefined` and the handler does not touch the filesystem. |
|
|
600
603
|
| `GET /api/links?kind=&from=&to=` | implemented | `RestEnvelope` (`kind: 'links'`), list of links. Filters: `kind` (CSV whitelist of `link.kind`), `from` (exact match on `link.source`), `to` (exact match on `link.target`). No pagination at v14.2. |
|
|
601
604
|
| `GET /api/issues?severity=&analyzerId=&node=` | implemented | `RestEnvelope` (`kind: 'issues'`), list of issues. Filters: `severity` (CSV from `error\|warn\|info`), `analyzerId` (CSV; qualified or short suffix per `sm check --analyzers`), `node` (filter to issues whose `nodeIds` includes the path). No pagination at v14.2. |
|
|
605
|
+
| `GET /api/folders` | implemented | `RestEnvelope` (`kind: 'folders'`), lightweight full-corpus projection: one entry per scanned node `{ path, kind, linksInCount, linksOutCount, tokensTotal, modifiedAtMs, errorCount, warnCount, sidecarStatus }`. Only cheap scalar columns of `scan_nodes` (no frontmatter, body, links, signals, or contributions), so the SPA folders tree renders the whole corpus (up to `scan.maxScan`) with per-node data columns + per-folder issue badges without hydrating the full `ScanResult`. `tokensTotal` / `modifiedAtMs` are nullable (tokenization disabled / unknown mtime). `errorCount` / `warnCount` are the count of error / warn issues whose `nodeIds` include that path (the same incidence the tree rolls up across descendants). `sidecarStatus` is the node's sidecar drift status (`scan_nodes.sidecar_status`), `null` when there is no parseable sidecar, so the folders tree can flag per-row staleness without hydrating the branch payload. No pagination (the complete tree is the point; the corpus is already bounded by `scan.maxScan`). DB absent → zero items. |
|
|
606
|
+
| `GET /api/branch?path=<prefix>&path=<prefix>&limit=<n>` | implemented | Branch projection for the map. `path` is **repeatable**: the response is the UNION of the subtrees under every given prefix (forward-slash; a node matches a prefix when its path equals it or starts with `<prefix>/`). No `path` (or a single empty one) = the whole corpus. The union is capped at `limit` nodes (default and effective max = the scan's `maxRenderNodes`), so the response stays bounded regardless of how many prefixes are sent. Direct shape (no envelope wrap, like `/api/scan`): `{ schemaVersion, kind: 'branch', branch: { paths, total, rendered, truncated, cap }, nodes: Node[], links: Link[], issues: Issue[] }`, where `paths` echoes the requested prefixes. `nodes` is the first `rendered` nodes of the union in stable path order; `links` carries only edges whose source AND target are in `nodes`; `issues` carries those touching `nodes`. `truncated` is `total > cap`. Lets the SPA render a multi-folder selection without hydrating the full `ScanResult`. DB absent → empty branch (zero nodes). Validation: `limit` integer ≥ 1 else 400 `bad-query`. |
|
|
602
607
|
| `GET /api/graph?format=ascii\|json\|md` | implemented | formatter-rendered graph. `Content-Type` per format: `text/plain` (ascii), `application/json` (json), `text/markdown` (md / mermaid). Default `format=ascii`. Unknown format → 400 `bad-query`. |
|
|
603
608
|
| `GET /api/config` | implemented | `RestEnvelope` (`kind: 'config'`), merged effective config (defaults → user → user-local → project → project-local → override). |
|
|
604
609
|
| `GET /api/plugins` | implemented | `RestEnvelope` (`kind: 'plugins'`), list of installed plugins (built-in + drop-in) with status. Item shape: `{ id, version, kinds, status, reason, source: 'built-in'\|'project', description?: string, locked?: boolean, startsAsDisabled?: boolean, extensions?: Array<{ id, kind, version, enabled, description?: string, stability?: 'experimental'\|'beta'\|'stable'\|'deprecated', locked?: boolean }> }`. The plugin row has no granular toggle axis; its `status` aggregates the children (`'enabled'` when at least one extension is enabled, else `'disabled'`). The `description` carries the manifest-declared description (built-ins: hardcoded on `IBuiltInPlugin`; drop-ins: `plugin.json#/description`); each `extensions[]` entry carries its manifest's `description` per `IExtensionBase` (`extensions/base.schema.json#/properties/description`), plus the optional `stability` lifecycle label per `extensions/base.schema.json#/properties/stability` (omitted when undeclared; missing means `stable`. The SPA badges only non-default values, `experimental` / `beta` / `deprecated`, next to the extension row; `stable` renders nothing. Presentation-only EXCEPT `experimental` and `deprecated`, which each flip the extension's installed default to disabled). The SPA's Settings list renders descriptions as muted secondary text and indexes them for substring search alongside the ids. The `extensions` array is present whenever the plugin declares any extension AND loaded successfully. Each entry's `enabled` reflects the per-extension override resolution (DB > settings.json > installed default, where the default is `false` for `experimental` and `deprecated` extensions and `true` otherwise). The optional `locked: true` flag is stamped when the plugin id (or qualified extension id) appears in the host's lock-list (`src/server/locked-plugins.ts`); locked items render the toggle disabled in the SPA and any `PATCH` returns `403 locked`. Omitted when false. The optional `startsAsDisabled: true` flag is stamped on drop-in plugins (never built-ins) whose discovery-time `status` was `'disabled'`, i.e. every extension was disabled in `config_plugins` / `settings.json` at `sm serve` boot, so the handlers were never bucketed into the runtime. The SPA renders a per-row hint when this flag is set AND the user re-enables at least one of the plugin's extensions in the buffered state, since re-enabling requires `sm serve` restart (the rest of the toggle pipeline applies live). Omitted when false. |
|
package/db-schema.md
CHANGED
|
@@ -153,6 +153,9 @@ Single-row table holding the last persisted scan's metadata. Lets `loadScanResul
|
|
|
153
153
|
| `stats_files_walked` | INTEGER | NOT NULL |
|
|
154
154
|
| `stats_files_skipped` | INTEGER | NOT NULL |
|
|
155
155
|
| `stats_duration_ms` | INTEGER | NOT NULL |
|
|
156
|
+
| `scan_ceiling` | INTEGER | NOT NULL | Effective walk ceiling for this scan (`--max-scan` override, else `scan.maxScan`, default 50000). The scan walks + reference-validates the full corpus up to this number. Carried on `ScanResult.scanCeiling`. |
|
|
157
|
+
| `scan_truncated` | INTEGER | NOT NULL | 1 when the walker reached `scan_ceiling` and dropped files in stable provider-walker order, 0 otherwise. Drives the UI "scan truncated" banner pointing at the `.skillmapignore` editor. Carried on `ScanResult.scanTruncated`. |
|
|
158
|
+
| `max_render_nodes` | INTEGER | NOT NULL | Effective map render cap (`--max-nodes` override, else `scan.maxNodes`, default 256). The full corpus is persisted regardless; this bounds only the graph projection (the folders tree shows everything). Carried on `ScanResult.maxRenderNodes`. |
|
|
156
159
|
| `tokenizer` | TEXT | NULL | Resolved offline encoder that produced this scan's per-node token counts (closed enum `cl100k_base` / `o200k_base`, see `project-config.md` / `project-config.schema.json` §tokenizer). Carried on the `ScanResult.tokenizer` wire field. NULL on a pre-feature DB or a scan with tokenization disabled. On `sm scan --changed` the orchestrator compares this against the freshly-resolved encoder and, when they differ (or the stored value is NULL), bypasses cached per-node token reuse so `buildNode` recomputes counts with the current encoder; changing the tokenizer thus invalidates prior counts. |
|
|
157
160
|
| `schema_fingerprint` | TEXT | NULL | sha256 (hex) of the migration DDL the schema was built from, written at persist time. NULL on a DB created by a pre-fingerprint CLI; a NULL (or mismatching) value is read as schema drift (see §Schema drift). Internal DB metadata, NOT carried on the `ScanResult` wire shape. |
|
|
158
161
|
|
package/index.json
CHANGED
|
@@ -174,14 +174,14 @@
|
|
|
174
174
|
}
|
|
175
175
|
]
|
|
176
176
|
},
|
|
177
|
-
"specPackageVersion": "0.
|
|
177
|
+
"specPackageVersion": "0.56.0",
|
|
178
178
|
"integrity": {
|
|
179
179
|
"algorithm": "sha256",
|
|
180
180
|
"files": {
|
|
181
|
-
"CHANGELOG.md": "
|
|
181
|
+
"CHANGELOG.md": "49c8267a556ee260ce198a53abacf22a5eca6798417ae5ee4776d39785d83899",
|
|
182
182
|
"README.md": "a790cd010b46d47883d1f37e3893cea9d7aa69ec4750c0202e6a0c99991e7980",
|
|
183
|
-
"architecture.md": "
|
|
184
|
-
"cli-contract.md": "
|
|
183
|
+
"architecture.md": "5eb1b54e66b3db3d28091830a9a990e94dc72b4488f94ab9506cd509aa884e8b",
|
|
184
|
+
"cli-contract.md": "3733b1277c92d2af9e0728b0bcdd0579dd13bf0c3adfd7f272a18efd3a8c8257",
|
|
185
185
|
"conformance/README.md": "dcbef7249f161acf597552a05dcadc813cd0ced430dcd3f813fcf5e1c876335d",
|
|
186
186
|
"conformance/cases/backtick-path-extraction.json": "4620e7f8bc161fc57cb44001e9d99879c7e22b4865a0c27a20dc28969cd936d9",
|
|
187
187
|
"conformance/cases/extractor-collision-detection.json": "179a02c61892f0d26492de0c4e2c327fa6b4986d1265a8f119e871df6afe4658",
|
|
@@ -229,16 +229,16 @@
|
|
|
229
229
|
"conformance/fixtures/view-contribution-payloads/notes/example.md": "312b1919cd7fd0f233648b053acfb2975662ede3c65dd391cc508204b67ad6fb",
|
|
230
230
|
"conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/analyzers/everything/index.js": "ea0022fec7f0fd5a26ba12db1310335f434f2f820682206a3a9542d98db0d346",
|
|
231
231
|
"conformance/fixtures/view-slots-all/.skill-map/plugins/all-slots/plugin.json": "c48e8a0574947ade0b4eb189d6bc27a48e24f92f616aacdc177f2d22d472a599",
|
|
232
|
-
"db-schema.md": "
|
|
232
|
+
"db-schema.md": "b27e57da2e6b10cf0aa15d841703654ce4e2b91cb3ed9ddbbe6c158205dbef7b",
|
|
233
233
|
"interfaces/security-scanner.md": "0996dd782e2d39d4791f2e290da4bb1a68a5b30c1f79187977188ec8e3fe6ef2",
|
|
234
234
|
"job-events.md": "2c7017f5f0003b19653424111a07043487173cbe88b51e961598bb1693987059",
|
|
235
235
|
"job-lifecycle.md": "ce33bc8bb5090ea183f860e495bfccc2a4a0ac2e23f6ebad83b9c28aad59124e",
|
|
236
|
-
"plugin-author-guide.md": "
|
|
236
|
+
"plugin-author-guide.md": "45c7da2293372c9781117f0ed1a2c1e1eb9a0da89f32fa7ed64e70d6379ed20a",
|
|
237
237
|
"plugin-kv-api.md": "5e095581020043af73ff028e272f56d42ca9eb6e506dd777d45703f9db796a5b",
|
|
238
238
|
"plugin-quickstart.md": "19092b278d80df357ea623dc3bd9f833d059582ee1356f317621913d91e50512",
|
|
239
239
|
"prompt-preamble.md": "5d0f836688aa23eafc32104c3174132340b268361f6060326eec84da17c6ad6d",
|
|
240
240
|
"schemas/annotations.schema.json": "09fcebc86e3b793bf9f03a35b38e5ca2a08d79ac3504f6f03895ac2ae1c2aded",
|
|
241
|
-
"schemas/api/rest-envelope.schema.json": "
|
|
241
|
+
"schemas/api/rest-envelope.schema.json": "216a9cecf952822ca48fa5f28cec5c20e706f769f42a7beaf8ef70259aca44f3",
|
|
242
242
|
"schemas/bump-report.schema.json": "c763e1f89f2665c479d6a4985c1d324c65e5278331ebab82220287a07e4c4429",
|
|
243
243
|
"schemas/conformance-case.schema.json": "958b316d646d0c64a715a7a28cee66d2c2d2498a60dbfc5ae8970687c2a96954",
|
|
244
244
|
"schemas/conformance-result.schema.json": "14f983a8f4e62cd4ff964688c9b2b026a3bee3a0b762b64091c8c34db5b75777",
|
|
@@ -250,7 +250,7 @@
|
|
|
250
250
|
"schemas/extensions/formatter.schema.json": "880dc379ad545a62404403533a01eda5171edba0390561fc46ec6e986e0b9bd3",
|
|
251
251
|
"schemas/extensions/hook.schema.json": "f56aef59e9986ffdf7d86aa2e048dccccf217000a358b8c64737cbd911c48dad",
|
|
252
252
|
"schemas/extensions/provider-kind.schema.json": "499b2418bbe6d8a84a1608e26c56b52c2652a30ce314bc2989094418797dc1e6",
|
|
253
|
-
"schemas/extensions/provider.schema.json": "
|
|
253
|
+
"schemas/extensions/provider.schema.json": "46e34c66beff2d6b8eefa8ee9d215324131a802a8fcf083f78f0ad326e817a21",
|
|
254
254
|
"schemas/frontmatter/base.schema.json": "cff81510ed94824dfd12ab8b30ce9fbac65e42d61ae0edf3fbb6bbb6bb8bcb8c",
|
|
255
255
|
"schemas/history-stats.schema.json": "436aa0ffe744bdb699000447e86b45724fbd2cc4642781074eb1527522b9058c",
|
|
256
256
|
"schemas/input-types.schema.json": "93b27a1cbd1f131d42730eb9a89cf3af6889e9f17b20a48ce36133885503e01b",
|
|
@@ -260,11 +260,11 @@
|
|
|
260
260
|
"schemas/node.schema.json": "1ebba38e0c0ae022fccbc0cdf7c298da1720a68d4cb375f0baf9f0847998a0d8",
|
|
261
261
|
"schemas/plugins-doctor.schema.json": "03e2dc51c052a09bf0198c80e2c26e6129734ada4a748e483245de3dd8576c42",
|
|
262
262
|
"schemas/plugins-registry.schema.json": "211d081691fc83526e1593c79ed9741ad8a5dbd4db1a756f72141b0cced2ea15",
|
|
263
|
-
"schemas/project-config.schema.json": "
|
|
263
|
+
"schemas/project-config.schema.json": "3724ed08ff06a8debe1f8f8a07f0d5f42e7f78ba7806a5c44844d4416f1b34df",
|
|
264
264
|
"schemas/refresh-report.schema.json": "47184d4f6b15e9b7671dc178b3b3886a64422da198898508ecdb2cb27876db04",
|
|
265
265
|
"schemas/report-base-deterministic.schema.json": "59785fe6f3ceb34814bbbd03d10fa7336a32835ce598946f2923d469b32aa32a",
|
|
266
266
|
"schemas/report-base.schema.json": "e4d25f055e24f18ae0f77c24661c1bddc87ff2e43b001b6a827fcb14f9753f44",
|
|
267
|
-
"schemas/scan-result.schema.json": "
|
|
267
|
+
"schemas/scan-result.schema.json": "236c7022fbbe19ffc1d8ad9bfab1159c427adba435da22ffda884b6c29420156",
|
|
268
268
|
"schemas/sidecar.schema.json": "f9d914e61b2d04495b84dc90e55240aca959e6f16137e5bfa4c0e10ada33ecbe",
|
|
269
269
|
"schemas/signal.schema.json": "c677f04964dcc15e00368c5cc4b0569fb4cf21889d34fa3c29dc21a5cb6b919c",
|
|
270
270
|
"schemas/summaries/agent.schema.json": "5b26b95fb082b73d302c8aa6489ab09488a155ccfbb8943dfc47079509d35122",
|
package/package.json
CHANGED
package/plugin-author-guide.md
CHANGED
|
@@ -227,7 +227,7 @@ Pure single-node analysis. **Never** read another node, the graph, or the databa
|
|
|
227
227
|
|
|
228
228
|
You can read `ctx.node.sidecar.*` freely: the per-`(node, extractor)` cache hashes the sidecar `annotations` block alongside the body, so a `.sm`-only edit invalidates the cached run automatically.
|
|
229
229
|
|
|
230
|
-
> **Pick a syntax that doesn't collide with built-ins.** `core/at-directive` claims `@`, `core/slash-command` claims `/`, both with LLM-aligned semantics (and both strip fenced code blocks
|
|
230
|
+
> **Pick a syntax that doesn't collide with built-ins.** `core/at-directive` claims `@`, `core/slash-command` claims `/`, both with LLM-aligned semantics (and both strip fenced code blocks, inline backticks, and raw HTML before matching). `core/backtick-path` is the deliberate inverse: it matches relative `.md` paths ONLY inside those stripped code regions, so it cannot overlap the prose-side extractors. A new extractor matching one of those prefixes fires on the same input and emits a competing link; when both resolve to the same node that surfaces as `reference-redundant` (`name-collision` is reserved for two nodes declaring the same resolvable `name`, not for overlapping invocation forms). The example below uses a wikilink-style `[[ref:<name>]]` pattern to side-step the overlap. See [`architecture.md` §Extractor · trigger normalization](./architecture.md#extractor--trigger-normalization) for the normalization pipeline.
|
|
231
231
|
|
|
232
232
|
```javascript
|
|
233
233
|
export default {
|
|
@@ -174,6 +174,10 @@
|
|
|
174
174
|
"hideChip": {
|
|
175
175
|
"type": "boolean",
|
|
176
176
|
"description": "When `true`, the UI suppresses this Provider's per-card chip (reserved for the universal `markdown` fallback). The Provider still appears in the lens dropdown and the topbar lens chip."
|
|
177
|
+
},
|
|
178
|
+
"comingSoon": {
|
|
179
|
+
"type": "boolean",
|
|
180
|
+
"description": "When `true`, this Provider is published in the registry but not yet selectable as the active lens (dropped from `GET /api/active-provider`'s `selectable` set and skipped by auto-detect). The UI renders it greyed with a `(coming soon)` suffix. Mirror of `extensions/provider.schema.json#/properties/presentation/properties/comingSoon`."
|
|
177
181
|
}
|
|
178
182
|
}
|
|
179
183
|
}
|
|
@@ -71,6 +71,10 @@
|
|
|
71
71
|
"hideChip": {
|
|
72
72
|
"type": "boolean",
|
|
73
73
|
"description": "When `true`, the UI does NOT paint this Provider's chip on node cards. Reserved for the universal fallback Provider (`markdown`): the majority of nodes in any project carry it, so badging every generic `.md` would be visual noise and dilute the chip's purpose (signalling when a node came from a NON-default platform). The Provider still appears in the active-lens dropdown and the topbar lens chip; only the per-card badge is suppressed. Defaults to `false` (chip shown)."
|
|
74
|
+
},
|
|
75
|
+
"comingSoon": {
|
|
76
|
+
"type": "boolean",
|
|
77
|
+
"description": "When `true`, this Provider is not yet selectable as the active lens: it is published in the registry (so node chips still render and the UI can show it) but it can never be picked. The auto-detect heuristic skips it (`detectProvidersFromFilesystem`), so a coming-soon Provider's markers never produce a candidate or an ambiguous prompt; the BFF drops it from the `selectable` set served by `GET /api/active-provider`; and the UI renders it greyed with a `(coming soon)` suffix in the lens dropdown. Use this for vendor Providers whose support is registered but not yet ready for end users (today: `openai`, `antigravity`, `agent-skills`). Defaults to `false` (selectable). Distinct from `hideChip` (which only suppresses the per-card badge) and from operator-driven disable via `plugins[<id>].enabled = false` (a reversible config toggle, not a product-availability statement)."
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
},
|
|
@@ -46,10 +46,15 @@
|
|
|
46
46
|
"minimum": 1,
|
|
47
47
|
"description": "Files larger than this are skipped before they are read and surfaced at WARN level. Default 1048576 (1 MiB). Protects against scanning accidental binary drops or generated artefacts. Every skipped file is reported in `ScanResult.oversizedFiles` (root-relative path + byte size), counted in `ScanResult.stats.filesOversized`, printed as a terminal warning on `sm scan` / `sm watch` / `sm serve`, and raised as a UI banner. Trim the offending paths via `.skillmapignore` or raise this limit to include them."
|
|
48
48
|
},
|
|
49
|
+
"maxScan": {
|
|
50
|
+
"type": "integer",
|
|
51
|
+
"minimum": 1,
|
|
52
|
+
"description": "Scan corpus ceiling: hard cap on the number of files the scan accepts after `.skillmapignore` filtering, before extractors run. Default 50000. The scan walks, parses, analyzes, and reference-validates every file up to this ceiling; that full corpus is what link resolution checks against, so references resolve across the whole project (a large monorepo) regardless of how many nodes the map renders. When the walker reaches the ceiling, additional files are dropped in stable provider-walker order and the scan is marked truncated in `scan_meta` (the UI raises a persistent banner pointing at the `.skillmapignore` editor in Settings → Project). Override per invocation with `--max-scan N` on `sm scan` / `sm watch` / `sm serve`, bidirectional (raises OR lowers the ceiling, the flag is a full override of this setting). Distinct from `maxNodes`, which bounds only the graph render, not the scan."
|
|
53
|
+
},
|
|
49
54
|
"maxNodes": {
|
|
50
55
|
"type": "integer",
|
|
51
56
|
"minimum": 1,
|
|
52
|
-
"description": "
|
|
57
|
+
"description": "Map render cap: the maximum number of nodes the graph view projects onto the canvas at once. Default 256. This does NOT bound the scan; the full corpus is walked and reference-validated up to `maxScan`, and the folders tree shows all of it. `maxNodes` only limits the Foblex graph projection so a large project stays readable. When a selected folder branch exceeds this cap, the map renders the branch's first N nodes (stable order) and raises an in-view banner inviting the user to pick a narrower sub-folder. Override per invocation with `--max-nodes N` on `sm scan` / `sm watch` / `sm serve`; the value persists in `scan_meta` and is read by the UI when the map renders. Headless `sm scan` records but does not act on it (it renders nothing)."
|
|
53
58
|
},
|
|
54
59
|
"watch": {
|
|
55
60
|
"type": "object",
|
|
@@ -41,15 +41,19 @@
|
|
|
41
41
|
"type": "string",
|
|
42
42
|
"description": "Resolved offline tokenizer (encoder) that produced the per-node token counts in this scan, one of the closed allow-list in `project-config.schema.json#/properties/tokenizer` (`cl100k_base` default, `o200k_base`). Mirrors `scan_meta.tokenizer`. Reported so consumers know which encoder the counts came from; the incremental scan compares the persisted value against the resolved one and force-recomputes counts when they differ. Absent on synthetic fixtures and when tokenization was disabled."
|
|
43
43
|
},
|
|
44
|
-
"
|
|
44
|
+
"scanCeiling": {
|
|
45
45
|
"type": "integer",
|
|
46
46
|
"minimum": 1,
|
|
47
|
-
"description": "Effective
|
|
47
|
+
"description": "Effective walk ceiling for this scan (`--max-scan <N>` override on `sm scan` / `sm watch` / `sm serve`, else `scan.maxScan` from settings, default 50000). The scan walks, parses, analyzes, and reference-validates the full corpus up to this number, so references resolve across the whole project regardless of how many nodes the map renders. Mirrors `scan_meta.scan_ceiling`. Absent on synthetic fixtures."
|
|
48
48
|
},
|
|
49
|
-
"
|
|
50
|
-
"type":
|
|
49
|
+
"scanTruncated": {
|
|
50
|
+
"type": "boolean",
|
|
51
|
+
"description": "True when the walker reached `scanCeiling` and dropped files in stable provider-walker order, false otherwise. Reported so the UI raises the persistent 'scan truncated' banner pointing at the `.skillmapignore` editor. Mirrors `scan_meta.scan_truncated`. Absent on synthetic fixtures."
|
|
52
|
+
},
|
|
53
|
+
"maxRenderNodes": {
|
|
54
|
+
"type": "integer",
|
|
51
55
|
"minimum": 1,
|
|
52
|
-
"description": "
|
|
56
|
+
"description": "Effective map render cap for this scan (`--max-nodes <N>` override, else `scan.maxNodes` from settings, default 256). Does NOT bound the scan (the full corpus up to `scanCeiling` is persisted and the folders tree shows all of it); it only bounds the graph projection. The UI projects the selected folder branch capped at this number and raises an in-view banner when a branch exceeds it. Mirrors `scan_meta.max_render_nodes`. Absent on synthetic fixtures."
|
|
53
57
|
},
|
|
54
58
|
"oversizedFiles": {
|
|
55
59
|
"type": "array",
|