@skill-map/spec 0.29.0 → 0.30.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 +150 -0
- package/architecture.md +2 -4
- package/index.json +5 -5
- package/package.json +1 -1
- package/plugin-author-guide.md +26 -22
- package/schemas/project-config.schema.json +1 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,155 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.30.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5f4b181: Remove `@skill-map/testkit` and `examples/hello-world` from the monorepo.
|
|
8
|
+
The packaged plugin-author helper layer is retired. Plugin authors test
|
|
9
|
+
extensions by building fake `ctx` literals against the public types
|
|
10
|
+
re-exported from `@skill-map/cli` (`IExtractor`, `IAnalyzer`,
|
|
11
|
+
`IFormatter`, the matching `*Context` shapes, `Node`, `Link`, `Issue`).
|
|
12
|
+
Reason: zero downstream consumers in the public ecosystem after Step
|
|
13
|
+
9.3; the maintenance cost of an independently-versioned npm package +
|
|
14
|
+
its own changesets, validate phases, and narrative outweighed the value
|
|
15
|
+
of a thin packaged helper layer.
|
|
16
|
+
|
|
17
|
+
**`spec/plugin-author-guide.md`:**
|
|
18
|
+
|
|
19
|
+
- §Testing rewritten as "Testing your plugin": shows the fake-`ctx`
|
|
20
|
+
pattern inline (extractor + analyzer + formatter + probabilistic
|
|
21
|
+
runner), with the public types coming from `@skill-map/cli`.
|
|
22
|
+
- §Stability footer updated to reference Step 10 for future
|
|
23
|
+
Action / Hook testing patterns instead of testkit coverage.
|
|
24
|
+
- §Providers / Actions advisory wording no longer references the
|
|
25
|
+
testkit roadmap.
|
|
26
|
+
|
|
27
|
+
**`spec/architecture.md`:**
|
|
28
|
+
|
|
29
|
+
- `src/` directory tree drops the `testkit/` row.
|
|
30
|
+
- Qualified-id example list swaps `hello-world/greet` for the
|
|
31
|
+
generic `my-plugin/my-extractor`.
|
|
32
|
+
|
|
33
|
+
**Monorepo plumbing** (no end-user impact):
|
|
34
|
+
|
|
35
|
+
- `pnpm-workspace.yaml`, root `package.json`, `Dockerfile`, and
|
|
36
|
+
`scripts/check-changeset.js` drop the `testkit/` and
|
|
37
|
+
`examples/hello-world/` entries.
|
|
38
|
+
- `context/scripts.md`, `context/kernel.md`, `context/notebooklm.md`,
|
|
39
|
+
`ROADMAP.md`, `CONTRIBUTING.md`, `AGENTS.md`, `.claude/agents/commit.md`,
|
|
40
|
+
and `scripts/build-user-changelog.js` updated to reflect the
|
|
41
|
+
two-public-package surface (`@skill-map/spec` + `@skill-map/cli`).
|
|
42
|
+
- `src/__tests__/integration/dockerfile-demo-assets.spec.ts` drops
|
|
43
|
+
the obsolete `COPY` assertions for both removed workspaces.
|
|
44
|
+
- JSDoc in `src/kernel/registry.ts` replaces the `hello-world/greet`
|
|
45
|
+
example with `my-plugin/my-extractor`.
|
|
46
|
+
|
|
47
|
+
**`web/modules/roadmap.js`:**
|
|
48
|
+
|
|
49
|
+
- Step 9 card (EN + ES, release tag + brief) drops the
|
|
50
|
+
`@skill-map/testkit` mention.
|
|
51
|
+
|
|
52
|
+
**Post-merge action required**: run
|
|
53
|
+
`/usr/bin/npm deprecate "@skill-map/testkit@*" "Subsumed: plugin authors
|
|
54
|
+
test against @skill-map/cli types directly. See
|
|
55
|
+
https://github.com/crystian/skill-map/blob/main/spec/plugin-author-guide.md."`
|
|
56
|
+
against the real `npm` binary (NOT the `pnpm`-aliased `npm` in the
|
|
57
|
+
maintainer's shell, which fails with `ERR_PNPM_REGISTRY_ERROR: 404 Not
|
|
58
|
+
Found` on the deprecate endpoint). `/usr/bin/` bypasses the zsh alias;
|
|
59
|
+
`command npm` and `\npm` are equivalent escapes. Latest published
|
|
60
|
+
version is `0.5.2`; the wildcard range covers every prior tag so anyone
|
|
61
|
+
with the package pinned sees the deprecation notice.
|
|
62
|
+
|
|
63
|
+
- d95e5b8: Remove the `scan.extraFolders` config key. Project-local persistent
|
|
64
|
+
extension of the indexed scan no longer exists; to walk a directory
|
|
65
|
+
outside the project root pass it as a positional argument to
|
|
66
|
+
`sm scan [roots...]` (per-invocation, not persisted). The narrower
|
|
67
|
+
`scan.referencePaths` key (validate links against on-disk files
|
|
68
|
+
without indexing them) is unaffected.
|
|
69
|
+
|
|
70
|
+
**Spec (`spec/`):**
|
|
71
|
+
|
|
72
|
+
- `spec/schemas/project-config.schema.json`: `extraFolders` block
|
|
73
|
+
deleted. `scan.referencePaths` description trimmed of cross-
|
|
74
|
+
references and now reads stand-alone.
|
|
75
|
+
- `spec/architecture.md` §Config layering: `PROJECT_LOCAL_ONLY_KEYS`
|
|
76
|
+
catalogue drops `scan.extraFolders`.
|
|
77
|
+
- `spec/plugin-author-guide.md`: the "the only way to scan paths
|
|
78
|
+
outside the project is `scan.extraFolders`" sentence rewrites to
|
|
79
|
+
point at positional roots.
|
|
80
|
+
- `spec/index.json` regenerated.
|
|
81
|
+
|
|
82
|
+
**Kernel + config (`src/kernel/`, `src/config/`, `src/core/config/`):**
|
|
83
|
+
|
|
84
|
+
- `IScanConfig` drops `extraFolders: string[]`.
|
|
85
|
+
- `PROJECT_LOCAL_ONLY_KEYS` and `PRIVACY_SENSITIVE_KEYS` lose the
|
|
86
|
+
entry.
|
|
87
|
+
- `projectPathExposure` collapses the two-branch list-check to one.
|
|
88
|
+
- `defaults.json` drops the `extraFolders: []` line.
|
|
89
|
+
|
|
90
|
+
**Runtime (`src/core/runtime/`):**
|
|
91
|
+
|
|
92
|
+
- `resolveScanRoots(inputs)` simplifies to `{ positionalRoots } =>
|
|
93
|
+
string[]`; no more `IScanRootsInputs.extraFolders`,
|
|
94
|
+
`IScanRootsResolution.fromExtra`, or `emitRootsAdvisory()`.
|
|
95
|
+
- The `includingExtraFoldersAdvisory` text catalog entry is removed.
|
|
96
|
+
|
|
97
|
+
**CLI (`src/cli/`):**
|
|
98
|
+
|
|
99
|
+
- `sm scan` help text loses the extraFolders sentence; positional
|
|
100
|
+
roots are now the documented way to extend the scan.
|
|
101
|
+
- `sm serve` boot banner reads only `scan.referencePaths` from the
|
|
102
|
+
effective config; the banner row labelled `Extras` (and the
|
|
103
|
+
matching shape on `IBannerInput` / `IFigletInput`) is removed.
|
|
104
|
+
`Refs` stays.
|
|
105
|
+
- `sm config set --yes` description trimmed to reflect the single
|
|
106
|
+
privacy-sensitive key remaining.
|
|
107
|
+
|
|
108
|
+
**Server (`src/server/`):**
|
|
109
|
+
|
|
110
|
+
- `WatcherService` no longer reads config to compute roots; it walks
|
|
111
|
+
`['.']` unconditionally. `loadConfig` and `resolveScanRoots`
|
|
112
|
+
imports drop. `restart()` is still useful (and still wired by
|
|
113
|
+
`PATCH /api/project-preferences`) so the side-set walk picks up
|
|
114
|
+
fresh `scan.referencePaths` on the next batch.
|
|
115
|
+
- `PATCH /api/project-preferences`: AJV body schema, `IPatchBody`,
|
|
116
|
+
`IProjectPreferencesEnvelope`, `IPlannedWrite.key`, `collectWrites`
|
|
117
|
+
all collapse to a single `referencePaths` branch.
|
|
118
|
+
- Catalog strings adjusted (the `extraFolders` example dropped from
|
|
119
|
+
`projectPrefsScanNotObject` etc).
|
|
120
|
+
|
|
121
|
+
**UI (`ui/src/`):**
|
|
122
|
+
|
|
123
|
+
- Settings → Project drops the entire `extraFolders` row (HTML, TS
|
|
124
|
+
signal + computed + add/remove handlers, i18n strings, mocks).
|
|
125
|
+
- `IProjectPreferencesApi` and `IProjectPreferencesPatchApi` lose
|
|
126
|
+
`extraFolders`.
|
|
127
|
+
- Test mocks (`app.spec.ts`, `graph-view.spec.ts`,
|
|
128
|
+
`inspector-view.spec.ts`) updated.
|
|
129
|
+
|
|
130
|
+
**Tests:**
|
|
131
|
+
|
|
132
|
+
- `server/routes/__tests__/project-preferences-route.spec.ts`: 5
|
|
133
|
+
PATCH cases remapped from `extraFolders` to `referencePaths`.
|
|
134
|
+
- `kernel/config/__tests__/config-loader.spec.ts`: strip-test
|
|
135
|
+
renamed and split.
|
|
136
|
+
- `core/runtime/__tests__/scan-roots.spec.ts`: drops 3 cases that
|
|
137
|
+
passed `extraFolders`; keeps the positional + default cases.
|
|
138
|
+
- `core/config/__tests__/config-helper.spec.ts`:
|
|
139
|
+
`PROJECT_LOCAL_ONLY_KEYS` catalogue assertion narrowed; the
|
|
140
|
+
`target=project` rejection test now targets `scan.referencePaths`.
|
|
141
|
+
|
|
142
|
+
**Backward compatibility note**: existing `settings.local.json` files
|
|
143
|
+
that still carry `scan.extraFolders` keep loading without error. The
|
|
144
|
+
loader's per-key resilience drops the unknown key with a generic
|
|
145
|
+
"unknown key ignored" warning; nothing crashes, the rest of the file
|
|
146
|
+
takes effect. Operators who relied on the key should switch to
|
|
147
|
+
positional roots on `sm scan`.
|
|
148
|
+
|
|
149
|
+
## User-facing
|
|
150
|
+
|
|
151
|
+
We removed `scan.extraFolders`. To extend the scan beyond the project root, pass folders as positional arguments to `sm scan [roots...]`. The `scan.referencePaths` key (validates links against on-disk files without indexing) is unchanged. Existing entries are silently ignored.
|
|
152
|
+
|
|
3
153
|
## 0.29.0
|
|
4
154
|
|
|
5
155
|
### Minor Changes
|
package/architecture.md
CHANGED
|
@@ -72,7 +72,7 @@ The loader enforces two id-uniqueness analyzers during discovery (see [`plugin-a
|
|
|
72
72
|
1. **Directory name == manifest id.** A plugin lives at `<root>/<id>/plugin.json`. A mismatch surfaces as status `invalid-manifest`. This analyzer eliminates same-root collisions by construction.
|
|
73
73
|
2. **Cross-root id collision blocks both sides.** Two plugins reachable from different roots (e.g. the project default `<cwd>/.skill-map/plugins/` and any `--plugin-dir` combination) that declare the same `id` BOTH receive status `id-collision`. No precedence analyzer applies, coherent with §Boot invariant ("no extension is privileged"). The user resolves by renaming one of them.
|
|
74
74
|
|
|
75
|
-
In addition, the loader **qualifies every extension** with its owning plugin id before registering it. The registry stores extensions under the qualified id `<plugin-id>/<extension-id>` (e.g. `core/slash`, `core/broken-ref`, `
|
|
75
|
+
In addition, the loader **qualifies every extension** with its owning plugin id before registering it. The registry stores extensions under the qualified id `<plugin-id>/<extension-id>` (e.g. `core/slash`, `core/broken-ref`, `my-plugin/my-extractor`). Authors continue to declare the short `id` in each extension manifest; the loader composes the qualified form from `manifest.id` at load time. Built-in extensions bundled with the reference impl 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` / `external-url-counter` / `stability`) and vendor-specific bundles such as `claude/` (the Claude provider) for Provider integrations whose territory is platform-bound. If a plugin author injects a `pluginId` field on an extension that disagrees with `plugin.json`'s `id`, the loader emits `invalid-manifest` with a directed reason.
|
|
76
76
|
|
|
77
77
|
Each plugin (and each built-in bundle) declares a **granularity** that controls how its extensions are toggled. `granularity: 'bundle'` (the default) means the plugin id is the only enable/disable key; `granularity: 'extension'` means each extension is independently toggle-able under its qualified id. The loader's pre-import `resolveEnabled(pluginId)` short-circuit is always coarse (bundle level), when a granularity=`extension` bundle is partially enabled, the import work proceeds and the runtime composer (the CLI's `composeScanExtensions` / `composeFormatters` in `src/cli/util/plugin-runtime.ts`) drops the disabled extensions before they reach the orchestrator. Vendor Provider bundles (`claude`, `gemini`, `agent-skills`) ship as granularity=`bundle` (the platform integration is on or off as a whole); the `core` bundle is granularity=`extension` (every kernel built-in is removable, satisfying §Boot invariant: "no extension is privileged"). See [`plugin-author-guide.md` §Granularity, bundle vs extension](./plugin-author-guide.md#granularity--bundle-vs-extension) for the author-facing summary.
|
|
78
78
|
|
|
@@ -408,7 +408,6 @@ src/
|
|
|
408
408
|
├── kernel/ Registry, Orchestrator, domain types, use cases, port interfaces
|
|
409
409
|
├── cli/ Clipanion commands, thin wrappers over kernel
|
|
410
410
|
├── server/ Hono + WebSocket, thin wrapper over kernel
|
|
411
|
-
├── testkit/ Kernel mocks for plugin authors
|
|
412
411
|
└── adapters/
|
|
413
412
|
├── sqlite/ node:sqlite + Kysely + CamelCasePlugin (StoragePort)
|
|
414
413
|
├── filesystem/ real fs (FilesystemPort)
|
|
@@ -459,10 +458,9 @@ One locality class constrains which layers a given key MAY live in. It is enforc
|
|
|
459
458
|
|
|
460
459
|
Members:
|
|
461
460
|
- `allowEditSmFiles`, per-project consent to create / modify `.sm` sidecars.
|
|
462
|
-
- `scan.extraFolders`, additional scan paths (the ONLY way to extend the scan beyond the project root).
|
|
463
461
|
- `scan.referencePaths`, additional link-validation paths.
|
|
464
462
|
|
|
465
|
-
|
|
463
|
+
Both describe disk access the local operator opted into; sharing them via the repo would silently expand every collaborator's scan surface to paths that only make sense on the original author's machine.
|
|
466
464
|
|
|
467
465
|
Adding a new entry is a behaviour change for older installs that wrote the key into a committed file, the value gets stripped at read time. The changeset that adds the entry MUST document the migration.
|
|
468
466
|
|
package/index.json
CHANGED
|
@@ -174,13 +174,13 @@
|
|
|
174
174
|
}
|
|
175
175
|
]
|
|
176
176
|
},
|
|
177
|
-
"specPackageVersion": "0.
|
|
177
|
+
"specPackageVersion": "0.30.0",
|
|
178
178
|
"integrity": {
|
|
179
179
|
"algorithm": "sha256",
|
|
180
180
|
"files": {
|
|
181
|
-
"CHANGELOG.md": "
|
|
181
|
+
"CHANGELOG.md": "9ef9cad9d55872179022ce039ffa02441e17547fec03a6b20c70a83398bda35a",
|
|
182
182
|
"README.md": "54c4649fa9742bf2f74423ea78788a7474ce09649cbe1e72a270b606cf16a0a5",
|
|
183
|
-
"architecture.md": "
|
|
183
|
+
"architecture.md": "296c8348b630016aac8b21b48c51ba78e4e42c1884805fe426a894e15ba746cc",
|
|
184
184
|
"cli-contract.md": "c22f7c82d460714efaf34a04a2d2367d21eb04985100aef1291071e6726cbc64",
|
|
185
185
|
"conformance/README.md": "6871dde25b5770ed945284c9e0f749e0768ec3f5ba4966bdb215985789e43887",
|
|
186
186
|
"conformance/cases/kernel-empty-boot.json": "2a5be9c93143d07a16d998df09dcc8fa4ea2d2f9a0bff6417573ed5a770352c1",
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
"interfaces/security-scanner.md": "e8049712b9cf7a07c786bf19f8f775f8ef9638f063f7fba5c7a8b1431b92f38e",
|
|
207
207
|
"job-events.md": "84206168ac12b536d34470d62f8c8cba95dab181fee66d23203c2cf5dfbee716",
|
|
208
208
|
"job-lifecycle.md": "9c429121f98a07c8795f8979ed1abc5e5334e3f89db51585a8da55c527ef855b",
|
|
209
|
-
"plugin-author-guide.md": "
|
|
209
|
+
"plugin-author-guide.md": "bf7e01b1a36bfe0287d552f12d2211e76de33daf3cd172b841d46e49f7394878",
|
|
210
210
|
"plugin-kv-api.md": "1acc69ed82433a74e35ada61d63a6d7379fb61046ff83de1e0facbe884c64704",
|
|
211
211
|
"prompt-preamble.md": "9dd4f6d1bc6a425f8782fcee10cbe75909e8d64e28781fda56c2fae909b02f40",
|
|
212
212
|
"schemas/annotations.schema.json": "e39990d47f53e25a1b3a5587a5714486d0b819b8eeaac10d42783a675296aee1",
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
"schemas/node.schema.json": "e5da06c9262cc0f2f7584d5733ebc1c08acd75487952ed7b4d6035fb417aaa4b",
|
|
233
233
|
"schemas/plugins-doctor.schema.json": "c1d92f30fdb0080e8cd8f7dc5d43e01aae02a16640bc5eb04811c337a275de58",
|
|
234
234
|
"schemas/plugins-registry.schema.json": "cca7ae65f0c22510ea27ea5ae34e0074f5beb5871a57b005b6b831e6ceaff5c0",
|
|
235
|
-
"schemas/project-config.schema.json": "
|
|
235
|
+
"schemas/project-config.schema.json": "84d41720edc968a8e05499783d5a81ff18aaf2dd8b702140ee84c0045fd68789",
|
|
236
236
|
"schemas/refresh-report.schema.json": "54519b8caf86ba84c182f9565be9b5084bc1631ae05e9217ee18f34c0039fff3",
|
|
237
237
|
"schemas/report-base-deterministic.schema.json": "9d318d0181d121097c906ef3af1c52d71c782740bd04cf23418d7627ce2c3ed5",
|
|
238
238
|
"schemas/report-base.schema.json": "a1021e9a59b4df9f99cd92454d797e88469766e7d49f52d231c4645ffdfdad8f",
|
package/package.json
CHANGED
package/plugin-author-guide.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Plugin author guide
|
|
2
2
|
|
|
3
|
-
How to ship a third-party `skill-map` plugin: directory layout, manifest fields, the six extension kinds, storage choice, version compatibility, dual-mode posture, and how to test the result
|
|
3
|
+
How to ship a third-party `skill-map` plugin: directory layout, manifest fields, the six extension kinds, storage choice, version compatibility, dual-mode posture, and how to unit-test the result against the kernel's public types.
|
|
4
4
|
|
|
5
5
|
This guide is **descriptive prose**, not the normative contract. The normative pieces live in the schemas and the architecture document, every claim here is cross-linked to its source. When the two disagree, [`architecture.md`](./architecture.md) wins.
|
|
6
6
|
|
|
@@ -485,7 +485,7 @@ export default {
|
|
|
485
485
|
|
|
486
486
|
### Providers / Actions
|
|
487
487
|
|
|
488
|
-
These ship later in the v1.x line as bundled built-ins; the spec already pins their manifest shapes. Until
|
|
488
|
+
These ship later in the v1.x line as bundled built-ins; the spec already pins their manifest shapes. Until Step 10 lands the job subsystem, authors are encouraged to test them with a live kernel via `sm scan` against a fixture directory rather than in unit tests.
|
|
489
489
|
|
|
490
490
|
#### Provider, `kinds` catalog
|
|
491
491
|
|
|
@@ -497,7 +497,7 @@ Every Provider declares one required top-level field beyond the manifest base: `
|
|
|
497
497
|
- **`defaultRefreshAction`**, qualified action id (`<plugin-id>/<action-id>`) the UI's `🧠 prob` button dispatches. The action MUST exist in the registry; a dangling reference disables the Provider with `invalid-manifest`.
|
|
498
498
|
- **`ui`**, presentation block: `{ label, color, colorDark?, emoji?, icon? }`. The UI ships every `ui` block to the front-end via the `kindRegistry` envelope so built-in and user-plugin kinds render identically. `icon` is a discriminated union (`{ kind: 'pi'; id }` for PrimeIcons, `{ kind: 'svg'; path }` for raw SVG). The `ui` block is required (not optional) so the UI never has to invent visuals for unknown kinds. See [`architecture.md` §Provider · `ui` presentation](./architecture.md#provider--ui-presentation) for the field-by-field contract.
|
|
499
499
|
|
|
500
|
-
The Provider's walker hardcodes the paths it scans within the project (e.g. `.claude/`, `.cursor/rules`). The kernel does NOT extend the scan into the user's HOME based on Provider hints; the only way to scan paths outside the project is
|
|
500
|
+
The Provider's walker hardcodes the paths it scans within the project (e.g. `.claude/`, `.cursor/rules`). The kernel does NOT extend the scan into the user's HOME based on Provider hints; the only way to scan paths outside the project is by passing them as positional roots to `sm scan [roots...]` (per-invocation, not persisted).
|
|
501
501
|
|
|
502
502
|
```jsonc
|
|
503
503
|
{
|
|
@@ -704,45 +704,49 @@ The full per-kind capability matrix lives in [`architecture.md` §Execution mode
|
|
|
704
704
|
|
|
705
705
|
---
|
|
706
706
|
|
|
707
|
-
## Testing
|
|
707
|
+
## Testing your plugin
|
|
708
708
|
|
|
709
|
-
|
|
710
|
-
npm install --save-dev @skill-map/testkit
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
The testkit ships builders, per-kind context factories, in-memory KV / runner fakes, and high-level `runExtractorOnFixture` / `runAnalyzerOnGraph` / `runFormatterOnGraph` helpers. Most plugin tests reduce to one line per assertion.
|
|
709
|
+
Plugin extensions are plain ESM modules with a single entry point per kind (`extract` / `evaluate` / `format` / `run` / `on`); their inputs are well-typed context objects from `@skill-map/cli`. That makes them straightforward to unit-test without a kernel or DB: build a fake `ctx` literal, call the entry point, assert on what it captured.
|
|
714
710
|
|
|
715
711
|
```javascript
|
|
716
712
|
import { test } from 'node:test';
|
|
717
713
|
import { strictEqual } from 'node:assert';
|
|
718
|
-
import { runExtractorOnFixture, node } from '@skill-map/testkit';
|
|
719
714
|
|
|
720
715
|
import extractor from '../extractors/my-extractor/index.js';
|
|
721
716
|
|
|
722
717
|
test('emits one reference per [[ref:<name>]] token', async () => {
|
|
723
|
-
const
|
|
718
|
+
const links = [];
|
|
719
|
+
await extractor.extract({
|
|
720
|
+
node: { path: 'a.md', kind: 'skill', provider: 'claude' },
|
|
724
721
|
body: 'Talk to [[ref:architect]] or [[ref:sre]].',
|
|
725
|
-
|
|
722
|
+
frontmatter: {},
|
|
723
|
+
settings: {},
|
|
724
|
+
emitLink: (link) => links.push(link),
|
|
725
|
+
enrichNode: () => {},
|
|
726
|
+
emitContribution: () => {},
|
|
726
727
|
});
|
|
727
728
|
strictEqual(links.length, 2);
|
|
728
729
|
strictEqual(links[0].target, 'architect');
|
|
729
730
|
});
|
|
730
731
|
```
|
|
731
732
|
|
|
732
|
-
For
|
|
733
|
+
For analyzers, the same pattern applies: build a `ctx` with `nodes`, `links`, an `emitContribution` spy if you assert on view contributions, and call `analyzer.evaluate(ctx)`, it returns the issue array. Formatters take `{ nodes, links, issues }` and return a string from `formatter.format(ctx)`.
|
|
733
734
|
|
|
734
|
-
For probabilistic extensions
|
|
735
|
+
For probabilistic extensions (Actions / Hooks running in `mode: 'probabilistic'`), shape a fake `ctx.runner` that records the calls your test cares about:
|
|
735
736
|
|
|
736
737
|
```javascript
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
738
|
+
const calls = [];
|
|
739
|
+
const runner = {
|
|
740
|
+
async run(call) {
|
|
741
|
+
calls.push(call);
|
|
742
|
+
return { text: 'mocked response' };
|
|
743
|
+
},
|
|
744
|
+
};
|
|
745
|
+
await myAction.run({ runner, /* … */ });
|
|
746
|
+
strictEqual(calls[0].action, 'skill-summarizer');
|
|
743
747
|
```
|
|
744
748
|
|
|
745
|
-
|
|
749
|
+
The public TypeScript types (`IExtractor`, `IAnalyzer`, `IFormatter`, `IExtractorContext`, `IAnalyzerContext`, `IFormatterContext`, `Node`, `Link`, `Issue`, …) are re-exported from `@skill-map/cli` so authors can type-check their fakes against the same surface the kernel consumes.
|
|
746
750
|
|
|
747
751
|
---
|
|
748
752
|
|
|
@@ -1218,7 +1222,7 @@ Companion verbs:
|
|
|
1218
1222
|
|
|
1219
1223
|
## Stability
|
|
1220
1224
|
|
|
1221
|
-
- Document status: **stable** as of spec v1.0.0. Future minor revisions add new sections (e.g.
|
|
1225
|
+
- Document status: **stable** as of spec v1.0.0. Future minor revisions add new sections (e.g. Action / Hook testing patterns once Step 10 lands the job subsystem); breaking edits to the documented surface require a major bump per [`versioning.md`](./versioning.md).
|
|
1222
1226
|
- The six plugin statuses (`loaded` / `disabled` / `incompatible-spec` / `invalid-manifest` / `load-error` / `id-collision`) are stable; adding a seventh status is a minor bump.
|
|
1223
1227
|
- The structural analyzer **directory name MUST equal manifest id** is stable; relaxing it (allowing mismatch) is a major bump.
|
|
1224
1228
|
- The cross-root id-collision analyzer (both sides blocked, no precedence) is stable; introducing precedence (e.g. project root wins over global) is a major bump.
|
|
@@ -58,15 +58,10 @@
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
|
-
"extraFolders": {
|
|
62
|
-
"type": "array",
|
|
63
|
-
"items": { "type": "string" },
|
|
64
|
-
"description": "**Privacy-sensitive, project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`) when entries point outside the project, opens disk access there. Default `[]`. Additional directories appended to the scan roots; same parsing / indexing as the project root. Paths starting with `~` resolve against the user home; relative paths resolve against the project root. Reference impl gates writes that introduce out-of-project paths behind `--yes` (CLI) and a confirm dialog (UI). **Stripped with a warning when found in the committed `project` layer**, paths are inherently per-machine and must not travel via the shared repo. This is the ONLY mechanism to extend the scan beyond the project root: skill-map does NOT auto-include the user's HOME based on Provider hints, every out-of-project path must be listed here explicitly."
|
|
65
|
-
},
|
|
66
61
|
"referencePaths": {
|
|
67
62
|
"type": "array",
|
|
68
63
|
"items": { "type": "string" },
|
|
69
|
-
"description": "**Privacy-sensitive, project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`) when entries point outside the project, opens read-only disk access for link validation only. Default `[]`. Directories walked in parallel by the scan to collect existing absolute paths into a side set; the kernel passes the set to analyzers via `IAnalyzerContext.referenceablePaths` so `core/broken-ref` can resolve a link against the filesystem when the in-graph lookup misses. Files under these paths are NOT parsed and NOT indexed as nodes, the only effect is suppressing `broken-ref` warnings for targets that exist on disk outside the scan.
|
|
64
|
+
"description": "**Privacy-sensitive, project-local only** (per `core/config/helper:PROJECT_LOCAL_ONLY_KEYS`) when entries point outside the project, opens read-only disk access for link validation only. Default `[]`. Directories walked in parallel by the scan to collect existing absolute paths into a side set; the kernel passes the set to analyzers via `IAnalyzerContext.referenceablePaths` so `core/broken-ref` can resolve a link against the filesystem when the in-graph lookup misses. Files under these paths are NOT parsed and NOT indexed as nodes, the only effect is suppressing `broken-ref` warnings for targets that exist on disk outside the scan. Reference impl gates writes that introduce out-of-project paths behind `--yes` (CLI) and a confirm dialog (UI). **Stripped with a warning when found in the committed `project` layer**, paths are inherently per-machine and must not travel via the shared repo."
|
|
70
65
|
}
|
|
71
66
|
}
|
|
72
67
|
},
|