@skill-map/cli 0.17.0 → 0.19.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/README.md +4 -0
- package/dist/cli/tutorial/sm-tutorial.md +7 -0
- package/dist/cli.js +12223 -6757
- package/dist/cli.js.map +1 -1
- package/dist/conformance/index.js +36 -14
- package/dist/conformance/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +776 -73
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +965 -134
- package/dist/kernel/index.js +776 -73
- package/dist/kernel/index.js.map +1 -1
- package/dist/migrations/001_initial.sql +122 -12
- package/dist/ui/chunk-3R7E3HPC.js +7 -0
- package/dist/ui/chunk-7CAK6MVK.js +2638 -0
- package/dist/ui/chunk-BORRASJB.js +247 -0
- package/dist/ui/chunk-CZSS4D6J.js +454 -0
- package/dist/ui/chunk-EQD7AYYJ.js +227 -0
- package/dist/ui/chunk-ETTRVTFV.js +1 -0
- package/dist/ui/chunk-LFIE4SCX.js +965 -0
- package/dist/ui/chunk-OKO3QOH6.js +1 -0
- package/dist/ui/chunk-PMIMYHBM.js +61 -0
- package/dist/ui/chunk-UHFGCO24.js +1 -0
- package/dist/ui/chunk-UJOZYR5I.js +1 -0
- package/dist/ui/chunk-VHIPW3TH.js +1 -0
- package/dist/ui/chunk-VWAUXWQX.js +237 -0
- package/dist/ui/index.html +10 -2
- package/dist/ui/main-BSYMJKTL.js +1 -0
- package/dist/ui/{styles-CBPFNGXA.css → styles-UAABA7VK.css} +1 -1
- package/migrations/001_initial.sql +122 -12
- package/package.json +6 -6
- package/dist/ui/chunk-5ZGVBIPP.js +0 -1031
- package/dist/ui/chunk-BWUDZKB6.js +0 -247
- package/dist/ui/chunk-LUDNWV6G.js +0 -3091
- package/dist/ui/chunk-WMWULWZX.js +0 -237
- package/dist/ui/main-7LR4JN4M.js +0 -1
package/dist/kernel/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* --- Naming convention (kernel-wide) -------------------------------------
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Five categories with distinct prefix rules; the rules are deliberate
|
|
12
12
|
* even though they look mixed at first read:
|
|
13
13
|
*
|
|
14
14
|
* 1. **Domain types** — every shape that mirrors a `spec/schemas/*.json`
|
|
@@ -31,13 +31,22 @@
|
|
|
31
31
|
* reading as the rest of TypeScript's plugin ecosystems where a
|
|
32
32
|
* shape is implementable.
|
|
33
33
|
*
|
|
34
|
-
* 4. **Internal
|
|
35
|
-
* slices, anything
|
|
36
|
-
* kernel / CLI but not part of the
|
|
37
|
-
* `
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
40
|
-
*
|
|
34
|
+
* 4. **Internal interfaces** — option bags, result records, config
|
|
35
|
+
* slices, anything declared as `interface` and passed across
|
|
36
|
+
* function boundaries inside the kernel / CLI but not part of the
|
|
37
|
+
* spec: `IPluginRuntimeBundle`, `IPruneResult`, `IMigrationFile`,
|
|
38
|
+
* `IDbLocationOptions`. **`I` prefix.** The prefix matches
|
|
39
|
+
* category 3 because both are "shapes that live in TypeScript
|
|
40
|
+
* only, never in JSON".
|
|
41
|
+
*
|
|
42
|
+
* 5. **Internal type aliases** — anything declared as `type` (string-
|
|
43
|
+
* literal unions, function types, mapped/derived types) that lives
|
|
44
|
+
* only in TS: `TLogLevel`, `TLogMethodLevel`, `TProgressListener`,
|
|
45
|
+
* `TLogFormatter`, `TActionWrite`, `TExecutionMode`, `TGranularity`,
|
|
46
|
+
* `THookFilter`, `THookTrigger`, `TNodeChangeReason`,
|
|
47
|
+
* `TPluginLoadStatus`, `TPluginStorage`, `TWatchEventKind`. **`T`
|
|
48
|
+
* prefix.** Use this bucket when `interface` is the wrong shape
|
|
49
|
+
* (a union, a callback signature, an `Exclude<…>` derivation).
|
|
41
50
|
*
|
|
42
51
|
* Edge cases worth knowing:
|
|
43
52
|
* - The following category-4 names lack the `I` prefix because
|
|
@@ -46,15 +55,16 @@
|
|
|
46
55
|
* option bags / records: `RunScanOptions`, `RenameOp`;
|
|
47
56
|
* TS-only exports from `kernel/index.ts` / `kernel/ports/*`:
|
|
48
57
|
* `Kernel`, `ProgressEvent`, `LogRecord`, `NodeStat`.
|
|
49
|
-
* New public option bags
|
|
50
|
-
* `
|
|
58
|
+
* New public option bags MUST still use `I*`; new public type
|
|
59
|
+
* aliases MUST still use `T*`. Removing a name from this list is a
|
|
60
|
+
* breaking change.
|
|
51
61
|
* - `IDatabase` (SQLite schema) is category 4 but lives in
|
|
52
62
|
* `adapters/sqlite/schema.ts`, not here. Same rule applies.
|
|
53
63
|
*
|
|
54
64
|
* If you find yourself wanting to add a new type and aren't sure which
|
|
55
65
|
* bucket it falls in: ask "does this shape exist in the spec?". If
|
|
56
|
-
* yes, no prefix and align the name with the schema. If no, `I`
|
|
57
|
-
* prefix.
|
|
66
|
+
* yes, no prefix and align the name with the schema. If no, `I` prefix
|
|
67
|
+
* for `interface`, `T` prefix for `type` aliases.
|
|
58
68
|
*/
|
|
59
69
|
/**
|
|
60
70
|
* The four node kinds the **built-in Claude Provider** declares — `skill`,
|
|
@@ -70,8 +80,12 @@
|
|
|
70
80
|
* Step 9.5 dropped `hook` from the catalog: `.claude/hooks/*.md` is NOT
|
|
71
81
|
* an Anthropic-defined node type — hooks live in `settings.json` or as
|
|
72
82
|
* sub-objects of agent / skill frontmatter (see
|
|
73
|
-
* https://code.claude.com/docs/en/hooks.md). Files at the old path
|
|
74
|
-
* classify as `
|
|
83
|
+
* https://code.claude.com/docs/en/hooks.md). Files at the old path
|
|
84
|
+
* classify as `markdown` via the Provider's fallback. The fallback is
|
|
85
|
+
* named after the *format* because the file is generic markdown with
|
|
86
|
+
* no specific role; format-named kinds apply only as the generic
|
|
87
|
+
* fallback — a file that matches a specific role (agent / command /
|
|
88
|
+
* skill) classifies under that role, not under `markdown`.
|
|
75
89
|
*
|
|
76
90
|
* This alias survives because:
|
|
77
91
|
* - claude-specific code legitimately wants to switch on the four
|
|
@@ -85,7 +99,7 @@
|
|
|
85
99
|
* For "any kind a Provider could declare", use plain `string`. Only use
|
|
86
100
|
* `NodeKind` when the code is intentionally claude-catalog-specific.
|
|
87
101
|
*/
|
|
88
|
-
type NodeKind = 'skill' | 'agent' | 'command' | '
|
|
102
|
+
type NodeKind = 'skill' | 'agent' | 'command' | 'markdown';
|
|
89
103
|
type LinkKind = 'invokes' | 'references' | 'mentions' | 'supersedes';
|
|
90
104
|
type Confidence = 'high' | 'medium' | 'low';
|
|
91
105
|
type Severity = 'error' | 'warn' | 'info';
|
|
@@ -137,13 +151,58 @@ interface Node {
|
|
|
137
151
|
linksOutCount: number;
|
|
138
152
|
linksInCount: number;
|
|
139
153
|
externalRefsCount: number;
|
|
140
|
-
title?: string | null;
|
|
141
|
-
description?: string | null;
|
|
142
|
-
stability?: Stability | null;
|
|
143
|
-
version?: string | null;
|
|
144
|
-
author?: string | null;
|
|
145
154
|
frontmatter?: Record<string, unknown>;
|
|
146
155
|
tokens?: TripleSplit;
|
|
156
|
+
/**
|
|
157
|
+
* Step 9.6.2 — sidecar denormalisation surface. Populated by the
|
|
158
|
+
* orchestrator at scan time; absent when the orchestrator did not
|
|
159
|
+
* inspect sidecars (legacy code paths) or when no sidecar accompanies
|
|
160
|
+
* the node. Read by `annotation-stale` rule and the persistence layer.
|
|
161
|
+
*/
|
|
162
|
+
sidecar?: ISidecarOverlay | null;
|
|
163
|
+
/**
|
|
164
|
+
* Per-user "favorite" flag, decorated by the BFF on `/api/nodes` and
|
|
165
|
+
* `/api/nodes/:pathB64` responses via in-memory `Set` lookup against
|
|
166
|
+
* `state_node_favorites`. Absent on emissions that don't carry per-user
|
|
167
|
+
* state (e.g. `sm export --json`); consumers that don't recognise the
|
|
168
|
+
* field MUST treat the absence as "unknown" rather than "false" — a
|
|
169
|
+
* truthy `isFavorite` only ever lands when the BFF set it.
|
|
170
|
+
*/
|
|
171
|
+
isFavorite?: boolean;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Drift status of a co-located `.sm` sidecar relative to the live
|
|
175
|
+
* node hashes. Mirrors `TSidecarStatus` on the SQLite schema.
|
|
176
|
+
*/
|
|
177
|
+
type SidecarStatus = 'fresh' | 'stale-body' | 'stale-frontmatter' | 'stale-both';
|
|
178
|
+
/**
|
|
179
|
+
* Sidecar overlay attached to a `Node` after the orchestrator parses
|
|
180
|
+
* `<basename>.sm`. `present === false` is the empty overlay (no
|
|
181
|
+
* sidecar accompanies the node); the other fields are absent or null
|
|
182
|
+
* in that case. When `present === true` and parse + validation
|
|
183
|
+
* succeeded, `status` carries the drift state and `annotations` carries
|
|
184
|
+
* the parsed (typed) `annotations:` block.
|
|
185
|
+
*/
|
|
186
|
+
interface ISidecarOverlay {
|
|
187
|
+
present: boolean;
|
|
188
|
+
status?: SidecarStatus | null;
|
|
189
|
+
/**
|
|
190
|
+
* Parsed `annotations:` block. Untyped object — schema lives in
|
|
191
|
+
* `spec/schemas/annotations.schema.json`. Null when no sidecar or
|
|
192
|
+
* the block is empty/absent.
|
|
193
|
+
*/
|
|
194
|
+
annotations?: Record<string, unknown> | null;
|
|
195
|
+
/**
|
|
196
|
+
* R15 closure (2026-05-07) — full parsed YAML root of the sidecar
|
|
197
|
+
* (the entire `.sm` payload, mirroring `sidecar.schema.json`). Surfaced
|
|
198
|
+
* so the UI inspector can render `for:`, `audit:`, `settings:`, and
|
|
199
|
+
* `<plugin-id>:` namespace blocks without re-reading the file. NULL
|
|
200
|
+
* when no sidecar is present, or when the sidecar exists but failed
|
|
201
|
+
* to parse / validate. The `annotations` field above stays — it
|
|
202
|
+
* duplicates `root.annotations` intentionally so existing consumers
|
|
203
|
+
* keep working unchanged.
|
|
204
|
+
*/
|
|
205
|
+
root?: Record<string, unknown> | null;
|
|
147
206
|
}
|
|
148
207
|
interface Link {
|
|
149
208
|
/** The originating node — the path of the file the extractor was reading
|
|
@@ -186,8 +245,8 @@ interface ScanStats {
|
|
|
186
245
|
/**
|
|
187
246
|
* Files walked but not classified by any Provider. Today every walked
|
|
188
247
|
* file is classified by its Provider (the `claude` Provider falls back to
|
|
189
|
-
* `'
|
|
190
|
-
* Providers can claim the same file.
|
|
248
|
+
* `'markdown'`), so this is always 0; the field will matter once
|
|
249
|
+
* multiple Providers can claim the same file.
|
|
191
250
|
*/
|
|
192
251
|
filesSkipped: number;
|
|
193
252
|
nodesCount: number;
|
|
@@ -316,7 +375,7 @@ interface ScanResult {
|
|
|
316
375
|
* and each kind's code carries its own fuller type where needed.
|
|
317
376
|
*
|
|
318
377
|
* **Spec § A.6 — qualified ids.** Every extension is keyed in the registry
|
|
319
|
-
* by `<pluginId>/<id>` (e.g. `core/
|
|
378
|
+
* by `<pluginId>/<id>` (e.g. `core/annotations`, `core/slash`,
|
|
320
379
|
* `hello-world/greet`). `Extension.id` carries the **short** id as authored;
|
|
321
380
|
* `Extension.pluginId` carries the namespace; the registry composes the
|
|
322
381
|
* qualifier internally and exposes lookup APIs that operate on either form
|
|
@@ -369,6 +428,333 @@ declare class Registry {
|
|
|
369
428
|
totalCount(): number;
|
|
370
429
|
}
|
|
371
430
|
|
|
431
|
+
/**
|
|
432
|
+
* Step 9.6.6 — runtime annotation-contribution catalog types.
|
|
433
|
+
*
|
|
434
|
+
* Lives in its own module (rather than `kernel/index.ts`) so consumers
|
|
435
|
+
* deep inside the kernel — `IRuleContext`, the BFF route factories,
|
|
436
|
+
* future Action contexts — can depend on the catalog shape without
|
|
437
|
+
* dragging the whole kernel barrel and risking a cycle.
|
|
438
|
+
*/
|
|
439
|
+
/**
|
|
440
|
+
* Single row of the runtime annotation-contribution catalog surfaced by
|
|
441
|
+
* `kernel.getRegisteredAnnotationKeys()`. One row per (plugin × key)
|
|
442
|
+
* tuple. Built-in catalog keys from `annotations.schema.json` are NOT
|
|
443
|
+
* included — this catalog is plugin-only; the UI knows the built-in
|
|
444
|
+
* catalog via the schema bundle.
|
|
445
|
+
*/
|
|
446
|
+
interface IRegisteredAnnotationKey {
|
|
447
|
+
pluginId: string;
|
|
448
|
+
key: string;
|
|
449
|
+
location: 'namespaced' | 'root';
|
|
450
|
+
ownership: 'exclusive' | 'shared';
|
|
451
|
+
/** Inline JSON Schema as declared in the manifest (not the AJV compiled validator). */
|
|
452
|
+
schema: Record<string, unknown>;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Step 11.x — runtime view-contribution catalog types.
|
|
457
|
+
*
|
|
458
|
+
* Lives in its own module (rather than `kernel/index.ts`) so consumers
|
|
459
|
+
* deep inside the kernel — `IRuleContext`, the BFF route factories,
|
|
460
|
+
* future Action contexts — can depend on the catalog shape without
|
|
461
|
+
* dragging the whole kernel barrel and risking a cycle.
|
|
462
|
+
*
|
|
463
|
+
* Mirrors `annotation-catalog.ts` for the annotation contribution side
|
|
464
|
+
* (Step 9.6.6). The two systems share the "plugin contributes data,
|
|
465
|
+
* kernel exposes catalog, UI renders" pattern but never overlap in
|
|
466
|
+
* storage or routing — see `architecture.md` §View contribution system
|
|
467
|
+
* for the comparison table.
|
|
468
|
+
*
|
|
469
|
+
* **Closed catalog by design.** Both `TContractName` and `TInputTypeName`
|
|
470
|
+
* mirror the closed enums in `spec/schemas/view-contracts.schema.json`
|
|
471
|
+
* and `spec/schemas/input-types.schema.json`. Adding a member is a
|
|
472
|
+
* coordinated kernel + spec + UI + scaffolder change. The closed-enum
|
|
473
|
+
* shape lets TypeScript surface unknown contracts at author time
|
|
474
|
+
* (in plugin authors' editors when their plugin imports `@skill-map/cli`)
|
|
475
|
+
* AND lets the runtime exhaustively dispatch contract → renderer in the
|
|
476
|
+
* UI without `default:` fallbacks.
|
|
477
|
+
*/
|
|
478
|
+
/**
|
|
479
|
+
* Closed enum of view contract names. Mirror of
|
|
480
|
+
* `spec/schemas/view-contracts.schema.json#/$defs/ContractName`.
|
|
481
|
+
*
|
|
482
|
+
* Plugins pick one of these by name in their extension manifest's
|
|
483
|
+
* `viewContributions[<contributionId>].contract` field. The kernel
|
|
484
|
+
* validates each pick at load time (`invalid-manifest` on miss); the
|
|
485
|
+
* UI maps each contract to one or more slots and a renderer.
|
|
486
|
+
*/
|
|
487
|
+
type TContractName = 'node-counter' | 'node-tag' | 'node-breakdown' | 'node-records' | 'node-tree' | 'node-key-values' | 'node-link-list' | 'node-markdown' | 'node-alert' | 'scope-stat';
|
|
488
|
+
/**
|
|
489
|
+
* Closed enum of input-type names for plugin settings. Mirror of
|
|
490
|
+
* `spec/schemas/input-types.schema.json#/$defs/InputTypeName`.
|
|
491
|
+
*
|
|
492
|
+
* Plugins pick one of these by name in their plugin manifest's
|
|
493
|
+
* `settings[<settingId>].type` field. The kernel exposes the resolved
|
|
494
|
+
* value via `ctx.settings.<settingId>` typed per the input-type's
|
|
495
|
+
* value-type promise.
|
|
496
|
+
*/
|
|
497
|
+
type TInputTypeName = 'string-list' | 'single-string' | 'boolean-flag' | 'integer' | 'enum-pick' | 'enum-multipick' | 'path-glob' | 'regex' | 'secret' | 'key-value-list';
|
|
498
|
+
/** Closed severity palette aligned with PrimeNG `<p-tag>` / `<p-message>`. */
|
|
499
|
+
type TSeverity = 'info' | 'warn' | 'success' | 'danger';
|
|
500
|
+
/**
|
|
501
|
+
* Manifest-side declaration of a single view contribution. The plugin
|
|
502
|
+
* author writes one of these per Record key in
|
|
503
|
+
* `IExtensionBase.viewContributions[<contributionId>]`.
|
|
504
|
+
*
|
|
505
|
+
* Mirror of `view-contracts.schema.json#/$defs/IViewContribution`.
|
|
506
|
+
*/
|
|
507
|
+
interface IViewContribution {
|
|
508
|
+
/**
|
|
509
|
+
* Required. Closed-catalog contract name. Unknown name rejects the
|
|
510
|
+
* extension as `invalid-manifest` at load.
|
|
511
|
+
*/
|
|
512
|
+
contract: TContractName;
|
|
513
|
+
/**
|
|
514
|
+
* Optional human-readable label. English-only per `AGENTS.md`
|
|
515
|
+
* (`Externalized texts, not internationalized`).
|
|
516
|
+
*/
|
|
517
|
+
label?: string;
|
|
518
|
+
/** Optional hover tooltip. English-only. */
|
|
519
|
+
tooltip?: string;
|
|
520
|
+
/**
|
|
521
|
+
* Optional emoji codepoint OR PrimeIcons class id (without the
|
|
522
|
+
* `pi-` prefix). The UI discriminates: matches Unicode
|
|
523
|
+
* `\p{Extended_Pictographic}` → emoji text, otherwise → PrimeIcon.
|
|
524
|
+
*/
|
|
525
|
+
icon?: string;
|
|
526
|
+
/**
|
|
527
|
+
* Optional empty placeholder text shown when the payload is empty
|
|
528
|
+
* AND `emitWhenEmpty` is true. Falls back to a UI-supplied generic
|
|
529
|
+
* 'No data.' string. English-only.
|
|
530
|
+
*/
|
|
531
|
+
emptyText?: string;
|
|
532
|
+
/**
|
|
533
|
+
* When false (default), the kernel drops emissions whose payload is
|
|
534
|
+
* structurally empty so the slot stays silent. When true, the
|
|
535
|
+
* renderer surfaces an empty placeholder. Per-contract definition
|
|
536
|
+
* of "empty" lives in the contract's payload schema.
|
|
537
|
+
*/
|
|
538
|
+
emitWhenEmpty?: boolean;
|
|
539
|
+
/**
|
|
540
|
+
* Optional ordering hint (default 100). Slots configured with
|
|
541
|
+
* `order: 'priority'` sort contributions ASC by this value, with
|
|
542
|
+
* alphabetical tie-break by qualified id. The plugin uses this to
|
|
543
|
+
* suggest where its contribution belongs relative to others sharing
|
|
544
|
+
* the same slot — the slot has the final say.
|
|
545
|
+
*/
|
|
546
|
+
priority?: number;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Single row of the runtime view-contribution catalog surfaced by
|
|
550
|
+
* `kernel.getRegisteredViewContributions()`. One row per
|
|
551
|
+
* `(pluginId × extensionId × contributionId)` tuple. Composed at boot
|
|
552
|
+
* by `loadPluginRuntime` from every loaded extension's
|
|
553
|
+
* `viewContributions` map.
|
|
554
|
+
*
|
|
555
|
+
* The qualified id is `<pluginId>/<extensionId>/<contributionId>` —
|
|
556
|
+
* matches the qualified id pattern used elsewhere in the kernel
|
|
557
|
+
* (`<pluginId>/<extensionId>` for extensions; this adds the third
|
|
558
|
+
* segment for per-contribution identity).
|
|
559
|
+
*/
|
|
560
|
+
interface IRegisteredViewContribution {
|
|
561
|
+
pluginId: string;
|
|
562
|
+
extensionId: string;
|
|
563
|
+
contributionId: string;
|
|
564
|
+
contract: TContractName;
|
|
565
|
+
/** Optional manifest-declared label (English-only). */
|
|
566
|
+
label?: string;
|
|
567
|
+
tooltip?: string;
|
|
568
|
+
icon?: string;
|
|
569
|
+
emptyText?: string;
|
|
570
|
+
emitWhenEmpty: boolean;
|
|
571
|
+
/** Manifest-declared ordering hint (default 100). See `IViewContribution.priority`. */
|
|
572
|
+
priority?: number;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Common fields on every setting declaration. The discriminated union
|
|
576
|
+
* `ISettingDeclaration` extends one of these per `type` value.
|
|
577
|
+
*/
|
|
578
|
+
interface ISettingCommon {
|
|
579
|
+
/** Required. Short human-readable label. English-only. */
|
|
580
|
+
label: string;
|
|
581
|
+
/** Optional helper text shown below the control. English-only. */
|
|
582
|
+
description?: string;
|
|
583
|
+
}
|
|
584
|
+
interface ISetting_StringList extends ISettingCommon {
|
|
585
|
+
type: 'string-list';
|
|
586
|
+
default?: string[];
|
|
587
|
+
min?: number;
|
|
588
|
+
max?: number;
|
|
589
|
+
itemMaxLength?: number;
|
|
590
|
+
}
|
|
591
|
+
interface ISetting_SingleString extends ISettingCommon {
|
|
592
|
+
type: 'single-string';
|
|
593
|
+
default?: string;
|
|
594
|
+
minLength?: number;
|
|
595
|
+
maxLength?: number;
|
|
596
|
+
/** Optional ECMAScript regex pattern (no flags). */
|
|
597
|
+
pattern?: string;
|
|
598
|
+
}
|
|
599
|
+
interface ISetting_BooleanFlag extends ISettingCommon {
|
|
600
|
+
type: 'boolean-flag';
|
|
601
|
+
default?: boolean;
|
|
602
|
+
}
|
|
603
|
+
interface ISetting_Integer extends ISettingCommon {
|
|
604
|
+
type: 'integer';
|
|
605
|
+
default?: number;
|
|
606
|
+
min?: number;
|
|
607
|
+
max?: number;
|
|
608
|
+
step?: number;
|
|
609
|
+
}
|
|
610
|
+
interface ISetting_EnumOption {
|
|
611
|
+
value: string;
|
|
612
|
+
label: string;
|
|
613
|
+
}
|
|
614
|
+
interface ISetting_EnumPick extends ISettingCommon {
|
|
615
|
+
type: 'enum-pick';
|
|
616
|
+
options: ISetting_EnumOption[];
|
|
617
|
+
default?: string;
|
|
618
|
+
}
|
|
619
|
+
interface ISetting_EnumMultipick extends ISettingCommon {
|
|
620
|
+
type: 'enum-multipick';
|
|
621
|
+
options: ISetting_EnumOption[];
|
|
622
|
+
default?: string[];
|
|
623
|
+
min?: number;
|
|
624
|
+
max?: number;
|
|
625
|
+
}
|
|
626
|
+
interface ISetting_PathGlob extends ISettingCommon {
|
|
627
|
+
type: 'path-glob';
|
|
628
|
+
default?: string;
|
|
629
|
+
/** When true, accepts string[]; when false (default), single string. */
|
|
630
|
+
multiple?: boolean;
|
|
631
|
+
}
|
|
632
|
+
interface ISetting_Regex extends ISettingCommon {
|
|
633
|
+
type: 'regex';
|
|
634
|
+
default?: string;
|
|
635
|
+
/** Subset of `gimsuy`. Default `''`. */
|
|
636
|
+
flags?: string;
|
|
637
|
+
}
|
|
638
|
+
interface ISetting_Secret extends ISettingCommon {
|
|
639
|
+
type: 'secret';
|
|
640
|
+
/**
|
|
641
|
+
* Optional uppercase-ASCII identifier. When set in the process
|
|
642
|
+
* environment, that value wins over any stored value (lets CI
|
|
643
|
+
* inject without writing to disk).
|
|
644
|
+
*/
|
|
645
|
+
envVar?: string;
|
|
646
|
+
}
|
|
647
|
+
interface ISetting_KeyValueListEntry {
|
|
648
|
+
key: string;
|
|
649
|
+
value: string;
|
|
650
|
+
}
|
|
651
|
+
interface ISetting_KeyValueList extends ISettingCommon {
|
|
652
|
+
type: 'key-value-list';
|
|
653
|
+
keyLabel?: string;
|
|
654
|
+
valueLabel?: string;
|
|
655
|
+
default?: ISetting_KeyValueListEntry[];
|
|
656
|
+
min?: number;
|
|
657
|
+
max?: number;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Discriminated union of every setting declaration shape. The plugin
|
|
661
|
+
* author NEVER writes JSON Schema for settings — they pick one of
|
|
662
|
+
* these `type` values and supply per-type parameters.
|
|
663
|
+
*
|
|
664
|
+
* Mirror of `input-types.schema.json#/$defs/ISettingDeclaration`.
|
|
665
|
+
*/
|
|
666
|
+
type ISettingDeclaration = ISetting_StringList | ISetting_SingleString | ISetting_BooleanFlag | ISetting_Integer | ISetting_EnumPick | ISetting_EnumMultipick | ISetting_PathGlob | ISetting_Regex | ISetting_Secret | ISetting_KeyValueList;
|
|
667
|
+
/**
|
|
668
|
+
* Runtime value type for a setting, derived from its declaration. The
|
|
669
|
+
* kernel exposes settings to extractors as `Record<string, TSettingValue>`
|
|
670
|
+
* via `ctx.settings.<settingId>`; consumers that want narrow typing
|
|
671
|
+
* narrow at the call site by reading `manifest.settings[id].type`.
|
|
672
|
+
*/
|
|
673
|
+
type TSettingValue = string | string[] | boolean | number | ISetting_KeyValueListEntry[];
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Base manifest shape shared by every extension kind. Mirrors
|
|
677
|
+
* `spec/schemas/extensions/base.schema.json` at the TypeScript level.
|
|
678
|
+
*
|
|
679
|
+
* Spec § A.6 — every extension is identified in the registry by the
|
|
680
|
+
* qualified id `<pluginId>/<id>`. The `pluginId` field is required at the
|
|
681
|
+
* runtime / TS level: built-ins declare it directly in
|
|
682
|
+
* `src/extensions/built-ins.ts`; user plugins have it injected by the
|
|
683
|
+
* `PluginLoader` from `plugin.json#/id` before the extension reaches the
|
|
684
|
+
* registry. A plugin author who hand-codes a `pluginId` that disagrees
|
|
685
|
+
* with the manifest's `id` is rejected as `invalid-manifest`.
|
|
686
|
+
*
|
|
687
|
+
* The JSON Schema deliberately does NOT model `pluginId` — the qualifier
|
|
688
|
+
* is a runtime concern composed by the loader, not a manifest field
|
|
689
|
+
* authors are expected to set. Stripping it before AJV validation in
|
|
690
|
+
* the loader keeps the spec contract clean ("authors declare only the
|
|
691
|
+
* short id").
|
|
692
|
+
*/
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Step 9.6.6 — single entry of an extension's `annotationContributions`
|
|
696
|
+
* map. Mirrors `spec/schemas/extensions/base.schema.json#/properties/annotationContributions/additionalProperties`.
|
|
697
|
+
*
|
|
698
|
+
* `schema` is an INLINE JSON Schema (object literal in the manifest),
|
|
699
|
+
* not a `$ref` to a file. The kernel compiles it at load time; an
|
|
700
|
+
* invalid schema rejects the extension as `invalid-manifest`.
|
|
701
|
+
*/
|
|
702
|
+
interface IAnnotationContribution {
|
|
703
|
+
/** Inline JSON Schema describing the value written under this key. */
|
|
704
|
+
schema: Record<string, unknown>;
|
|
705
|
+
/**
|
|
706
|
+
* Conflict policy. `shared` (default) — multiple plugins MAY write
|
|
707
|
+
* the key; `exclusive` — only this plugin may. REQUIRED to be
|
|
708
|
+
* `'exclusive'` when `location: 'root'`.
|
|
709
|
+
*/
|
|
710
|
+
ownership?: 'exclusive' | 'shared';
|
|
711
|
+
/**
|
|
712
|
+
* Where the key lands. `namespaced` (default) — under the plugin's
|
|
713
|
+
* `<plugin-id>:` block; `root` — top-level, alongside `for` /
|
|
714
|
+
* `annotations` / `settings` / `audit`. Cross-plugin root-key
|
|
715
|
+
* collisions on `exclusive` are a fatal startup error.
|
|
716
|
+
*/
|
|
717
|
+
location?: 'namespaced' | 'root';
|
|
718
|
+
}
|
|
719
|
+
interface IExtensionBase {
|
|
720
|
+
id: string;
|
|
721
|
+
/**
|
|
722
|
+
* Owning plugin namespace. Composed with `id` to produce the
|
|
723
|
+
* qualified registry key `<pluginId>/<id>`. Built-ins declare this
|
|
724
|
+
* directly; user plugins have it injected by the `PluginLoader`
|
|
725
|
+
* from `plugin.json#/id`.
|
|
726
|
+
*/
|
|
727
|
+
pluginId: string;
|
|
728
|
+
version: string;
|
|
729
|
+
description?: string;
|
|
730
|
+
stability?: Stability;
|
|
731
|
+
preconditions?: string[];
|
|
732
|
+
entry?: string;
|
|
733
|
+
/**
|
|
734
|
+
* Step 9.6.6 — plugin-contributed annotation keys. Each entry maps a
|
|
735
|
+
* key name to an inline JSON Schema + ownership + location triple.
|
|
736
|
+
* The kernel surfaces the aggregate via `kernel.getRegisteredAnnotationKeys()`.
|
|
737
|
+
* See `IAnnotationContribution` for the field semantics and
|
|
738
|
+
* `plugin-author-guide.md` §Annotation contributions for examples.
|
|
739
|
+
*/
|
|
740
|
+
annotationContributions?: Record<string, IAnnotationContribution>;
|
|
741
|
+
/**
|
|
742
|
+
* Plugin-contributed view contributions. Each entry maps a local
|
|
743
|
+
* contribution id (kebab-case, unique within the extension) to a
|
|
744
|
+
* `IViewContribution` declaration that picks a view contract by name
|
|
745
|
+
* from the closed kernel catalog (`view-catalog.ts#TContractName`).
|
|
746
|
+
* The kernel validates each `contract` pick at load time
|
|
747
|
+
* (`invalid-manifest` on miss); the plugin emits per-node payloads
|
|
748
|
+
* via `ctx.emitContribution(<contributionId>, payload)` during scan;
|
|
749
|
+
* the runtime validates payloads against the contract's payload
|
|
750
|
+
* schema. The aggregate runtime catalog is exposed via
|
|
751
|
+
* `kernel.getRegisteredViewContributions()`. The plugin author
|
|
752
|
+
* NEVER picks a UI slot — slot mapping is owned by the UI driving
|
|
753
|
+
* adapter. See `architecture.md` §View contribution system.
|
|
754
|
+
*/
|
|
755
|
+
viewContributions?: Record<string, IViewContribution>;
|
|
756
|
+
}
|
|
757
|
+
|
|
372
758
|
/**
|
|
373
759
|
* `.skillmapignore` parser + filter facade. Wraps `ignore` (kaelzhang)
|
|
374
760
|
* with the project-local layering: bundled defaults → `config.ignore`
|
|
@@ -412,10 +798,10 @@ interface ProgressEvent {
|
|
|
412
798
|
jobId?: string;
|
|
413
799
|
data?: unknown;
|
|
414
800
|
}
|
|
415
|
-
type
|
|
801
|
+
type TProgressListener = (event: ProgressEvent) => void;
|
|
416
802
|
interface ProgressEmitterPort {
|
|
417
803
|
emit(event: ProgressEvent): void;
|
|
418
|
-
subscribe(listener:
|
|
804
|
+
subscribe(listener: TProgressListener): () => void;
|
|
419
805
|
}
|
|
420
806
|
|
|
421
807
|
/**
|
|
@@ -481,6 +867,16 @@ interface IPluginManifest {
|
|
|
481
867
|
id: string;
|
|
482
868
|
version: string;
|
|
483
869
|
specCompat: string;
|
|
870
|
+
/**
|
|
871
|
+
* Optional semver range against the kernel's view-contracts +
|
|
872
|
+
* input-types catalog version. Independent from `specCompat` because
|
|
873
|
+
* the catalog evolves on its own cadence (see `architecture.md`
|
|
874
|
+
* §View contribution system → Catalog versioning). Mismatch surfaces
|
|
875
|
+
* as `incompatible-catalog`. Absent = the plugin opts out of catalog
|
|
876
|
+
* checking; `sm plugins doctor` warns if such a plugin actually
|
|
877
|
+
* declares `viewContributions` or `settings`.
|
|
878
|
+
*/
|
|
879
|
+
catalogCompat?: string;
|
|
484
880
|
extensions: string[];
|
|
485
881
|
description?: string;
|
|
486
882
|
storage?: TPluginStorage;
|
|
@@ -490,6 +886,18 @@ interface IPluginManifest {
|
|
|
490
886
|
* the default.
|
|
491
887
|
*/
|
|
492
888
|
granularity?: TGranularity;
|
|
889
|
+
/**
|
|
890
|
+
* Plugin user-configurable settings. Each entry picks an `input-type`
|
|
891
|
+
* from the closed catalog at
|
|
892
|
+
* `spec/schemas/input-types.schema.json#/$defs/InputTypeName`.
|
|
893
|
+
* The plugin author NEVER writes JSON Schema — they pick `type` by
|
|
894
|
+
* name and supply per-type parameters. The kernel exposes resolved
|
|
895
|
+
* settings to extractors via `ctx.settings.<settingId>`; settings
|
|
896
|
+
* are read once at extractor invocation; changing a setting requires
|
|
897
|
+
* `sm scan` to re-emit. See `architecture.md` §View contribution
|
|
898
|
+
* system → Settings.
|
|
899
|
+
*/
|
|
900
|
+
settings?: Record<string, ISettingDeclaration>;
|
|
493
901
|
author?: string;
|
|
494
902
|
license?: string;
|
|
495
903
|
homepage?: string;
|
|
@@ -530,7 +938,7 @@ interface IPluginManifest {
|
|
|
530
938
|
* precedence rule applies. The user resolves
|
|
531
939
|
* by renaming one of them and rerunning.
|
|
532
940
|
*/
|
|
533
|
-
type TPluginLoadStatus = 'enabled' | 'disabled' | 'incompatible-spec' | 'invalid-manifest' | 'load-error' | 'id-collision';
|
|
941
|
+
type TPluginLoadStatus = 'enabled' | 'disabled' | 'incompatible-spec' | 'incompatible-catalog' | 'invalid-manifest' | 'load-error' | 'id-collision';
|
|
534
942
|
interface ILoadedExtension {
|
|
535
943
|
kind: ExtensionKind;
|
|
536
944
|
id: string;
|
|
@@ -723,38 +1131,60 @@ declare function makePluginStore(opts: {
|
|
|
723
1131
|
}): IPluginStore | undefined;
|
|
724
1132
|
|
|
725
1133
|
/**
|
|
726
|
-
*
|
|
727
|
-
* `
|
|
1134
|
+
* `scan_contributions` adapter — replace-all writer used by
|
|
1135
|
+
* `persistScanResult`, plus read helpers consumed by the BFF
|
|
1136
|
+
* (`/api/contributions/...`) and rules (`core/contribution-orphan`).
|
|
728
1137
|
*
|
|
729
|
-
*
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
* `PluginLoader` from `plugin.json#/id` before the extension reaches the
|
|
734
|
-
* registry. A plugin author who hand-codes a `pluginId` that disagrees
|
|
735
|
-
* with the manifest's `id` is rejected as `invalid-manifest`.
|
|
1138
|
+
* One row per `(plugin_id, extension_id, node_path, contribution_id)`
|
|
1139
|
+
* tuple. See `spec/architecture.md` § View contribution system →
|
|
1140
|
+
* Persistence and `migrations/001_initial.sql` § View contribution
|
|
1141
|
+
* layer for the normative shape.
|
|
736
1142
|
*
|
|
737
|
-
*
|
|
738
|
-
* is a
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
*
|
|
1143
|
+
* Replace-all semantics mirror the rest of the `scan_*` zone: every
|
|
1144
|
+
* scan is a fresh snapshot, so prior rows are deleted before insert.
|
|
1145
|
+
* Wrapped in the same transaction `persistScanResult` opens.
|
|
1146
|
+
*
|
|
1147
|
+
* The rename heuristic does NOT need to migrate `node_path` here —
|
|
1148
|
+
* because of replace-all, every contribution is re-emitted on the new
|
|
1149
|
+
* path automatically. Keeping the rename path lighter than `state_*`
|
|
1150
|
+
* (which IS rename-migrated because state survives across scans).
|
|
742
1151
|
*/
|
|
743
1152
|
|
|
744
|
-
|
|
745
|
-
|
|
1153
|
+
/**
|
|
1154
|
+
* In-memory contribution record buffered during scan and flushed to
|
|
1155
|
+
* `scan_contributions` by `persistScanResult`. One entry per accepted
|
|
1156
|
+
* `ctx.emitContribution(id, payload)` call. Payload validation against
|
|
1157
|
+
* the contract's payload schema happens at emit time (orchestrator);
|
|
1158
|
+
* by the time records reach this adapter they are wire-shape clean.
|
|
1159
|
+
*/
|
|
1160
|
+
interface IContributionRecord {
|
|
1161
|
+
pluginId: string;
|
|
1162
|
+
extensionId: string;
|
|
1163
|
+
nodePath: string;
|
|
1164
|
+
contributionId: string;
|
|
746
1165
|
/**
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
* directly; user plugins have it injected by the `PluginLoader`
|
|
750
|
-
* from `plugin.json#/id`.
|
|
1166
|
+
* Closed enum value mirroring `view-contracts.schema.json#/$defs/ContractName`.
|
|
1167
|
+
* Persisted as TEXT (no SQL CHECK by design — see migration comment).
|
|
751
1168
|
*/
|
|
1169
|
+
contract: string;
|
|
1170
|
+
/** Already-validated payload. Serialised via `JSON.stringify` at write. */
|
|
1171
|
+
payload: unknown;
|
|
1172
|
+
emittedAt: number;
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Single contribution row as returned to callers. The payload is
|
|
1176
|
+
* `unknown` because the contract space is open at the type layer
|
|
1177
|
+
* (catalog evolution is a kernel + spec concern); narrow at the call
|
|
1178
|
+
* site by reading `contract`.
|
|
1179
|
+
*/
|
|
1180
|
+
interface IPersistedContribution {
|
|
752
1181
|
pluginId: string;
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1182
|
+
extensionId: string;
|
|
1183
|
+
nodePath: string;
|
|
1184
|
+
contributionId: string;
|
|
1185
|
+
contract: string;
|
|
1186
|
+
payload: unknown;
|
|
1187
|
+
emittedAt: number;
|
|
758
1188
|
}
|
|
759
1189
|
|
|
760
1190
|
/**
|
|
@@ -882,7 +1312,7 @@ interface IProviderKindUi {
|
|
|
882
1312
|
* `emoji`; when both are absent, the UI falls back to the first
|
|
883
1313
|
* letter of `label` colored with `color`.
|
|
884
1314
|
*/
|
|
885
|
-
icon?:
|
|
1315
|
+
icon?: TProviderKindIcon;
|
|
886
1316
|
}
|
|
887
1317
|
/**
|
|
888
1318
|
* Discriminated icon contract. `pi` references a PrimeIcons identifier
|
|
@@ -891,7 +1321,7 @@ interface IProviderKindUi {
|
|
|
891
1321
|
* `currentColor`. The discriminator (`kind`) keeps the UI dispatch
|
|
892
1322
|
* exhaustive without string-sniffing the payload.
|
|
893
1323
|
*/
|
|
894
|
-
type
|
|
1324
|
+
type TProviderKindIcon = {
|
|
895
1325
|
kind: 'pi';
|
|
896
1326
|
id: string;
|
|
897
1327
|
} | {
|
|
@@ -942,6 +1372,30 @@ interface IProvider extends IExtensionBase {
|
|
|
942
1372
|
* concern of how the runtime composes those.
|
|
943
1373
|
*/
|
|
944
1374
|
schemas?: unknown[];
|
|
1375
|
+
/**
|
|
1376
|
+
* Declarative file-discovery config consumed by the kernel walker.
|
|
1377
|
+
* When present, the kernel walks every root, includes files whose
|
|
1378
|
+
* extension matches `extensions`, parses each with the parser id
|
|
1379
|
+
* registered in the kernel-internal registry, and yields `IRawNode`
|
|
1380
|
+
* records the same shape `walk()` would.
|
|
1381
|
+
*
|
|
1382
|
+
* When neither `read` nor `walk` is declared, `resolveProviderWalk`
|
|
1383
|
+
* applies the default `{ extensions: ['.md'], parser: 'frontmatter-yaml' }`
|
|
1384
|
+
* so the most common Provider shape needs zero configuration.
|
|
1385
|
+
*
|
|
1386
|
+
* Precedence: when both `walk()` (runtime field) and `read` are
|
|
1387
|
+
* declared, `walk()` wins — `read` is ignored. The escape-hatch
|
|
1388
|
+
* relationship is intentional: most Providers should use `read`;
|
|
1389
|
+
* Providers with non-standard discovery requirements (custom file
|
|
1390
|
+
* naming, multi-pass walks, dynamic ignore logic) implement `walk()`
|
|
1391
|
+
* directly and accept the duplication of audit-cleared defences.
|
|
1392
|
+
*
|
|
1393
|
+
* Built-in parsers: `'frontmatter-yaml'` (markdown with `--- … ---`
|
|
1394
|
+
* YAML frontmatter; pollution-strip + JSON_SCHEMA-pinned), `'plain'`
|
|
1395
|
+
* (entire body, empty frontmatter). The set is closed; user plugins
|
|
1396
|
+
* cannot register their own.
|
|
1397
|
+
*/
|
|
1398
|
+
read?: IProviderReadConfig;
|
|
945
1399
|
/**
|
|
946
1400
|
* Walk the given roots and yield every node the Provider recognises.
|
|
947
1401
|
* Non-matching files are silently skipped. Unreadable files produce
|
|
@@ -952,22 +1406,52 @@ interface IProvider extends IExtensionBase {
|
|
|
952
1406
|
* filter reports as ignored. Providers MAY also keep their own
|
|
953
1407
|
* hard-coded skip list (e.g. `.git`) as a defensive measure, but the
|
|
954
1408
|
* filter is the canonical source of user intent.
|
|
1409
|
+
*
|
|
1410
|
+
* Optional. When omitted, the Provider MUST declare `read` (or rely
|
|
1411
|
+
* on the default config). The orchestrator never calls `walk()`
|
|
1412
|
+
* directly — it goes through `resolveProviderWalk(provider)` which
|
|
1413
|
+
* picks `walk` over `read`.
|
|
955
1414
|
*/
|
|
956
|
-
walk(roots: string[], options?: {
|
|
1415
|
+
walk?(roots: string[], options?: {
|
|
957
1416
|
ignoreFilter?: IIgnoreFilter;
|
|
958
1417
|
}): AsyncIterable<IRawNode>;
|
|
959
1418
|
/**
|
|
960
|
-
* Given a path and its parsed frontmatter, decide the node kind
|
|
961
|
-
* classifier is called after walk()
|
|
962
|
-
*
|
|
963
|
-
*
|
|
1419
|
+
* Given a path and its parsed frontmatter, decide the node kind — or
|
|
1420
|
+
* `null` to disclaim the file. The classifier is called after walk()
|
|
1421
|
+
* yields; with multiple Providers active, every Provider walks every
|
|
1422
|
+
* file matching its `read.extensions`, so each Provider MUST disclaim
|
|
1423
|
+
* paths it does not recognise. Returning the same path's kind from
|
|
1424
|
+
* two Providers fires the spec's `provider-ambiguous` issue and the
|
|
1425
|
+
* orchestrator drops the duplicate.
|
|
964
1426
|
*
|
|
965
|
-
*
|
|
966
|
-
*
|
|
967
|
-
*
|
|
968
|
-
* …)
|
|
1427
|
+
* Convention: a Provider's classify returns one of its own `kinds`
|
|
1428
|
+
* map keys for paths in its territory (`.claude/`, `.gemini/`,
|
|
1429
|
+
* `.agents/skills/`, etc.) and `null` elsewhere. External Providers
|
|
1430
|
+
* (Cursor, Obsidian, …) follow the same rule: claim what's yours,
|
|
1431
|
+
* disclaim everything else. The orchestrator does not validate the
|
|
1432
|
+
* kind against `NodeKind`.
|
|
1433
|
+
*/
|
|
1434
|
+
classify(path: string, frontmatter: Record<string, unknown>): string | null;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Declarative read config a Provider declares via `IProvider.read`.
|
|
1438
|
+
* Mirrors `extensions/provider.schema.json#/properties/read` at the
|
|
1439
|
+
* TypeScript level. Built-in parser ids: `'frontmatter-yaml'`, `'plain'`.
|
|
1440
|
+
*/
|
|
1441
|
+
interface IProviderReadConfig {
|
|
1442
|
+
/**
|
|
1443
|
+
* File extensions the walker yields. Strings include the leading dot
|
|
1444
|
+
* (e.g. `'.md'`, `'.mdc'`, `'.toml'`). Match is suffix-based; the
|
|
1445
|
+
* comparison is case-sensitive.
|
|
1446
|
+
*/
|
|
1447
|
+
extensions: string[];
|
|
1448
|
+
/**
|
|
1449
|
+
* Parser id from the kernel-internal registry. Built-ins:
|
|
1450
|
+
* `'frontmatter-yaml'`, `'plain'`. Unknown ids surface as
|
|
1451
|
+
* `UnknownParserError` from the walker; the orchestrator translates
|
|
1452
|
+
* the error into a Provider issue with status `invalid-manifest`.
|
|
969
1453
|
*/
|
|
970
|
-
|
|
1454
|
+
parser: string;
|
|
971
1455
|
}
|
|
972
1456
|
|
|
973
1457
|
/**
|
|
@@ -976,6 +1460,11 @@ interface IProvider extends IExtensionBase {
|
|
|
976
1460
|
* a return value. Extractors run in isolation: they MUST NOT read other
|
|
977
1461
|
* nodes, the graph, or the DB. Cross-node reasoning lives in rules.
|
|
978
1462
|
*
|
|
1463
|
+
* Extractors are deterministic-only. They run synchronously inside the
|
|
1464
|
+
* scan loop; LLM-driven enrichment of a node is an Action concern, not
|
|
1465
|
+
* an Extractor concern. The Extractor context therefore exposes no
|
|
1466
|
+
* `RunnerPort` — see spec `architecture.md` §Execution modes.
|
|
1467
|
+
*
|
|
979
1468
|
* Output channels (all on the context):
|
|
980
1469
|
*
|
|
981
1470
|
* - `ctx.emitLink(link)` — persist a link in the kernel's `links` table.
|
|
@@ -983,14 +1472,12 @@ interface IProvider extends IExtensionBase {
|
|
|
983
1472
|
* kind drops the link and surfaces an `extension.error` event.
|
|
984
1473
|
* - `ctx.enrichNode(partial)` — merge canonical, kernel-curated properties
|
|
985
1474
|
* onto the node. Strictly separate from the author-supplied frontmatter
|
|
986
|
-
* (the latter remains immutable and survives verbatim). Persistence
|
|
987
|
-
*
|
|
1475
|
+
* (the latter remains immutable and survives verbatim). Persistence
|
|
1476
|
+
* is spec'd in § A.8.
|
|
988
1477
|
* - `ctx.store` — plugin-scoped persistence. Present only when the
|
|
989
1478
|
* plugin declares `storage.mode` in `plugin.json`; shape depends on the
|
|
990
1479
|
* mode (`KvStore` for mode A, scoped `Database` for mode B). See
|
|
991
1480
|
* `plugin-kv-api.md` for the contract.
|
|
992
|
-
* - `ctx.runner` — `RunnerPort` injection for `probabilistic` extractors.
|
|
993
|
-
* `undefined` for the default `deterministic` mode.
|
|
994
1481
|
*
|
|
995
1482
|
* The manifest's `scope` field tells the orchestrator which parts to feed:
|
|
996
1483
|
* `frontmatter` extractors receive an empty string for body and vice versa.
|
|
@@ -1021,6 +1508,21 @@ interface IExtractorCallbacks {
|
|
|
1021
1508
|
* partials and `persistScanResult` upserts them.
|
|
1022
1509
|
*/
|
|
1023
1510
|
enrichNode(partial: Partial<Node>): void;
|
|
1511
|
+
/**
|
|
1512
|
+
* Emit a per-node view contribution. The first argument is the
|
|
1513
|
+
* extension-local Record key declared under
|
|
1514
|
+
* `extension.viewContributions[<contributionId>]`; the second is a
|
|
1515
|
+
* payload that conforms to the contract's payload schema in
|
|
1516
|
+
* `spec/schemas/view-contracts.schema.json#/$defs/payloads/<contract>`.
|
|
1517
|
+
* The orchestrator validates the payload against the contract schema
|
|
1518
|
+
* before persisting to `scan_contributions`; off-contract payloads
|
|
1519
|
+
* are silently dropped with an `extension.error` event (mirror of
|
|
1520
|
+
* `emitLink` rejecting off-`emitsLinkKinds` links). Calling
|
|
1521
|
+
* `emitContribution` with a `contributionId` that is not declared in
|
|
1522
|
+
* the manifest is also dropped with an `extension.error`. See
|
|
1523
|
+
* `architecture.md` §View contribution system → Emit path.
|
|
1524
|
+
*/
|
|
1525
|
+
emitContribution(contributionId: string, payload: unknown): void;
|
|
1024
1526
|
}
|
|
1025
1527
|
interface IExtractorContext extends IExtractorCallbacks {
|
|
1026
1528
|
node: Node;
|
|
@@ -1041,33 +1543,17 @@ interface IExtractorContext extends IExtractorCallbacks {
|
|
|
1041
1543
|
* it here.
|
|
1042
1544
|
*/
|
|
1043
1545
|
store?: unknown;
|
|
1044
|
-
/**
|
|
1045
|
-
* `RunnerPort` injection for `probabilistic` extractors. `undefined`
|
|
1046
|
-
* for `deterministic` mode (the default). The kernel rejects
|
|
1047
|
-
* probabilistic extractors that try to register scan-time hooks at
|
|
1048
|
-
* load time.
|
|
1049
|
-
*/
|
|
1050
|
-
runner?: unknown;
|
|
1051
1546
|
}
|
|
1052
1547
|
interface IExtractor extends IExtensionBase {
|
|
1053
1548
|
kind: 'extractor';
|
|
1054
|
-
/**
|
|
1055
|
-
* Execution mode. Optional in the manifest with a default of
|
|
1056
|
-
* `deterministic` per `spec/schemas/extensions/extractor.schema.json`.
|
|
1057
|
-
* `probabilistic` extractors invoke an LLM through the kernel's
|
|
1058
|
-
* `RunnerPort` and never participate in scan-time pipelines —
|
|
1059
|
-
* they dispatch only as queued jobs.
|
|
1060
|
-
*/
|
|
1061
|
-
mode?: TExecutionMode;
|
|
1062
1549
|
emitsLinkKinds: LinkKind[];
|
|
1063
1550
|
defaultConfidence: Confidence;
|
|
1064
1551
|
scope: 'frontmatter' | 'body' | 'both';
|
|
1065
1552
|
/**
|
|
1066
1553
|
* Optional opt-in filter on `node.kind`. When declared, the orchestrator
|
|
1067
1554
|
* skips invocation of `extract()` for any node whose `kind` is NOT in
|
|
1068
|
-
* this list — fail-fast, before context construction, so
|
|
1069
|
-
*
|
|
1070
|
-
* and a deterministic extractor wastes zero CPU.
|
|
1555
|
+
* this list — fail-fast, before context construction, so the extractor
|
|
1556
|
+
* wastes zero CPU on inapplicable nodes.
|
|
1071
1557
|
*
|
|
1072
1558
|
* Absent (`undefined`) is the default: the extractor applies to every
|
|
1073
1559
|
* kind. There are no wildcards — the absence of the field already
|
|
@@ -1099,9 +1585,76 @@ interface IExtractor extends IExtensionBase {
|
|
|
1099
1585
|
* `deterministic`).
|
|
1100
1586
|
*/
|
|
1101
1587
|
|
|
1588
|
+
/**
|
|
1589
|
+
* Step 9.6.2 — orphan sidecar entry surfaced to rules. A `.sm` file
|
|
1590
|
+
* whose sibling `.md` does not exist on disk; the `annotation-orphan`
|
|
1591
|
+
* built-in rule emits one warning per entry. Other rules that care
|
|
1592
|
+
* about orphan sidecars MAY consume the list too.
|
|
1593
|
+
*/
|
|
1594
|
+
interface IRuleOrphanSidecar {
|
|
1595
|
+
/** Relative path (POSIX-separated) of the orphan `.sm`. */
|
|
1596
|
+
relativePath: string;
|
|
1597
|
+
/** Absolute path of the missing `.md` the sidecar was anchored to. */
|
|
1598
|
+
expectedMdPath: string;
|
|
1599
|
+
}
|
|
1102
1600
|
interface IRuleContext {
|
|
1103
1601
|
nodes: Node[];
|
|
1104
1602
|
links: Link[];
|
|
1603
|
+
/**
|
|
1604
|
+
* Step 9.6.2 — orphaned sidecars discovered during the scan walk.
|
|
1605
|
+
* Empty when sidecar discovery did not run (legacy callers) or
|
|
1606
|
+
* when no orphans exist.
|
|
1607
|
+
*/
|
|
1608
|
+
orphanSidecars?: IRuleOrphanSidecar[];
|
|
1609
|
+
/**
|
|
1610
|
+
* Step 9.6.6 — raw parsed sidecar root keyed by `node.path`. Populated
|
|
1611
|
+
* by the orchestrator alongside the public `Node.sidecar` overlay so
|
|
1612
|
+
* rules that inspect plugin namespaces (e.g. the built-in
|
|
1613
|
+
* `core/unknown-field` Rule) can walk the full tree without re-reading
|
|
1614
|
+
* the file from disk. Absent (or `undefined` per node) when no
|
|
1615
|
+
* sidecar accompanies the node, or when the sidecar failed to parse.
|
|
1616
|
+
* Treat as read-only.
|
|
1617
|
+
*/
|
|
1618
|
+
sidecarRoots?: ReadonlyMap<string, Record<string, unknown>>;
|
|
1619
|
+
/**
|
|
1620
|
+
* Step 9.6.6 — runtime catalog of plugin-contributed annotation keys,
|
|
1621
|
+
* as exposed by `kernel.getRegisteredAnnotationKeys()`. Threaded
|
|
1622
|
+
* through so rules can reason about the registered-vs-unknown split
|
|
1623
|
+
* without reaching back into the kernel. Empty array when no plugin
|
|
1624
|
+
* declares contributions; absent for legacy callers (older runScan
|
|
1625
|
+
* sites that never wired the catalog through).
|
|
1626
|
+
*/
|
|
1627
|
+
annotationContributions?: readonly IRegisteredAnnotationKey[];
|
|
1628
|
+
/**
|
|
1629
|
+
* Step 11.x — runtime catalog of plugin-contributed view contributions,
|
|
1630
|
+
* as exposed by `kernel.getRegisteredViewContributions()`. Threaded
|
|
1631
|
+
* through so rules can reason about emissions without reaching back
|
|
1632
|
+
* into the kernel: built-in `core/unknown-contract` walks this list to
|
|
1633
|
+
* detect deprecated contracts in use, and `core/contribution-orphan`
|
|
1634
|
+
* joins it with the live node set to flag dangling emissions. Empty
|
|
1635
|
+
* array when no extension declares view contributions; absent for
|
|
1636
|
+
* legacy callers (older runScan sites that never wired the catalog
|
|
1637
|
+
* through).
|
|
1638
|
+
*/
|
|
1639
|
+
viewContributions?: readonly IRegisteredViewContribution[];
|
|
1640
|
+
/**
|
|
1641
|
+
* Emit a per-node view contribution declared in this rule's manifest
|
|
1642
|
+
* `viewContributions` map. Sync, void return; the orchestrator
|
|
1643
|
+
* validates the payload against the contract schema at call time and
|
|
1644
|
+
* silently drops invalid emissions with a logged `extension.error`
|
|
1645
|
+
* event (parallel to `IExtractorCallbacks.emitContribution`).
|
|
1646
|
+
*
|
|
1647
|
+
* Unlike Extractor's emit (which binds `nodePath` from `ctx.node.path`
|
|
1648
|
+
* implicitly because Extractors run per-node), Rule's `evaluate()`
|
|
1649
|
+
* sees the full graph at once. The rule walks `ctx.nodes` itself and
|
|
1650
|
+
* MUST supply the target node path explicitly per emission.
|
|
1651
|
+
*
|
|
1652
|
+
* Calling `emitContribution` with a `contributionId` that is not
|
|
1653
|
+
* declared in the manifest is dropped with an `extension.error`. The
|
|
1654
|
+
* kernel routes emitted contributions to the same persistence
|
|
1655
|
+
* pipeline as Extractor emissions (`scan_contributions`).
|
|
1656
|
+
*/
|
|
1657
|
+
emitContribution(nodePath: string, contributionId: string, payload: unknown): void;
|
|
1105
1658
|
}
|
|
1106
1659
|
interface IRule extends IExtensionBase {
|
|
1107
1660
|
kind: 'rule';
|
|
@@ -1156,6 +1709,58 @@ interface IRule extends IExtensionBase {
|
|
|
1156
1709
|
* - `fanOutPolicy` — `'per-node'` (default) vs `'batch'`.
|
|
1157
1710
|
*/
|
|
1158
1711
|
|
|
1712
|
+
/**
|
|
1713
|
+
* Single sidecar write payload an Action can return. Discriminated union so
|
|
1714
|
+
* future write kinds (storage rows, plugin KV, etc.) can land additively
|
|
1715
|
+
* without breaking consumers that only handle `kind: 'sidecar'`.
|
|
1716
|
+
*
|
|
1717
|
+
* - `path` — absolute path to the `.sm` file the kernel must materialise
|
|
1718
|
+
* the change into. Resolved by the Action from the node's absolute
|
|
1719
|
+
* path via `sidecarPathFor()`.
|
|
1720
|
+
* - `changes` — partial sidecar root used as a deep-merge patch (NOT a
|
|
1721
|
+
* full replacement). Arrays REPLACE; objects RECURSE. Reason:
|
|
1722
|
+
* sidecars are shared-write between skill-map core and plugins;
|
|
1723
|
+
* a full replace would clobber `<plugin-id>:` namespaced blocks.
|
|
1724
|
+
*/
|
|
1725
|
+
type TActionWrite = {
|
|
1726
|
+
kind: 'sidecar';
|
|
1727
|
+
path: string;
|
|
1728
|
+
changes: Record<string, unknown>;
|
|
1729
|
+
};
|
|
1730
|
+
/**
|
|
1731
|
+
* Result envelope returned by deterministic Actions. The `report` field
|
|
1732
|
+
* carries the typed report payload (each Action declares its shape via
|
|
1733
|
+
* `reportSchemaRef`); `writes` is opt-in — Actions that do not mutate
|
|
1734
|
+
* persistent state simply omit it.
|
|
1735
|
+
*/
|
|
1736
|
+
interface IActionResult<TReport = unknown> {
|
|
1737
|
+
report: TReport;
|
|
1738
|
+
writes?: TActionWrite[];
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Runtime context passed to a deterministic Action's `invoke()` method.
|
|
1742
|
+
* Minimal surface — Actions stay pure (no IO inside `invoke`); the kernel
|
|
1743
|
+
* materialises any returned `writes` after the call.
|
|
1744
|
+
*
|
|
1745
|
+
* - `node` — the target `Node` the Action operates on. Open-by-design;
|
|
1746
|
+
* batch / fan-out flows pick the matching nodes upstream.
|
|
1747
|
+
* - `nodeAbsolutePath` — absolute path to the node's `.md` file on
|
|
1748
|
+
* disk. The Action uses this to compute the sidecar path it returns
|
|
1749
|
+
* in a `TActionWrite`. Surfaced separately from `node.path` (which is
|
|
1750
|
+
* the relative scope-root form) so Actions never compose absolute
|
|
1751
|
+
* paths from `node.path` themselves.
|
|
1752
|
+
* - `invoker` — identity of the caller; written into the sidecar's
|
|
1753
|
+
* `audit.lastBumpedBy` when the Action chooses to. CLI invocations
|
|
1754
|
+
* pass `'cli'`; plugin-driven invocations pass `'plugin:<plugin-id>'`.
|
|
1755
|
+
* - `now` — clock function; tests inject a deterministic source.
|
|
1756
|
+
* Defaults to `() => new Date()` at the composition root.
|
|
1757
|
+
*/
|
|
1758
|
+
interface IActionContext {
|
|
1759
|
+
node: Node;
|
|
1760
|
+
nodeAbsolutePath: string;
|
|
1761
|
+
invoker: string;
|
|
1762
|
+
now: () => Date;
|
|
1763
|
+
}
|
|
1159
1764
|
/**
|
|
1160
1765
|
* Declarative filter applied by `--all` fan-out, UI button gating, and
|
|
1161
1766
|
* `sm actions show`. All fields optional — an empty precondition matches
|
|
@@ -1225,6 +1830,26 @@ interface IAction extends IExtensionBase {
|
|
|
1225
1830
|
* full list. Batch actions tend to hit context limits; use sparingly.
|
|
1226
1831
|
*/
|
|
1227
1832
|
fanOutPolicy?: 'per-node' | 'batch';
|
|
1833
|
+
/**
|
|
1834
|
+
* Deterministic invocation entry point. OPTIONAL on the runtime
|
|
1835
|
+
* contract for backward compatibility with the manifest-only era
|
|
1836
|
+
* (Decision #114) — actions that ship for the future probabilistic
|
|
1837
|
+
* runner / record path leave it absent and the kernel never calls it.
|
|
1838
|
+
* Step 9.6.3 (Decision #125) introduces the first concrete consumer:
|
|
1839
|
+
* the built-in `bump` Action implements `invoke()` and returns a
|
|
1840
|
+
* `writes: [{ kind: 'sidecar', ... }]` payload that the kernel
|
|
1841
|
+
* materialises through `ISidecarStore`.
|
|
1842
|
+
*
|
|
1843
|
+
* Implementations MUST stay pure — no IO inside `invoke()`. The Action
|
|
1844
|
+
* computes the patch and returns it; the kernel reads the on-disk
|
|
1845
|
+
* sidecar, deep-merges, validates, and writes back inside its critical
|
|
1846
|
+
* section.
|
|
1847
|
+
*
|
|
1848
|
+
* `TInput` is action-specific; the built-in `bump` Action declares
|
|
1849
|
+
* `{ force?: boolean; reason?: string }`. The signature stays generic
|
|
1850
|
+
* so each Action narrows it locally without forcing a common base.
|
|
1851
|
+
*/
|
|
1852
|
+
invoke?: <TInput, TReport>(input: TInput, ctx: IActionContext) => IActionResult<TReport>;
|
|
1228
1853
|
}
|
|
1229
1854
|
|
|
1230
1855
|
/**
|
|
@@ -1499,6 +2124,31 @@ interface RunScanOptions {
|
|
|
1499
2124
|
emitter?: ProgressEmitterPort;
|
|
1500
2125
|
/** Runtime extension instances. Absent → empty pipeline. */
|
|
1501
2126
|
extensions?: IScanExtensions;
|
|
2127
|
+
/**
|
|
2128
|
+
* Step 9.6.6 — runtime catalog of plugin-contributed annotation keys
|
|
2129
|
+
* (the same shape `kernel.getRegisteredAnnotationKeys()` returns).
|
|
2130
|
+
* Threaded into the rule pass so `core/unknown-field` can
|
|
2131
|
+
* legitimise registered plugin namespaces / root keys without
|
|
2132
|
+
* re-walking the manifests. Absent → empty catalog (every plugin
|
|
2133
|
+
* key is treated as unknown). Built-in catalog from
|
|
2134
|
+
* `annotations.schema.json` is NOT included — that is hard-coded
|
|
2135
|
+
* inside the rule.
|
|
2136
|
+
*/
|
|
2137
|
+
annotationContributions?: readonly IRegisteredAnnotationKey[];
|
|
2138
|
+
/**
|
|
2139
|
+
* Runtime catalog of plugin-contributed view contributions (the same
|
|
2140
|
+
* shape `kernel.getRegisteredViewContributions()` returns). Threaded
|
|
2141
|
+
* into the rule pass so:
|
|
2142
|
+
* - `core/unknown-contract` and `core/contribution-orphan` can
|
|
2143
|
+
* introspect the catalog (read-only).
|
|
2144
|
+
* - The orchestrator's per-rule emit closure can look up each
|
|
2145
|
+
* declared `(contributionId → contract)` pairing for AJV
|
|
2146
|
+
* payload validation.
|
|
2147
|
+
* Absent → empty catalog. Rules that emit contributions silently
|
|
2148
|
+
* drop emissions when the catalog has no entry for the rule's
|
|
2149
|
+
* declared contributionId.
|
|
2150
|
+
*/
|
|
2151
|
+
viewContributions?: readonly IRegisteredViewContribution[];
|
|
1502
2152
|
/**
|
|
1503
2153
|
* Scan scope. Defaults to `'project'`. The CLI flag wiring lands in
|
|
1504
2154
|
* the config layer wiring; `runScan` already accepts the override
|
|
@@ -1618,8 +2268,6 @@ interface IExtractorRunRecord {
|
|
|
1618
2268
|
*
|
|
1619
2269
|
* - upsert a single row per pair (stable PRIMARY KEY conflict on
|
|
1620
2270
|
* re-extract);
|
|
1621
|
-
* - flag probabilistic rows `stale = 1` when the body changes between
|
|
1622
|
-
* scans (preserving the prior LLM cost);
|
|
1623
2271
|
* - feed `mergeNodeWithEnrichments` with `enrichedAt`-sorted partials
|
|
1624
2272
|
* for last-write-wins per field at read time.
|
|
1625
2273
|
*
|
|
@@ -1629,10 +2277,12 @@ interface IExtractorRunRecord {
|
|
|
1629
2277
|
* fold into a single row, but two different Extractors hitting the
|
|
1630
2278
|
* same node yield two distinct rows.
|
|
1631
2279
|
*
|
|
1632
|
-
* `isProbabilistic` is
|
|
1633
|
-
*
|
|
1634
|
-
*
|
|
1635
|
-
*
|
|
2280
|
+
* `isProbabilistic` is reserved: Extractors are deterministic-only, so
|
|
2281
|
+
* every record produced by the orchestrator sets it to `false`. The
|
|
2282
|
+
* field is kept on the record (and the row in `node_enrichments`) so a
|
|
2283
|
+
* future Action-issued enrichment can populate it without reshaping
|
|
2284
|
+
* the persistence contract — see spec `architecture.md`
|
|
2285
|
+
* §Extractor · enrichment layer.
|
|
1636
2286
|
*/
|
|
1637
2287
|
interface IEnrichmentRecord {
|
|
1638
2288
|
nodePath: string;
|
|
@@ -1660,6 +2310,7 @@ declare function runScanWithRenames(_kernel: Kernel, options: RunScanOptions): P
|
|
|
1660
2310
|
renameOps: RenameOp[];
|
|
1661
2311
|
extractorRuns: IExtractorRunRecord[];
|
|
1662
2312
|
enrichments: IEnrichmentRecord[];
|
|
2313
|
+
contributions: IContributionRecord[];
|
|
1663
2314
|
}>;
|
|
1664
2315
|
declare function runScan(_kernel: Kernel, options: RunScanOptions): Promise<ScanResult>;
|
|
1665
2316
|
/**
|
|
@@ -1696,6 +2347,7 @@ declare function runExtractorsForNode(opts: {
|
|
|
1696
2347
|
internalLinks: Link[];
|
|
1697
2348
|
externalLinks: Link[];
|
|
1698
2349
|
enrichments: IEnrichmentRecord[];
|
|
2350
|
+
contributions: IContributionRecord[];
|
|
1699
2351
|
}>;
|
|
1700
2352
|
/**
|
|
1701
2353
|
* Pure rename / orphan classification per `spec/db-schema.md` §Rename
|
|
@@ -1736,10 +2388,11 @@ declare function detectRenamesAndOrphans(prior: ScanResult, current: Node[], iss
|
|
|
1736
2388
|
* Algorithm:
|
|
1737
2389
|
*
|
|
1738
2390
|
* 1. Filter `enrichments` down to rows targeting this node AND not
|
|
1739
|
-
* flagged `stale`.
|
|
1740
|
-
*
|
|
1741
|
-
*
|
|
1742
|
-
*
|
|
2391
|
+
* flagged `stale`. With Extractors deterministic-only no row is
|
|
2392
|
+
* stale-flagged in this revision; the filter is preserved for the
|
|
2393
|
+
* future Action-issued enrichment revision (queued LLM jobs whose
|
|
2394
|
+
* output must survive body changes), where stale visibility
|
|
2395
|
+
* belongs to the UI layer next to the value.
|
|
1743
2396
|
* 2. Sort the survivors by `enrichedAt` ASC so iteration order is
|
|
1744
2397
|
* "oldest first". This makes the spread merge below
|
|
1745
2398
|
* last-write-wins per field — the freshest Extractor's value
|
|
@@ -1792,7 +2445,7 @@ interface IPersistedEnrichment {
|
|
|
1792
2445
|
declare class InMemoryProgressEmitter implements ProgressEmitterPort {
|
|
1793
2446
|
#private;
|
|
1794
2447
|
emit(event: ProgressEvent): void;
|
|
1795
|
-
subscribe(listener:
|
|
2448
|
+
subscribe(listener: TProgressListener): () => void;
|
|
1796
2449
|
}
|
|
1797
2450
|
|
|
1798
2451
|
/**
|
|
@@ -2033,6 +2686,72 @@ declare function applyExportQuery(scan: {
|
|
|
2033
2686
|
issues: Issue[];
|
|
2034
2687
|
}, query: IExportQuery): IExportSubset;
|
|
2035
2688
|
|
|
2689
|
+
/**
|
|
2690
|
+
* `scan_node_tags` adapter — tags · dual-source persistence layer.
|
|
2691
|
+
*
|
|
2692
|
+
* One row per `(node_path, tag, source)` triple. Projected at persist
|
|
2693
|
+
* time from BOTH `frontmatter.tags` (with `source='author'`) and
|
|
2694
|
+
* `sidecar.annotations.tags` (with `source='user'`). The same tag
|
|
2695
|
+
* string MAY appear under both sources for the same node — the PK
|
|
2696
|
+
* accepts the pair; search returns the node once via DISTINCT, the
|
|
2697
|
+
* UI renders both chips with their attribution.
|
|
2698
|
+
*
|
|
2699
|
+
* Belongs to the `scan_*` family — replaced wholesale per scan.
|
|
2700
|
+
* Cached nodes' tag rows are projected from the cached
|
|
2701
|
+
* `node.frontmatter.tags` / `node.sidecar.annotations.tags` (both
|
|
2702
|
+
* already in memory at persist time), so the rebuild is cheap
|
|
2703
|
+
* regardless of cache hit / miss. See `spec/db-schema.md`
|
|
2704
|
+
* § scan_node_tags for the normative shape and replace-all semantics.
|
|
2705
|
+
*/
|
|
2706
|
+
|
|
2707
|
+
/**
|
|
2708
|
+
* In-memory tag record buffered during scan and flushed to
|
|
2709
|
+
* `scan_node_tags` by `persistScanResult`. One entry per
|
|
2710
|
+
* `(node_path, tag, source)` projected from a node's frontmatter tags
|
|
2711
|
+
* (`source: 'author'`) or sidecar annotations tags
|
|
2712
|
+
* (`source: 'user'`).
|
|
2713
|
+
*/
|
|
2714
|
+
interface ITagRecord {
|
|
2715
|
+
nodePath: string;
|
|
2716
|
+
tag: string;
|
|
2717
|
+
source: 'author' | 'user';
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
/**
|
|
2721
|
+
* Pure helpers for the "update available" notification feature.
|
|
2722
|
+
*
|
|
2723
|
+
* Three responsibilities:
|
|
2724
|
+
* - `fetchLatestVersion` — query `https://registry.npmjs.org/<pkg>/latest`
|
|
2725
|
+
* with `AbortController` + timeout. Throws on
|
|
2726
|
+
* non-200 / parse failure / abort.
|
|
2727
|
+
* - `compareVersions` — semver compare (-1 / 0 / 1). Pre-1.0 aware:
|
|
2728
|
+
* treats prereleases via the standard rules
|
|
2729
|
+
* (release > prerelease at the same triple).
|
|
2730
|
+
* - `isOutdated` — sugar over `compareVersions` for the common
|
|
2731
|
+
* "is `latest` strictly greater than `current`"
|
|
2732
|
+
* check the banner runs against.
|
|
2733
|
+
*
|
|
2734
|
+
* Pure kernel module — NO `process.env` reads, NO Node globals beyond the
|
|
2735
|
+
* built-in `fetch` / `AbortController` (Node 22+). Every env / settings
|
|
2736
|
+
* lookup happens in `src/cli/util/update-check-banner.ts`, the CLI-side
|
|
2737
|
+
* adapter that owns side effects.
|
|
2738
|
+
*
|
|
2739
|
+
* The shared cache type (`IUpdateCheckCache`) is used by the storage
|
|
2740
|
+
* helpers under `kernel/storage/update-check.ts` and by the BFF's
|
|
2741
|
+
* `GET /api/update-status` projection. A second type
|
|
2742
|
+
* (`IUpdateStatus`) shapes the BFF response — it merges `current`
|
|
2743
|
+
* (from `VERSION`) into the cache so the UI can render without a
|
|
2744
|
+
* second lookup. Both stay flat — no nested objects — so JSON
|
|
2745
|
+
* serialization is trivial.
|
|
2746
|
+
*/
|
|
2747
|
+
interface IUpdateCheckCache {
|
|
2748
|
+
latestVersion: string;
|
|
2749
|
+
/** Epoch ms — when the registry was last successfully probed. */
|
|
2750
|
+
checkedAt: number;
|
|
2751
|
+
/** Epoch ms — when the banner was last printed; null = never shown yet. */
|
|
2752
|
+
shownAt: number | null;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2036
2755
|
/**
|
|
2037
2756
|
* `PluginLoaderPort` — discovers plugin directories and loads their
|
|
2038
2757
|
* extensions. The shape mirrors what the concrete loader actually
|
|
@@ -2126,6 +2845,19 @@ interface IPersistOptions {
|
|
|
2126
2845
|
renameOps?: RenameOp[];
|
|
2127
2846
|
extractorRuns?: IExtractorRunRecord[];
|
|
2128
2847
|
enrichments?: IEnrichmentRecord[];
|
|
2848
|
+
contributions?: IContributionRecord[];
|
|
2849
|
+
/**
|
|
2850
|
+
* Phase 3 / View contribution system — active runtime catalog of
|
|
2851
|
+
* registered view contributions, keyed by qualified id
|
|
2852
|
+
* `<pluginId>/<extensionId>/<contributionId>`. Passed to the
|
|
2853
|
+
* `scan_contributions` upsert so the catalog sweep can drop rows
|
|
2854
|
+
* belonging to plugins / extensions that are no longer in the
|
|
2855
|
+
* catalog (uninstalled plugins, disabled bundles, removed
|
|
2856
|
+
* contributions). Empty / absent set = no catalog sweep (legacy
|
|
2857
|
+
* behaviour, leaves disabled-plugin rows stale per design F24
|
|
2858
|
+
* pre-fix).
|
|
2859
|
+
*/
|
|
2860
|
+
registeredContributionKeys?: ReadonlySet<string>;
|
|
2129
2861
|
}
|
|
2130
2862
|
/**
|
|
2131
2863
|
* Issue row as the storage layer sees it — paired with its DB-assigned
|
|
@@ -2181,16 +2913,18 @@ interface IMigrateNodeFksReport {
|
|
|
2181
2913
|
summaries: number;
|
|
2182
2914
|
enrichments: number;
|
|
2183
2915
|
pluginKvs: number;
|
|
2916
|
+
nodeFavorites: number;
|
|
2184
2917
|
/**
|
|
2185
|
-
*
|
|
2186
|
-
* `
|
|
2187
|
-
*
|
|
2188
|
-
*
|
|
2189
|
-
*
|
|
2190
|
-
*
|
|
2918
|
+
* Collisions encountered when migrating any of the keyed-by-node
|
|
2919
|
+
* `state_*` tables because a row already existed at the destination
|
|
2920
|
+
* PK. The pre-existing rows are preserved — the migrating rows are
|
|
2921
|
+
* dropped (deleted from `fromPath` without a corresponding INSERT).
|
|
2922
|
+
* One entry per dropped row, with the affected PK fields included
|
|
2923
|
+
* for diagnostic output. `state_node_favorites` has no composite key
|
|
2924
|
+
* so its `keys` is the empty object.
|
|
2191
2925
|
*/
|
|
2192
2926
|
collisions: Array<{
|
|
2193
|
-
table: 'state_summaries' | 'state_enrichments' | 'state_plugin_kvs';
|
|
2927
|
+
table: 'state_summaries' | 'state_enrichments' | 'state_plugin_kvs' | 'state_node_favorites';
|
|
2194
2928
|
fromPath: string;
|
|
2195
2929
|
toPath: string;
|
|
2196
2930
|
keys: Record<string, string>;
|
|
@@ -2264,28 +2998,6 @@ interface IPluginApplyResult {
|
|
|
2264
2998
|
intrusions: string[];
|
|
2265
2999
|
}
|
|
2266
3000
|
|
|
2267
|
-
/**
|
|
2268
|
-
* `StoragePort` — the kernel's persistence boundary. Driving adapters
|
|
2269
|
-
* (CLI, future server, in-memory test harness) consume this surface
|
|
2270
|
-
* exclusively; nothing in `cli/**` should reach into the SQLite
|
|
2271
|
-
* adapter's internal helpers (free functions on
|
|
2272
|
-
* `kernel/adapters/sqlite/*`) directly. Phase F of the
|
|
2273
|
-
* storage-port-promotion refactor finishes that hardening; A-E grow
|
|
2274
|
-
* the port enough that the CLI has somewhere to land.
|
|
2275
|
-
*
|
|
2276
|
-
* The port is namespaced by domain (`scans`, `issues`, `enrichments`,
|
|
2277
|
-
* etc.) — explicitly NOT a generic `port.query<T>(sql)`. Each
|
|
2278
|
-
* namespace's methods name an operation the kernel cares about; the
|
|
2279
|
-
* adapter translates to its persistence engine's idioms.
|
|
2280
|
-
*
|
|
2281
|
-
* Phase A lands the **scans / issues / enrichments / transaction**
|
|
2282
|
-
* namespaces — the core scan pipeline. The remaining namespaces
|
|
2283
|
-
* (history / jobs / pluginConfig / migrations / pluginMigrations)
|
|
2284
|
-
* arrive in subsequent phases. The port shape declared here is the
|
|
2285
|
-
* Phase A subset; later phases extend it without reshaping what
|
|
2286
|
-
* lands today.
|
|
2287
|
-
*/
|
|
2288
|
-
|
|
2289
3001
|
/**
|
|
2290
3002
|
* Subset of `StoragePort` exposed inside a `transaction(fn)` callback.
|
|
2291
3003
|
* Lifecycle methods are intentionally omitted — a transaction that
|
|
@@ -2362,6 +3074,50 @@ interface StoragePort {
|
|
|
2362
3074
|
*/
|
|
2363
3075
|
findNode(path: string): Promise<INodeBundle | null>;
|
|
2364
3076
|
};
|
|
3077
|
+
/**
|
|
3078
|
+
* Phase 3 / View contribution system — read access to
|
|
3079
|
+
* `scan_contributions`. Writes happen exclusively via
|
|
3080
|
+
* `scans.persist({ contributions })` to keep the replace-all
|
|
3081
|
+
* semantics intact; this namespace is read-only.
|
|
3082
|
+
*/
|
|
3083
|
+
contributions: {
|
|
3084
|
+
/** Every contribution row for a single node. Stable order. */
|
|
3085
|
+
listForNode(nodePath: string): Promise<IPersistedContribution[]>;
|
|
3086
|
+
/**
|
|
3087
|
+
* Bulk variant for the BFF nodes-list route. Returns rows for
|
|
3088
|
+
* every path in `paths`, sorted `nodePath` ASC, then qualified-id
|
|
3089
|
+
* ASC. Empty `paths` returns `[]` without a query.
|
|
3090
|
+
*/
|
|
3091
|
+
listForPaths(paths: readonly string[]): Promise<IPersistedContribution[]>;
|
|
3092
|
+
/**
|
|
3093
|
+
* Lookup by qualified id + path. Used by
|
|
3094
|
+
* `GET /api/contributions/:pluginId/:contributionId?path=...`.
|
|
3095
|
+
*/
|
|
3096
|
+
lookup(pluginId: string, contributionId: string, nodePath: string, extensionId?: string): Promise<IPersistedContribution[]>;
|
|
3097
|
+
};
|
|
3098
|
+
/**
|
|
3099
|
+
* Read-only access to `scan_node_tags`. Writes happen exclusively
|
|
3100
|
+
* via `scans.persist({...})` (the persistence layer projects from
|
|
3101
|
+
* `node.frontmatter.tags` and `node.sidecar.annotations.tags`); this
|
|
3102
|
+
* namespace is read-only.
|
|
3103
|
+
*/
|
|
3104
|
+
tags: {
|
|
3105
|
+
/** Every tag row for a single node. Author entries first, then user. */
|
|
3106
|
+
listForNode(nodePath: string): Promise<ITagRecord[]>;
|
|
3107
|
+
/**
|
|
3108
|
+
* Bulk variant for the BFF nodes-list route. Returns rows for every
|
|
3109
|
+
* path in `paths`, sorted `nodePath` ASC, then `source` ASC, then
|
|
3110
|
+
* `tag` ASC. Empty `paths` returns `[]` without a query.
|
|
3111
|
+
*/
|
|
3112
|
+
listForPaths(paths: readonly string[]): Promise<ITagRecord[]>;
|
|
3113
|
+
/**
|
|
3114
|
+
* Find every node carrying `tag`. Optional `source` narrows to one
|
|
3115
|
+
* side of the dual surface (matches `sm list --tag <name>
|
|
3116
|
+
* --tag-source author|user`); absent matches the union (default
|
|
3117
|
+
* `sm list --tag`).
|
|
3118
|
+
*/
|
|
3119
|
+
findNodes(tag: string, source?: 'author' | 'user'): Promise<string[]>;
|
|
3120
|
+
};
|
|
2365
3121
|
issues: {
|
|
2366
3122
|
/** Every issue from the latest scan, in insertion order. */
|
|
2367
3123
|
listAll(): Promise<Issue[]>;
|
|
@@ -2418,6 +3174,53 @@ interface StoragePort {
|
|
|
2418
3174
|
*/
|
|
2419
3175
|
listReferencedFilePaths(): Promise<Set<string>>;
|
|
2420
3176
|
};
|
|
3177
|
+
/**
|
|
3178
|
+
* Generic key/value preferences keyed by a stable string. Backs the
|
|
3179
|
+
* `config_preferences` table — one row per `key`, `value_json` is a
|
|
3180
|
+
* single JSON blob the caller serialises. Keys with the `_kernel.`
|
|
3181
|
+
* prefix are reserved for kernel-managed entries (today: the
|
|
3182
|
+
* update-check cache); user-set preferences land under unprefixed
|
|
3183
|
+
* keys when those ship.
|
|
3184
|
+
*
|
|
3185
|
+
* Read-only by design at the port level — the only writer is the
|
|
3186
|
+
* CLI's post-run hook (`cli/util/update-check-banner.ts`), which
|
|
3187
|
+
* reaches the persistence helpers directly. The port surfaces the
|
|
3188
|
+
* read so the BFF's `GET /api/update-status` projection can stay
|
|
3189
|
+
* inside the abstract contract.
|
|
3190
|
+
*/
|
|
3191
|
+
preferences: {
|
|
3192
|
+
/**
|
|
3193
|
+
* Load the update-check cache row. Returns `null` when the row
|
|
3194
|
+
* is absent, malformed JSON, or fails the shape guard. Never
|
|
3195
|
+
* throws — read failures degrade silently because the banner is
|
|
3196
|
+
* a non-essential surface.
|
|
3197
|
+
*/
|
|
3198
|
+
loadUpdateCheckCache(): Promise<IUpdateCheckCache | null>;
|
|
3199
|
+
/**
|
|
3200
|
+
* Upsert the update-check cache row. Always overwrites the
|
|
3201
|
+
* existing JSON blob in place. `updated_at` tracks wall-clock
|
|
3202
|
+
* now — separate from the embedded `checkedAt` field, which
|
|
3203
|
+
* the caller controls.
|
|
3204
|
+
*/
|
|
3205
|
+
saveUpdateCheckCache(cache: IUpdateCheckCache): Promise<void>;
|
|
3206
|
+
};
|
|
3207
|
+
favorites: {
|
|
3208
|
+
/**
|
|
3209
|
+
* Mark `path` as favorited. Idempotent — a second call refreshes
|
|
3210
|
+
* `favoritedAt` but does not error. The path is FK-semantic to
|
|
3211
|
+
* `scan_nodes.path`; the route layer is responsible for confirming
|
|
3212
|
+
* the path exists in the live scan before calling.
|
|
3213
|
+
*/
|
|
3214
|
+
set(path: string): Promise<void>;
|
|
3215
|
+
/** Drop the favorite row for `path`. Idempotent — no-op when absent. */
|
|
3216
|
+
unset(path: string): Promise<void>;
|
|
3217
|
+
/**
|
|
3218
|
+
* Load every favorited path as a `Set<string>` ready for `O(1)`
|
|
3219
|
+
* membership checks. Used by the BFF's `/api/nodes` decorator —
|
|
3220
|
+
* one query per request, no SQL JOIN against `scan_nodes`.
|
|
3221
|
+
*/
|
|
3222
|
+
listPaths(): Promise<Set<string>>;
|
|
3223
|
+
};
|
|
2421
3224
|
history: {
|
|
2422
3225
|
/** List `state_executions` rows (paginated by filter). */
|
|
2423
3226
|
list(filter: IListExecutionsFilter): Promise<ExecutionRecord[]>;
|
|
@@ -2544,19 +3347,19 @@ interface RunnerPort {
|
|
|
2544
3347
|
* `LogRecord.level`. Setting an adapter to `silent` disables every
|
|
2545
3348
|
* method.
|
|
2546
3349
|
*/
|
|
2547
|
-
type
|
|
2548
|
-
type
|
|
2549
|
-
declare const LOG_LEVELS: readonly
|
|
2550
|
-
declare function logLevelRank(level:
|
|
2551
|
-
declare function isLogLevel(value: unknown): value is
|
|
3350
|
+
type TLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent';
|
|
3351
|
+
type TLogMethodLevel = Exclude<TLogLevel, 'silent'>;
|
|
3352
|
+
declare const LOG_LEVELS: readonly TLogLevel[];
|
|
3353
|
+
declare function logLevelRank(level: TLogLevel): number;
|
|
3354
|
+
declare function isLogLevel(value: unknown): value is TLogLevel;
|
|
2552
3355
|
/**
|
|
2553
|
-
* Parse a string into a `
|
|
3356
|
+
* Parse a string into a `TLogLevel`. Returns `null` for invalid input
|
|
2554
3357
|
* (incl. `undefined` / `null` / empty). Case-insensitive; trims
|
|
2555
3358
|
* whitespace.
|
|
2556
3359
|
*/
|
|
2557
|
-
declare function parseLogLevel(value: string | undefined | null):
|
|
3360
|
+
declare function parseLogLevel(value: string | undefined | null): TLogLevel | null;
|
|
2558
3361
|
interface LogRecord {
|
|
2559
|
-
level:
|
|
3362
|
+
level: TLogMethodLevel;
|
|
2560
3363
|
/** ISO 8601 timestamp produced at the moment the log call was made. */
|
|
2561
3364
|
timestamp: string;
|
|
2562
3365
|
message: string;
|
|
@@ -2628,7 +3431,35 @@ declare function getActiveLogger(): LoggerPort;
|
|
|
2628
3431
|
|
|
2629
3432
|
interface Kernel {
|
|
2630
3433
|
registry: Registry;
|
|
3434
|
+
/**
|
|
3435
|
+
* Step 9.6.6 — read-only catalog of plugin-contributed annotation
|
|
3436
|
+
* keys, keyed by `(pluginId, key)`. Populated at plugin-load time;
|
|
3437
|
+
* pure read with no side effects. Built-in catalog (from
|
|
3438
|
+
* `annotations.schema.json`) is NOT included here.
|
|
3439
|
+
*/
|
|
3440
|
+
getRegisteredAnnotationKeys: () => readonly IRegisteredAnnotationKey[];
|
|
3441
|
+
/**
|
|
3442
|
+
* Internal — replace the frozen catalog. Called once by the
|
|
3443
|
+
* plugin runtime composer after every plugin has loaded; consumers
|
|
3444
|
+
* MUST treat the resulting array as immutable.
|
|
3445
|
+
*/
|
|
3446
|
+
setRegisteredAnnotationKeys: (entries: readonly IRegisteredAnnotationKey[]) => void;
|
|
3447
|
+
/**
|
|
3448
|
+
* Step 11.x — read-only catalog of plugin-contributed view
|
|
3449
|
+
* contributions, keyed by `(pluginId, extensionId, contributionId)`.
|
|
3450
|
+
* Populated at plugin-load time; pure read with no side effects.
|
|
3451
|
+
* Mirror of `getRegisteredAnnotationKeys` for the view contribution
|
|
3452
|
+
* surface (see `architecture.md` §View contribution system →
|
|
3453
|
+
* Runtime catalog).
|
|
3454
|
+
*/
|
|
3455
|
+
getRegisteredViewContributions: () => readonly IRegisteredViewContribution[];
|
|
3456
|
+
/**
|
|
3457
|
+
* Internal — replace the frozen view-contribution catalog. Called
|
|
3458
|
+
* once by the plugin runtime composer after every plugin has loaded;
|
|
3459
|
+
* consumers MUST treat the resulting array as immutable.
|
|
3460
|
+
*/
|
|
3461
|
+
setRegisteredViewContributions: (entries: readonly IRegisteredViewContribution[]) => void;
|
|
2631
3462
|
}
|
|
2632
3463
|
declare function createKernel(): Kernel;
|
|
2633
3464
|
|
|
2634
|
-
export { type Confidence, DuplicateExtensionError, EXTENSION_KINDS, type ExecutionFailureReason, type ExecutionKind, type ExecutionRecord, type ExecutionRunner, type ExecutionStatus, ExportQueryError, type Extension, type ExtensionKind, type FilesystemPort, HOOK_TRIGGERS, type HistoryStats, type HistoryStatsErrorRates, type HistoryStatsExecutionsPerPeriod, type HistoryStatsPerActionRate, type HistoryStatsTokensPerAction, type HistoryStatsTopNode, type HistoryStatsTotals, type IAction, type IActionPrecondition, type ICreateFsWatcherOptions, type IDedicatedStorePersist, type IDedicatedStoreWrapper, type IDiscoveredPlugin, type IEnrichmentRecord, type IExportQuery, type IExportSubset, type IExtensionBase, type IExtractor, type IExtractorCallbacks, type IExtractorContext, type IExtractorRunRecord, type IFormatter, type IFormatterContext, type IFsWatcher, type IHook, type IHookContext, type IIssueRow, type IKvStorePersist, type IKvStoreWrapper, type ILoadedExtension, type INodeBundle, type INodeChange, type INodeCounts, type INodeFilter, type IPersistOptions, type IPersistedEnrichment, type IPluginManifest, type IPluginStorageSchema, type IPluginStore, type IProvider, type IRawNode, type IRule, type IRuleContext, type IRunOptions, type IRunResult, type IScanDelta, type ITransactionalStorage, type IWalkOptions, type IWatchBatch, type IWatchEvent, InMemoryProgressEmitter, type Issue, type IssueFix, KV_SCHEMA_KEY, type Kernel, LOG_LEVELS, type Link, type LinkKind, type LinkLocation, type LinkTrigger, type
|
|
3465
|
+
export { type Confidence, DuplicateExtensionError, EXTENSION_KINDS, type ExecutionFailureReason, type ExecutionKind, type ExecutionRecord, type ExecutionRunner, type ExecutionStatus, ExportQueryError, type Extension, type ExtensionKind, type FilesystemPort, HOOK_TRIGGERS, type HistoryStats, type HistoryStatsErrorRates, type HistoryStatsExecutionsPerPeriod, type HistoryStatsPerActionRate, type HistoryStatsTokensPerAction, type HistoryStatsTopNode, type HistoryStatsTotals, type IAction, type IActionContext, type IActionPrecondition, type IActionResult, type IAnnotationContribution, type ICreateFsWatcherOptions, type IDedicatedStorePersist, type IDedicatedStoreWrapper, type IDiscoveredPlugin, type IEnrichmentRecord, type IExportQuery, type IExportSubset, type IExtensionBase, type IExtractor, type IExtractorCallbacks, type IExtractorContext, type IExtractorRunRecord, type IFormatter, type IFormatterContext, type IFsWatcher, type IHook, type IHookContext, type IIssueRow, type IKvStorePersist, type IKvStoreWrapper, type ILoadedExtension, type INodeBundle, type INodeChange, type INodeCounts, type INodeFilter, type IPersistOptions, type IPersistedEnrichment, type IPluginManifest, type IPluginStorageSchema, type IPluginStore, type IProvider, type IRawNode, type IRegisteredAnnotationKey, type IRegisteredViewContribution, type IRule, type IRuleContext, type IRunOptions, type IRunResult, type IScanDelta, type ISettingDeclaration, type ITransactionalStorage, type IViewContribution, type IWalkOptions, type IWatchBatch, type IWatchEvent, InMemoryProgressEmitter, type Issue, type IssueFix, KV_SCHEMA_KEY, type Kernel, LOG_LEVELS, type Link, type LinkKind, type LinkLocation, type LinkTrigger, type LogRecord, type LoggerPort, type Node, type NodeKind, type NodeStat, type PluginLoaderPort, type ProgressEmitterPort, type ProgressEvent, Registry, type RenameOp, type RunScanOptions, type RunnerPort, type ScanResult, type ScanScannedBy, type ScanStats, type Severity, SilentLogger, type Stability, type StoragePort, type TActionWrite, type TContractName, type TExecutionMode, type TGranularity, type THookFilter, type THookTrigger, type TInputTypeName, type TLogLevel, type TLogMethodLevel, type TNodeChangeReason, type TPluginLoadStatus, type TPluginStorage, type TProgressListener, type TSettingValue, type TSeverity, type TWatchEventKind, type TripleSplit, applyExportQuery, computeScanDelta, configureLogger, createChokidarWatcher, createKernel, detectRenamesAndOrphans, getActiveLogger, isEmptyDelta, isLogLevel, log, logLevelRank, makeDedicatedStoreWrapper, makeKvStoreWrapper, makePluginStore, mergeNodeWithEnrichments, parseExportQuery, parseLogLevel, qualifiedExtensionId, resetLogger, runExtractorsForNode, runScan, runScanWithRenames };
|