@skill-map/spec 0.49.0 → 0.50.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 +10 -0
- package/architecture.md +4 -0
- package/cli-contract.md +1 -0
- package/index.json +7 -7
- package/package.json +1 -1
- package/plugin-author-guide.md +16 -0
- package/schemas/input-types.schema.json +21 -3
- package/schemas/project-config.schema.json +17 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.50.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Plugin extensions declare operator-configurable `settings` in their manifest, read at scan time via `ctx.settings` and resolved through the config layers under `plugins.<id>.extensions.<extId>.settings`. The `sm plugins config <plugin>/<ext>` verb, `GET`/`PATCH /api/plugins`, and per-plugin sections in Settings all read and write them; `secret` values route to the gitignored project-local file (no encryption). Adds a `number` (decimal) input-type to the catalog.
|
|
8
|
+
|
|
9
|
+
## User-facing
|
|
10
|
+
|
|
11
|
+
Plugin extensions can expose options: edit them per plugin in Settings (one global Apply) or via `sm plugins config <plugin>/<ext>` (saved in `.skill-map/settings.json`; secrets stay local, never committed). Run `sm scan` to apply. New decimal `number` option type.
|
|
12
|
+
|
|
3
13
|
## 0.49.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/architecture.md
CHANGED
|
@@ -633,6 +633,10 @@ One locality class constrains which layers a given key MAY live in. It is enforc
|
|
|
633
633
|
|
|
634
634
|
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.
|
|
635
635
|
|
|
636
|
+
### Extension settings resolution
|
|
637
|
+
|
|
638
|
+
Plugin extensions declare user-configurable `settings` in their manifest (per-extension, see `plugin-author-guide.md` §Settings); the operator's values for them live in the config tree under `plugins.<pluginId>.extensions.<extId>.settings.<settingId>` and therefore flow through the same four-layer merge as any other key. The kernel's settings resolver runs once per scan, while composing the enabled extensions: for each declared setting it takes the manifest `default`, overlays the merged config value, and validates the result against the input-type's value schema (`input-types.schema.json#/$defs/ISettingDeclaration`); a value that fails validation falls back to the default with a warning, so the scan never aborts on a bad setting. The resolved object reaches the extension's runtime methods as `ctx.settings.<settingId>`. The `project-config.schema.json` keeps the `settings` object permissive (`additionalProperties: true`) on purpose: the static schema cannot know which input-type a given `settingId` picked, so per-value validation is the resolver's responsibility, not AJV's. `secret`-typed settings are still config-layer values, but the kernel forces them into the project-local layer (`settings.local.json`, gitignored), never the committed `settings.json`, the dynamic equivalent of `PROJECT_LOCAL_ONLY_KEYS` (the destination follows the declared type, not a fixed key list). There is no encryption in v1: the protection is that the value never travels via the shared repo (see `input-types.schema.json#/$defs/Setting_Secret`).
|
|
639
|
+
|
|
636
640
|
---
|
|
637
641
|
|
|
638
642
|
## Annotation system
|
package/cli-contract.md
CHANGED
|
@@ -543,6 +543,7 @@ Authentication: the nonce is the sole credential. An implementation MUST reject
|
|
|
543
543
|
| `sm plugins show <plugin>/<ext>` | Single-extension detail (Kind / Version / Stability / Description / Preconditions / Entry; `--json` emits the single extension object). Accepts only a qualified `<plugin>/<ext>` id; a bare plugin id is rejected with a redirect to `sm plugins list <id>`. |
|
|
544
544
|
| `sm plugins enable <id>... \| --all` | Toggle on. Persists in `config_plugins`. Accepts one or more ids; batches are all-or-nothing (any unknown / mismatched id aborts before any write) and repeated ids are deduped. `--all` applies to every discovered plugin. |
|
|
545
545
|
| `sm plugins disable <id>... \| --all` | Toggle off; does not delete the plugin directory. Eagerly purges each id's rows from `scan_contributions` so its UI chips disappear before the next scan (plugin-managed state in `state_plugin_kvs` / dedicated tables is preserved, see `plugin-kv-api.md`). Accepts one or more ids; batches are all-or-nothing and repeated ids are deduped. `--all` applies to every discovered plugin. |
|
|
546
|
+
| `sm plugins config <plugin>/<ext> [<settingId> [<value>]] [--reset]` | Read or write the operator-supplied values for an extension's declared `settings`. No `settingId`: table of each declared setting with its effective value and the layer that set it (`--json` emits the resolved set). With `<settingId> <value>`: coerce the shell string to the setting's input-type, validate, then write it under `plugins.<pluginId>.extensions.<extId>.settings.<settingId>` (a normal setting lands in `settings.json`; a `secret`-typed one is forced into `settings.local.json`, gitignored, never committed); prints a "re-scan to apply" reminder. `--reset` drops the override back to the manifest default. Requires a qualified `<plugin>/<ext>` id; a bare plugin id is rejected with a redirect to `sm plugins list <id>`. `secret` values are redacted as `<redacted>` in output. |
|
|
546
547
|
| `sm plugins doctor` | Revalidate all plugins against current spec version; update `status` fields. `--json` emits the report shape declared by [`plugins-doctor.schema.json`](./schemas/plugins-doctor.schema.json): `{ ok: true, kind: 'plugins.doctor', counts, issues[], warnings[], elapsedMs }`. |
|
|
547
548
|
|
|
548
549
|
---
|
package/index.json
CHANGED
|
@@ -174,14 +174,14 @@
|
|
|
174
174
|
}
|
|
175
175
|
]
|
|
176
176
|
},
|
|
177
|
-
"specPackageVersion": "0.
|
|
177
|
+
"specPackageVersion": "0.50.0",
|
|
178
178
|
"integrity": {
|
|
179
179
|
"algorithm": "sha256",
|
|
180
180
|
"files": {
|
|
181
|
-
"CHANGELOG.md": "
|
|
181
|
+
"CHANGELOG.md": "d67e2fa8c42deb9f771652c66c5bc95db5df53445a956d2dc6d936e5342b2467",
|
|
182
182
|
"README.md": "a7505a7b0672c39a8b011e3c5e7d41826306476ee63768249bba4bdb3c03d4d1",
|
|
183
|
-
"architecture.md": "
|
|
184
|
-
"cli-contract.md": "
|
|
183
|
+
"architecture.md": "9fb9167ac9604cb8e4fb89e514b176ffb9ddfcf510ce94d5a3074c83a274bbcd",
|
|
184
|
+
"cli-contract.md": "6f719b132f7f219c34bca3ffe2de7e4d4b54dd16c071254c3c081c3b4ea965fe",
|
|
185
185
|
"conformance/README.md": "4ec22ca3cc8e4282fe0bfd111f22b121e0781e2b525867cd092258b8f58ae1e1",
|
|
186
186
|
"conformance/cases/backtick-path-extraction.json": "4620e7f8bc161fc57cb44001e9d99879c7e22b4865a0c27a20dc28969cd936d9",
|
|
187
187
|
"conformance/cases/extractor-emits-signal.json": "0115c7bb62a7a705f72e9d8048b3f0396e5caaeb3d04dea204415e279e58479d",
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
"interfaces/security-scanner.md": "e8049712b9cf7a07c786bf19f8f775f8ef9638f063f7fba5c7a8b1431b92f38e",
|
|
229
229
|
"job-events.md": "9d5b35d4c451a7f8eef9915d85316d924ac52f1c026a316cdda5f1099d496854",
|
|
230
230
|
"job-lifecycle.md": "9c429121f98a07c8795f8979ed1abc5e5334e3f89db51585a8da55c527ef855b",
|
|
231
|
-
"plugin-author-guide.md": "
|
|
231
|
+
"plugin-author-guide.md": "7b50d52f39bb17f9b27fd9fabcd375b9ade08fc61b33e68d78fc01a85887a3ac",
|
|
232
232
|
"plugin-kv-api.md": "1acc69ed82433a74e35ada61d63a6d7379fb61046ff83de1e0facbe884c64704",
|
|
233
233
|
"prompt-preamble.md": "9dd4f6d1bc6a425f8782fcee10cbe75909e8d64e28781fda56c2fae909b02f40",
|
|
234
234
|
"schemas/annotations.schema.json": "8c639b149cad675fdd4e7d6be2b47e920cfdd24087b41361d6e1b8280f646322",
|
|
@@ -247,14 +247,14 @@
|
|
|
247
247
|
"schemas/extensions/provider.schema.json": "bea1d73897dc8fa8499ba7c77ce535337473e5ecb3702ebca9966c08afc920f4",
|
|
248
248
|
"schemas/frontmatter/base.schema.json": "cff81510ed94824dfd12ab8b30ce9fbac65e42d61ae0edf3fbb6bbb6bb8bcb8c",
|
|
249
249
|
"schemas/history-stats.schema.json": "436aa0ffe744bdb699000447e86b45724fbd2cc4642781074eb1527522b9058c",
|
|
250
|
-
"schemas/input-types.schema.json": "
|
|
250
|
+
"schemas/input-types.schema.json": "93b27a1cbd1f131d42730eb9a89cf3af6889e9f17b20a48ce36133885503e01b",
|
|
251
251
|
"schemas/issue.schema.json": "d173aa5c5312b3d2a2cd249f55c10943c8f3cd5799e4645ae3c66316221e12d1",
|
|
252
252
|
"schemas/job.schema.json": "dbcedf137de03fde38f74686f594e600c627bf808f2aad23511a26617a663a02",
|
|
253
253
|
"schemas/link.schema.json": "02d9d8b2a1cdd1c6672e6b5821e08f09e43c298c8d602520a95decaffabfd1d3",
|
|
254
254
|
"schemas/node.schema.json": "1ebba38e0c0ae022fccbc0cdf7c298da1720a68d4cb375f0baf9f0847998a0d8",
|
|
255
255
|
"schemas/plugins-doctor.schema.json": "03e2dc51c052a09bf0198c80e2c26e6129734ada4a748e483245de3dd8576c42",
|
|
256
256
|
"schemas/plugins-registry.schema.json": "211d081691fc83526e1593c79ed9741ad8a5dbd4db1a756f72141b0cced2ea15",
|
|
257
|
-
"schemas/project-config.schema.json": "
|
|
257
|
+
"schemas/project-config.schema.json": "6f161b547072d8470eedbc773a5e0e04edf3479e91f4de3fbd9b4b7a36a2e69d",
|
|
258
258
|
"schemas/refresh-report.schema.json": "47184d4f6b15e9b7671dc178b3b3886a64422da198898508ecdb2cb27876db04",
|
|
259
259
|
"schemas/report-base-deterministic.schema.json": "59785fe6f3ceb34814bbbd03d10fa7336a32835ce598946f2923d469b32aa32a",
|
|
260
260
|
"schemas/report-base.schema.json": "e4d25f055e24f18ae0f77c24661c1bddc87ff2e43b001b6a827fcb14f9753f44",
|
package/package.json
CHANGED
package/plugin-author-guide.md
CHANGED
|
@@ -695,6 +695,22 @@ The ten input-types: `string-list`, `single-string`, `boolean-flag`, `integer`,
|
|
|
695
695
|
|
|
696
696
|
The kernel exposes resolved settings via `ctx.settings.<settingId>`. Settings are read once at extension invocation; **changing a setting requires `sm scan` to re-emit** affected contributions (the UI surfaces a "settings changed, rescan needed" indicator).
|
|
697
697
|
|
|
698
|
+
### Setting values and the operator
|
|
699
|
+
|
|
700
|
+
The manifest declares the *shape* (label, type, default); the **operator** supplies the *values*. Non-`secret` values live in the project config under `plugins.<pluginId>.extensions.<extId>.settings.<settingId>` (the extension id is the leaf folder name, not the qualified `<plugin>/<ext>` id, the plugin is already the parent key), so a team can commit them in `settings.json` or keep a per-checkout override in `settings.local.json`. The kernel's settings resolver builds the runtime `ctx.settings` object by taking each declared setting's `default`, overlaying the merged config value, and validating the result against the input-type's value schema; a value that fails validation is dropped back to the default with a warning (the scan never crashes on bad settings). The `project-config.schema.json` keeps the `settings` object deliberately permissive (`additionalProperties: true`), the per-type validation is the resolver's job because the static schema cannot know which type a given `settingId` picked.
|
|
701
|
+
|
|
702
|
+
`secret` settings are the exception on WHERE they land: the kernel forces them into project-local `settings.local.json` (gitignored), never the committed `settings.json`, so a token never travels via the shared repo. There is **no encryption** (the value is plain text on the local machine); the only protection is "does not leave the checkout". An optional `envVar` lets CI inject the value without writing it to disk at all. See `input-types.schema.json#/$defs/Setting_Secret`.
|
|
703
|
+
|
|
704
|
+
The operator reads and writes values through the CLI (UI form is the parallel surface):
|
|
705
|
+
|
|
706
|
+
```text
|
|
707
|
+
sm plugins config <plugin>/<ext> # table: declared setting · effective value · source layer
|
|
708
|
+
sm plugins config <plugin>/<ext> <settingId> <value> # validate against the input-type, then write
|
|
709
|
+
sm plugins config <plugin>/<ext> <settingId> --reset # remove the override (falls back to the manifest default)
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
A write lands in `settings.json` by default (or `settings.local.json` when the layering routes it per-checkout); the command prints a "re-scan to apply" footer because settings are read once per scan.
|
|
713
|
+
|
|
698
714
|
### Catalog version
|
|
699
715
|
|
|
700
716
|
The slot + input-type catalog evolves on its own cadence. `catalogCompat` (required in the manifest) is the semver range you tested against, independent of `specCompat`. A mismatch surfaces as `incompatible-catalog`; resolution is `sm plugins upgrade <id>`, which runs registered migrations from the kernel's closed registry. When auto-migration is impossible (a slot you used was removed), the upgrade verb fails loud and your manifest needs a manual edit.
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
{ "const": "single-string", "description": "Single text input." },
|
|
13
13
|
{ "const": "boolean-flag", "description": "On/off toggle." },
|
|
14
14
|
{ "const": "integer", "description": "Integer with optional bounds." },
|
|
15
|
+
{ "const": "number", "description": "Decimal number with optional bounds." },
|
|
15
16
|
{ "const": "enum-pick", "description": "Pick one from a closed set." },
|
|
16
17
|
{ "const": "enum-multipick", "description": "Pick zero or more from a closed set." },
|
|
17
18
|
{ "const": "path-glob", "description": "Glob pattern (single or multiple)." },
|
|
18
19
|
{ "const": "regex", "description": "ECMAScript regex pattern body." },
|
|
19
|
-
{ "const": "secret", "description": "Sensitive string (
|
|
20
|
+
{ "const": "secret", "description": "Sensitive string, forced into project-local storage (gitignored), not encrypted." },
|
|
20
21
|
{ "const": "key-value-list", "description": "Editable mapping of strings to strings." }
|
|
21
22
|
],
|
|
22
23
|
"description": "Closed enum of input-type identifiers. Adding an entry requires the full spec/UI/CLI/tests round-trip. Removing or renaming an entry is a catalog-major-bump and triggers `sm plugins upgrade` migration. Each member's `description` is the catalog summary surfaced by `sm plugins slots list` and is the single source of truth for the generated kernel + CLI mirrors (see `scripts/generate-view-catalog.js`)."
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
{ "$ref": "#/$defs/Setting_SingleString" },
|
|
29
30
|
{ "$ref": "#/$defs/Setting_BooleanFlag" },
|
|
30
31
|
{ "$ref": "#/$defs/Setting_Integer" },
|
|
32
|
+
{ "$ref": "#/$defs/Setting_Number" },
|
|
31
33
|
{ "$ref": "#/$defs/Setting_EnumPick" },
|
|
32
34
|
{ "$ref": "#/$defs/Setting_EnumMultipick" },
|
|
33
35
|
{ "$ref": "#/$defs/Setting_PathGlob" },
|
|
@@ -120,6 +122,22 @@
|
|
|
120
122
|
},
|
|
121
123
|
"description": "Integer input with optional bounds. Renders as PrimeNG `<p-inputnumber>` with spinner. Value type at runtime: `number` (always integer)."
|
|
122
124
|
},
|
|
125
|
+
"Setting_Number": {
|
|
126
|
+
"allOf": [{ "$ref": "#/$defs/_Common" }],
|
|
127
|
+
"type": "object",
|
|
128
|
+
"additionalProperties": false,
|
|
129
|
+
"required": ["type", "label"],
|
|
130
|
+
"properties": {
|
|
131
|
+
"type": { "const": "number" },
|
|
132
|
+
"label": true,
|
|
133
|
+
"description": true,
|
|
134
|
+
"default": { "type": "number" },
|
|
135
|
+
"min": { "type": "number" },
|
|
136
|
+
"max": { "type": "number" },
|
|
137
|
+
"step": { "type": "number", "exclusiveMinimum": 0, "default": 1 }
|
|
138
|
+
},
|
|
139
|
+
"description": "Decimal number input with optional bounds (a threshold like 0.3, a ratio, a confidence floor). Renders as PrimeNG `<p-inputnumber>` with `mode=\"decimal\"`. Value type at runtime: `number` (whole OR fractional accepted; pick `integer` instead when the value must be a whole number). Validation rejects `NaN` / `Infinity`."
|
|
140
|
+
},
|
|
123
141
|
"Setting_EnumPick": {
|
|
124
142
|
"allOf": [{ "$ref": "#/$defs/_Common" }],
|
|
125
143
|
"type": "object",
|
|
@@ -223,10 +241,10 @@
|
|
|
223
241
|
"envVar": {
|
|
224
242
|
"type": "string",
|
|
225
243
|
"pattern": "^[A-Z][A-Z0-9_]*$",
|
|
226
|
-
"description": "Optional env var name the kernel checks first; if set in the process environment, that value wins over any stored value
|
|
244
|
+
"description": "Optional env var name the kernel checks first; if set in the process environment, that value wins over any stored value, letting CI inject the secret without writing it to disk at all."
|
|
227
245
|
}
|
|
228
246
|
},
|
|
229
|
-
"description": "Sensitive string (token, password, API key). Renders as `<input type=\"password\">` with reveal toggle. Stored
|
|
247
|
+
"description": "Sensitive string (token, password, API key). Renders as `<input type=\"password\">` with reveal toggle. **Stored in project-local `settings.local.json` (gitignored), never in the committed `settings.json`**: the protection is that the value never travels via the shared repo, NOT encryption, it is kept as plain text on the local machine. The kernel routes any `secret`-typed setting to the project-local layer automatically, the dynamic equivalent of `PROJECT_LOCAL_ONLY_KEYS` (the destination follows the declared type, not a fixed key list), so `sm plugins config` writes a secret to `settings.local.json` even without an explicit local flag. Logged as `<redacted>` in CLI output. Value type at runtime: `string`."
|
|
230
248
|
},
|
|
231
249
|
"Setting_KeyValueList": {
|
|
232
250
|
"allOf": [{ "$ref": "#/$defs/_Common" }],
|
|
@@ -72,12 +72,27 @@
|
|
|
72
72
|
},
|
|
73
73
|
"plugins": {
|
|
74
74
|
"type": "object",
|
|
75
|
-
"description": "Per-plugin
|
|
75
|
+
"description": "Per-plugin overrides. Keys are plugin ids. Carries the enable/disable toggle and, under `extensions.<extId>.settings`, the operator-supplied values for the settings each extension declares in its manifest. Absent = installed defaults (enabled, declared setting defaults).",
|
|
76
76
|
"additionalProperties": {
|
|
77
77
|
"type": "object",
|
|
78
78
|
"additionalProperties": false,
|
|
79
79
|
"properties": {
|
|
80
|
-
"enabled": { "type": "boolean" }
|
|
80
|
+
"enabled": { "type": "boolean" },
|
|
81
|
+
"extensions": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"description": "Per-extension overrides, keyed by extension id (the leaf folder name, NOT the qualified `<plugin>/<ext>` id, the plugin is already the parent key). Today only `settings`; a future per-extension `enabled` filter (the deferred Phase 4+ work) would slot in alongside it.",
|
|
84
|
+
"additionalProperties": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"properties": {
|
|
88
|
+
"settings": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"description": "Operator-supplied values for the extension's declared settings, keyed by settingId. Values are intentionally NOT validated by this schema: the kernel's settings resolver validates each value against the per-type value schema of the input-type the manifest declares (`input-types.schema.json#/$defs/ISettingDeclaration`), since this schema cannot know which type a given settingId picked. A non-`secret` setting lands here in `settings.json` (team-shared) or `settings.local.json` (per-checkout override) via the normal config layering; `secret` settings are NOT stored here, they ride the dedicated encrypted `state_secrets` path (see `input-types.schema.json#/$defs/Setting_Secret`).",
|
|
91
|
+
"additionalProperties": true
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
81
96
|
}
|
|
82
97
|
}
|
|
83
98
|
},
|