@skill-map/cli 0.18.0 → 0.20.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 +9 -10
- package/dist/cli.js +11062 -7069
- package/dist/cli.js.map +1 -1
- package/dist/conformance/index.js +1 -1
- package/dist/conformance/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +424 -148
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +776 -119
- package/dist/kernel/index.js +424 -148
- package/dist/kernel/index.js.map +1 -1
- package/dist/migrations/001_initial.sql +125 -15
- package/dist/ui/chunk-2W62S3FU.js +2638 -0
- package/dist/ui/chunk-C7QWBAYP.js +247 -0
- package/dist/ui/chunk-DLT5AP43.js +237 -0
- package/dist/ui/chunk-HJSRMZTK.js +450 -0
- package/dist/ui/chunk-HOBQ4G4O.js +125 -0
- package/dist/ui/chunk-IBUV6OG2.js +1 -0
- package/dist/ui/chunk-LQTUSDHD.js +124 -0
- package/dist/ui/chunk-QICH7GU2.js +5 -0
- package/dist/ui/chunk-UJRROL5X.js +1 -0
- package/dist/ui/chunk-VLNLJAUB.js +61 -0
- package/dist/ui/chunk-W3JLG7BI.js +965 -0
- package/dist/ui/index.html +10 -2
- package/dist/ui/main-QHE47BCM.js +1 -0
- package/dist/ui/{styles-CBPFNGXA.css → styles-VJ5Q6D2X.css} +1 -1
- package/migrations/001_initial.sql +125 -15
- package/package.json +2 -2
- package/dist/migrations/002_sidecar_columns.sql +0 -53
- package/dist/migrations/003_drop_node_author.sql +0 -20
- package/dist/migrations/004_sidecar_root_json.sql +0 -23
- package/dist/migrations/005_node_favorites.sql +0 -20
- package/dist/ui/chunk-JKJGGXCS.js +0 -1025
- package/dist/ui/chunk-SX2A3WBX.js +0 -247
- package/dist/ui/chunk-TWZHUCAT.js +0 -237
- package/dist/ui/chunk-WTAL2RK4.js +0 -1
- package/dist/ui/chunk-Z3UJHHTC.js +0 -3091
- package/dist/ui/main-AAYGMON4.js +0 -1
- package/migrations/002_sidecar_columns.sql +0 -53
- package/migrations/003_drop_node_author.sql +0 -20
- package/migrations/004_sidecar_root_json.sql +0 -23
- package/migrations/005_node_favorites.sql +0 -20
package/dist/kernel/index.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* implements `StoragePort`).
|
|
26
26
|
*
|
|
27
27
|
* 3. **Runtime extension contracts** — what a plugin author
|
|
28
|
-
* implements: `IProvider`, `IExtractor`, `
|
|
28
|
+
* implements: `IProvider`, `IExtractor`, `IAnalyzer`, `IFormatter`,
|
|
29
29
|
* `IExtensionBase`. **`I` prefix.** The prefix flags "this is a
|
|
30
30
|
* contract you supply, not a value the kernel hands you" — same
|
|
31
31
|
* reading as the rest of TypeScript's plugin ecosystems where a
|
|
@@ -151,17 +151,6 @@ interface Node {
|
|
|
151
151
|
linksOutCount: number;
|
|
152
152
|
linksInCount: number;
|
|
153
153
|
externalRefsCount: number;
|
|
154
|
-
title?: string | null;
|
|
155
|
-
description?: string | null;
|
|
156
|
-
stability?: Stability | null;
|
|
157
|
-
/**
|
|
158
|
-
* Monotonic version counter sourced from the sidecar's
|
|
159
|
-
* `annotations.version` (Step 9.6.2). `null` when no sidecar accompanies
|
|
160
|
-
* the node, or when the sidecar omits `version`. Pre-9.6.2 the field
|
|
161
|
-
* was a semver string sourced from `frontmatter.metadata.version`;
|
|
162
|
-
* see migration `002_sidecar_columns.sql` and Decision #125.
|
|
163
|
-
*/
|
|
164
|
-
version?: number | null;
|
|
165
154
|
frontmatter?: Record<string, unknown>;
|
|
166
155
|
tokens?: TripleSplit;
|
|
167
156
|
/**
|
|
@@ -237,7 +226,7 @@ interface IssueFix {
|
|
|
237
226
|
autofixable?: boolean;
|
|
238
227
|
}
|
|
239
228
|
interface Issue {
|
|
240
|
-
|
|
229
|
+
analyzerId: string;
|
|
241
230
|
severity: Severity;
|
|
242
231
|
nodeIds: string[];
|
|
243
232
|
message: string;
|
|
@@ -381,12 +370,12 @@ interface ScanResult {
|
|
|
381
370
|
* Extension registry — six kinds, first-class, loaded through a single API.
|
|
382
371
|
*
|
|
383
372
|
* The `Extension` shape is aligned with `spec/schemas/extensions/base.schema.json`.
|
|
384
|
-
* Kind-specific manifests (provider / extractor /
|
|
373
|
+
* Kind-specific manifests (provider / extractor / analyzer / action / formatter /
|
|
385
374
|
* hook) extend this base structurally; the registry stores the base view
|
|
386
375
|
* and each kind's code carries its own fuller type where needed.
|
|
387
376
|
*
|
|
388
377
|
* **Spec § A.6 — qualified ids.** Every extension is keyed in the registry
|
|
389
|
-
* by `<pluginId>/<id>` (e.g. `core/
|
|
378
|
+
* by `<pluginId>/<id>` (e.g. `core/annotations`, `core/slash`,
|
|
390
379
|
* `hello-world/greet`). `Extension.id` carries the **short** id as authored;
|
|
391
380
|
* `Extension.pluginId` carries the namespace; the registry composes the
|
|
392
381
|
* qualifier internally and exposes lookup APIs that operate on either form
|
|
@@ -397,7 +386,7 @@ interface ScanResult {
|
|
|
397
386
|
* `kernel-empty-boot` conformance contract.
|
|
398
387
|
*/
|
|
399
388
|
|
|
400
|
-
type ExtensionKind = 'provider' | 'extractor' | '
|
|
389
|
+
type ExtensionKind = 'provider' | 'extractor' | 'analyzer' | 'action' | 'formatter' | 'hook';
|
|
401
390
|
declare const EXTENSION_KINDS: readonly ExtensionKind[];
|
|
402
391
|
interface Extension {
|
|
403
392
|
/** Short (unqualified) extension id as declared in the manifest. */
|
|
@@ -443,7 +432,7 @@ declare class Registry {
|
|
|
443
432
|
* Step 9.6.6 — runtime annotation-contribution catalog types.
|
|
444
433
|
*
|
|
445
434
|
* Lives in its own module (rather than `kernel/index.ts`) so consumers
|
|
446
|
-
* deep inside the kernel — `
|
|
435
|
+
* deep inside the kernel — `IAnalyzerContext`, the BFF route factories,
|
|
447
436
|
* future Action contexts — can depend on the catalog shape without
|
|
448
437
|
* dragging the whole kernel barrel and risking a cycle.
|
|
449
438
|
*/
|
|
@@ -463,6 +452,230 @@ interface IRegisteredAnnotationKey {
|
|
|
463
452
|
schema: Record<string, unknown>;
|
|
464
453
|
}
|
|
465
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 — `IAnalyzerContext`, 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 `TSlotName` and `TInputTypeName`
|
|
470
|
+
* mirror the closed enums in `spec/schemas/view-slots.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 slots at author time
|
|
474
|
+
* (in plugin authors' editors when their plugin imports `@skill-map/cli`)
|
|
475
|
+
* AND lets the runtime exhaustively dispatch slot → renderer in the
|
|
476
|
+
* UI without `default:` fallbacks.
|
|
477
|
+
*/
|
|
478
|
+
/**
|
|
479
|
+
* Closed enum of view slot names. Mirror of
|
|
480
|
+
* `spec/schemas/view-slots.schema.json#/$defs/SlotName`.
|
|
481
|
+
*
|
|
482
|
+
* Plugins pick one of these by name in their extension manifest's
|
|
483
|
+
* `viewContributions[<contributionId>].slot` field. The kernel
|
|
484
|
+
* validates each pick at load time (`invalid-manifest` on miss); the
|
|
485
|
+
* slot fixes both the renderer and the payload shape.
|
|
486
|
+
*/
|
|
487
|
+
type TSlotName = 'card.title.right' | 'card.subtitle.left' | 'card.footer.left.counter' | 'card.footer.right' | 'graph.node.alert' | 'inspector.header.badge.counter' | 'inspector.header.badge.tag' | 'inspector.body.panel.breakdown' | 'inspector.body.panel.records' | 'inspector.body.panel.tree' | 'inspector.body.panel.key-values' | 'inspector.body.panel.link-list' | 'inspector.body.panel.markdown' | 'topbar.actions.indicator';
|
|
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-slots.schema.json#/$defs/IViewContribution`.
|
|
506
|
+
*/
|
|
507
|
+
interface IViewContribution {
|
|
508
|
+
/**
|
|
509
|
+
* Required. Closed-catalog slot name. Unknown name rejects the
|
|
510
|
+
* extension as `invalid-manifest` at load. The slot fixes both the
|
|
511
|
+
* renderer and the payload shape; there is no separate "contract"
|
|
512
|
+
* abstraction.
|
|
513
|
+
*/
|
|
514
|
+
slot: TSlotName;
|
|
515
|
+
/**
|
|
516
|
+
* Optional human-readable label. English-only per `AGENTS.md`
|
|
517
|
+
* (`Externalized texts, not internationalized`).
|
|
518
|
+
*/
|
|
519
|
+
label?: string;
|
|
520
|
+
/** Optional hover tooltip. English-only. */
|
|
521
|
+
tooltip?: string;
|
|
522
|
+
/**
|
|
523
|
+
* Optional emoji codepoint OR PrimeIcons class id (without the
|
|
524
|
+
* `pi-` prefix). The UI discriminates: matches Unicode
|
|
525
|
+
* `\p{Extended_Pictographic}` → emoji text, otherwise → PrimeIcon.
|
|
526
|
+
* Required for counter slots and `card.title.right` (enforced by
|
|
527
|
+
* the manifest-side conditional in `view-slots.schema.json`).
|
|
528
|
+
*/
|
|
529
|
+
icon?: string;
|
|
530
|
+
/**
|
|
531
|
+
* Optional empty placeholder text shown when the payload is empty
|
|
532
|
+
* AND `emitWhenEmpty` is true. Falls back to a UI-supplied generic
|
|
533
|
+
* 'No data.' string. English-only.
|
|
534
|
+
*/
|
|
535
|
+
emptyText?: string;
|
|
536
|
+
/**
|
|
537
|
+
* When false (default), the kernel drops emissions whose payload is
|
|
538
|
+
* structurally empty so the slot stays silent. When true, the
|
|
539
|
+
* renderer surfaces an empty placeholder. Per-slot definition of
|
|
540
|
+
* "empty" lives in the slot's payload schema.
|
|
541
|
+
*/
|
|
542
|
+
emitWhenEmpty?: boolean;
|
|
543
|
+
/**
|
|
544
|
+
* Optional ordering hint (default 100). Slots configured with
|
|
545
|
+
* `order: 'priority'` sort contributions ASC by this value, with
|
|
546
|
+
* alphabetical tie-break by qualified id. The plugin uses this to
|
|
547
|
+
* suggest where its contribution belongs relative to others sharing
|
|
548
|
+
* the same slot — the slot has the final say.
|
|
549
|
+
*/
|
|
550
|
+
priority?: number;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Single row of the runtime view-contribution catalog surfaced by
|
|
554
|
+
* `kernel.getRegisteredViewContributions()`. One row per
|
|
555
|
+
* `(pluginId × extensionId × contributionId)` tuple. Composed at boot
|
|
556
|
+
* by `loadPluginRuntime` from every loaded extension's
|
|
557
|
+
* `viewContributions` map.
|
|
558
|
+
*
|
|
559
|
+
* The qualified id is `<pluginId>/<extensionId>/<contributionId>` —
|
|
560
|
+
* matches the qualified id pattern used elsewhere in the kernel
|
|
561
|
+
* (`<pluginId>/<extensionId>` for extensions; this adds the third
|
|
562
|
+
* segment for per-contribution identity).
|
|
563
|
+
*/
|
|
564
|
+
interface IRegisteredViewContribution {
|
|
565
|
+
pluginId: string;
|
|
566
|
+
extensionId: string;
|
|
567
|
+
contributionId: string;
|
|
568
|
+
slot: TSlotName;
|
|
569
|
+
/** Optional manifest-declared label (English-only). */
|
|
570
|
+
label?: string;
|
|
571
|
+
tooltip?: string;
|
|
572
|
+
icon?: string;
|
|
573
|
+
emptyText?: string;
|
|
574
|
+
emitWhenEmpty: boolean;
|
|
575
|
+
/** Manifest-declared ordering hint (default 100). See `IViewContribution.priority`. */
|
|
576
|
+
priority?: number;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Common fields on every setting declaration. The discriminated union
|
|
580
|
+
* `ISettingDeclaration` extends one of these per `type` value.
|
|
581
|
+
*/
|
|
582
|
+
interface ISettingCommon {
|
|
583
|
+
/** Required. Short human-readable label. English-only. */
|
|
584
|
+
label: string;
|
|
585
|
+
/** Optional helper text shown below the control. English-only. */
|
|
586
|
+
description?: string;
|
|
587
|
+
}
|
|
588
|
+
interface ISetting_StringList extends ISettingCommon {
|
|
589
|
+
type: 'string-list';
|
|
590
|
+
default?: string[];
|
|
591
|
+
min?: number;
|
|
592
|
+
max?: number;
|
|
593
|
+
itemMaxLength?: number;
|
|
594
|
+
}
|
|
595
|
+
interface ISetting_SingleString extends ISettingCommon {
|
|
596
|
+
type: 'single-string';
|
|
597
|
+
default?: string;
|
|
598
|
+
minLength?: number;
|
|
599
|
+
maxLength?: number;
|
|
600
|
+
/** Optional ECMAScript regex pattern (no flags). */
|
|
601
|
+
pattern?: string;
|
|
602
|
+
}
|
|
603
|
+
interface ISetting_BooleanFlag extends ISettingCommon {
|
|
604
|
+
type: 'boolean-flag';
|
|
605
|
+
default?: boolean;
|
|
606
|
+
}
|
|
607
|
+
interface ISetting_Integer extends ISettingCommon {
|
|
608
|
+
type: 'integer';
|
|
609
|
+
default?: number;
|
|
610
|
+
min?: number;
|
|
611
|
+
max?: number;
|
|
612
|
+
step?: number;
|
|
613
|
+
}
|
|
614
|
+
interface ISetting_EnumOption {
|
|
615
|
+
value: string;
|
|
616
|
+
label: string;
|
|
617
|
+
}
|
|
618
|
+
interface ISetting_EnumPick extends ISettingCommon {
|
|
619
|
+
type: 'enum-pick';
|
|
620
|
+
options: ISetting_EnumOption[];
|
|
621
|
+
default?: string;
|
|
622
|
+
}
|
|
623
|
+
interface ISetting_EnumMultipick extends ISettingCommon {
|
|
624
|
+
type: 'enum-multipick';
|
|
625
|
+
options: ISetting_EnumOption[];
|
|
626
|
+
default?: string[];
|
|
627
|
+
min?: number;
|
|
628
|
+
max?: number;
|
|
629
|
+
}
|
|
630
|
+
interface ISetting_PathGlob extends ISettingCommon {
|
|
631
|
+
type: 'path-glob';
|
|
632
|
+
default?: string;
|
|
633
|
+
/** When true, accepts string[]; when false (default), single string. */
|
|
634
|
+
multiple?: boolean;
|
|
635
|
+
}
|
|
636
|
+
interface ISetting_Regex extends ISettingCommon {
|
|
637
|
+
type: 'regex';
|
|
638
|
+
default?: string;
|
|
639
|
+
/** Subset of `gimsuy`. Default `''`. */
|
|
640
|
+
flags?: string;
|
|
641
|
+
}
|
|
642
|
+
interface ISetting_Secret extends ISettingCommon {
|
|
643
|
+
type: 'secret';
|
|
644
|
+
/**
|
|
645
|
+
* Optional uppercase-ASCII identifier. When set in the process
|
|
646
|
+
* environment, that value wins over any stored value (lets CI
|
|
647
|
+
* inject without writing to disk).
|
|
648
|
+
*/
|
|
649
|
+
envVar?: string;
|
|
650
|
+
}
|
|
651
|
+
interface ISetting_KeyValueListEntry {
|
|
652
|
+
key: string;
|
|
653
|
+
value: string;
|
|
654
|
+
}
|
|
655
|
+
interface ISetting_KeyValueList extends ISettingCommon {
|
|
656
|
+
type: 'key-value-list';
|
|
657
|
+
keyLabel?: string;
|
|
658
|
+
valueLabel?: string;
|
|
659
|
+
default?: ISetting_KeyValueListEntry[];
|
|
660
|
+
min?: number;
|
|
661
|
+
max?: number;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Discriminated union of every setting declaration shape. The plugin
|
|
665
|
+
* author NEVER writes JSON Schema for settings — they pick one of
|
|
666
|
+
* these `type` values and supply per-type parameters.
|
|
667
|
+
*
|
|
668
|
+
* Mirror of `input-types.schema.json#/$defs/ISettingDeclaration`.
|
|
669
|
+
*/
|
|
670
|
+
type ISettingDeclaration = ISetting_StringList | ISetting_SingleString | ISetting_BooleanFlag | ISetting_Integer | ISetting_EnumPick | ISetting_EnumMultipick | ISetting_PathGlob | ISetting_Regex | ISetting_Secret | ISetting_KeyValueList;
|
|
671
|
+
/**
|
|
672
|
+
* Runtime value type for a setting, derived from its declaration. The
|
|
673
|
+
* kernel exposes settings to extractors as `Record<string, TSettingValue>`
|
|
674
|
+
* via `ctx.settings.<settingId>`; consumers that want narrow typing
|
|
675
|
+
* narrow at the call site by reading `manifest.settings[id].type`.
|
|
676
|
+
*/
|
|
677
|
+
type TSettingValue = string | string[] | boolean | number | ISetting_KeyValueListEntry[];
|
|
678
|
+
|
|
466
679
|
/**
|
|
467
680
|
* Base manifest shape shared by every extension kind. Mirrors
|
|
468
681
|
* `spec/schemas/extensions/base.schema.json` at the TypeScript level.
|
|
@@ -529,6 +742,21 @@ interface IExtensionBase {
|
|
|
529
742
|
* `plugin-author-guide.md` §Annotation contributions for examples.
|
|
530
743
|
*/
|
|
531
744
|
annotationContributions?: Record<string, IAnnotationContribution>;
|
|
745
|
+
/**
|
|
746
|
+
* Plugin-contributed view contributions. Each entry maps a local
|
|
747
|
+
* contribution id (kebab-case, unique within the extension) to a
|
|
748
|
+
* `IViewContribution` declaration that picks a view slot by name
|
|
749
|
+
* from the closed kernel catalog (`view-catalog.ts#TSlotName`).
|
|
750
|
+
* The slot fixes both the renderer and the payload shape — there
|
|
751
|
+
* is no separate "contract" abstraction. The kernel validates each
|
|
752
|
+
* `slot` pick at load time (`invalid-manifest` on miss); the plugin
|
|
753
|
+
* emits per-node payloads via `ctx.emitContribution(<contributionId>,
|
|
754
|
+
* payload)` during scan; the runtime validates payloads against
|
|
755
|
+
* the slot's payload schema. The aggregate runtime catalog is
|
|
756
|
+
* exposed via `kernel.getRegisteredViewContributions()`. See
|
|
757
|
+
* `architecture.md` §View contribution system.
|
|
758
|
+
*/
|
|
759
|
+
viewContributions?: Record<string, IViewContribution>;
|
|
532
760
|
}
|
|
533
761
|
|
|
534
762
|
/**
|
|
@@ -643,6 +871,16 @@ interface IPluginManifest {
|
|
|
643
871
|
id: string;
|
|
644
872
|
version: string;
|
|
645
873
|
specCompat: string;
|
|
874
|
+
/**
|
|
875
|
+
* Optional semver range against the kernel's view-slots +
|
|
876
|
+
* input-types catalog version. Independent from `specCompat` because
|
|
877
|
+
* the catalog evolves on its own cadence (see `architecture.md`
|
|
878
|
+
* §View contribution system → Catalog versioning). Mismatch surfaces
|
|
879
|
+
* as `incompatible-catalog`. Absent = the plugin opts out of catalog
|
|
880
|
+
* checking; `sm plugins doctor` warns if such a plugin actually
|
|
881
|
+
* declares `viewContributions` or `settings`.
|
|
882
|
+
*/
|
|
883
|
+
catalogCompat?: string;
|
|
646
884
|
extensions: string[];
|
|
647
885
|
description?: string;
|
|
648
886
|
storage?: TPluginStorage;
|
|
@@ -652,6 +890,18 @@ interface IPluginManifest {
|
|
|
652
890
|
* the default.
|
|
653
891
|
*/
|
|
654
892
|
granularity?: TGranularity;
|
|
893
|
+
/**
|
|
894
|
+
* Plugin user-configurable settings. Each entry picks an `input-type`
|
|
895
|
+
* from the closed catalog at
|
|
896
|
+
* `spec/schemas/input-types.schema.json#/$defs/InputTypeName`.
|
|
897
|
+
* The plugin author NEVER writes JSON Schema — they pick `type` by
|
|
898
|
+
* name and supply per-type parameters. The kernel exposes resolved
|
|
899
|
+
* settings to extractors via `ctx.settings.<settingId>`; settings
|
|
900
|
+
* are read once at extractor invocation; changing a setting requires
|
|
901
|
+
* `sm scan` to re-emit. See `architecture.md` §View contribution
|
|
902
|
+
* system → Settings.
|
|
903
|
+
*/
|
|
904
|
+
settings?: Record<string, ISettingDeclaration>;
|
|
655
905
|
author?: string;
|
|
656
906
|
license?: string;
|
|
657
907
|
homepage?: string;
|
|
@@ -692,7 +942,7 @@ interface IPluginManifest {
|
|
|
692
942
|
* precedence rule applies. The user resolves
|
|
693
943
|
* by renaming one of them and rerunning.
|
|
694
944
|
*/
|
|
695
|
-
type TPluginLoadStatus = 'enabled' | 'disabled' | 'incompatible-spec' | 'invalid-manifest' | 'load-error' | 'id-collision';
|
|
945
|
+
type TPluginLoadStatus = 'enabled' | 'disabled' | 'incompatible-spec' | 'incompatible-catalog' | 'invalid-manifest' | 'load-error' | 'id-collision';
|
|
696
946
|
interface ILoadedExtension {
|
|
697
947
|
kind: ExtensionKind;
|
|
698
948
|
id: string;
|
|
@@ -884,6 +1134,63 @@ declare function makePluginStore(opts: {
|
|
|
884
1134
|
persistDedicated?: IDedicatedStorePersist;
|
|
885
1135
|
}): IPluginStore | undefined;
|
|
886
1136
|
|
|
1137
|
+
/**
|
|
1138
|
+
* `scan_contributions` adapter — replace-all writer used by
|
|
1139
|
+
* `persistScanResult`, plus read helpers consumed by the BFF
|
|
1140
|
+
* (`/api/contributions/...`) and rules (`core/contribution-orphan`).
|
|
1141
|
+
*
|
|
1142
|
+
* One row per `(plugin_id, extension_id, node_path, contribution_id)`
|
|
1143
|
+
* tuple. See `spec/architecture.md` § View contribution system →
|
|
1144
|
+
* Persistence and `migrations/001_initial.sql` § View contribution
|
|
1145
|
+
* layer for the normative shape.
|
|
1146
|
+
*
|
|
1147
|
+
* Replace-all semantics mirror the rest of the `scan_*` zone: every
|
|
1148
|
+
* scan is a fresh snapshot, so prior rows are deleted before insert.
|
|
1149
|
+
* Wrapped in the same transaction `persistScanResult` opens.
|
|
1150
|
+
*
|
|
1151
|
+
* The rename heuristic does NOT need to migrate `node_path` here —
|
|
1152
|
+
* because of replace-all, every contribution is re-emitted on the new
|
|
1153
|
+
* path automatically. Keeping the rename path lighter than `state_*`
|
|
1154
|
+
* (which IS rename-migrated because state survives across scans).
|
|
1155
|
+
*/
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* In-memory contribution record buffered during scan and flushed to
|
|
1159
|
+
* `scan_contributions` by `persistScanResult`. One entry per accepted
|
|
1160
|
+
* `ctx.emitContribution(id, payload)` call. Payload validation against
|
|
1161
|
+
* the slot's payload schema happens at emit time (orchestrator);
|
|
1162
|
+
* by the time records reach this adapter they are wire-shape clean.
|
|
1163
|
+
*/
|
|
1164
|
+
interface IContributionRecord {
|
|
1165
|
+
pluginId: string;
|
|
1166
|
+
extensionId: string;
|
|
1167
|
+
nodePath: string;
|
|
1168
|
+
contributionId: string;
|
|
1169
|
+
/**
|
|
1170
|
+
* Closed enum value mirroring `view-slots.schema.json#/$defs/SlotName`.
|
|
1171
|
+
* Persisted as TEXT (no SQL CHECK by design — see migration comment).
|
|
1172
|
+
*/
|
|
1173
|
+
slot: string;
|
|
1174
|
+
/** Already-validated payload. Serialised via `JSON.stringify` at write. */
|
|
1175
|
+
payload: unknown;
|
|
1176
|
+
emittedAt: number;
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Single contribution row as returned to callers. The payload is
|
|
1180
|
+
* `unknown` because the slot space is open at the type layer (catalog
|
|
1181
|
+
* evolution is a kernel + spec concern); narrow at the call site by
|
|
1182
|
+
* reading `slot`.
|
|
1183
|
+
*/
|
|
1184
|
+
interface IPersistedContribution {
|
|
1185
|
+
pluginId: string;
|
|
1186
|
+
extensionId: string;
|
|
1187
|
+
nodePath: string;
|
|
1188
|
+
contributionId: string;
|
|
1189
|
+
slot: string;
|
|
1190
|
+
payload: unknown;
|
|
1191
|
+
emittedAt: number;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
887
1194
|
/**
|
|
888
1195
|
* Provider runtime contract. Walks filesystem roots and emits raw node
|
|
889
1196
|
* records; classification maps path conventions to a node kind.
|
|
@@ -1157,6 +1464,11 @@ interface IProviderReadConfig {
|
|
|
1157
1464
|
* a return value. Extractors run in isolation: they MUST NOT read other
|
|
1158
1465
|
* nodes, the graph, or the DB. Cross-node reasoning lives in rules.
|
|
1159
1466
|
*
|
|
1467
|
+
* Extractors are deterministic-only. They run synchronously inside the
|
|
1468
|
+
* scan loop; LLM-driven enrichment of a node is an Action concern, not
|
|
1469
|
+
* an Extractor concern. The Extractor context therefore exposes no
|
|
1470
|
+
* `RunnerPort` — see spec `architecture.md` §Execution modes.
|
|
1471
|
+
*
|
|
1160
1472
|
* Output channels (all on the context):
|
|
1161
1473
|
*
|
|
1162
1474
|
* - `ctx.emitLink(link)` — persist a link in the kernel's `links` table.
|
|
@@ -1164,14 +1476,12 @@ interface IProviderReadConfig {
|
|
|
1164
1476
|
* kind drops the link and surfaces an `extension.error` event.
|
|
1165
1477
|
* - `ctx.enrichNode(partial)` — merge canonical, kernel-curated properties
|
|
1166
1478
|
* onto the node. Strictly separate from the author-supplied frontmatter
|
|
1167
|
-
* (the latter remains immutable and survives verbatim). Persistence
|
|
1168
|
-
*
|
|
1479
|
+
* (the latter remains immutable and survives verbatim). Persistence
|
|
1480
|
+
* is spec'd in § A.8.
|
|
1169
1481
|
* - `ctx.store` — plugin-scoped persistence. Present only when the
|
|
1170
1482
|
* plugin declares `storage.mode` in `plugin.json`; shape depends on the
|
|
1171
1483
|
* mode (`KvStore` for mode A, scoped `Database` for mode B). See
|
|
1172
1484
|
* `plugin-kv-api.md` for the contract.
|
|
1173
|
-
* - `ctx.runner` — `RunnerPort` injection for `probabilistic` extractors.
|
|
1174
|
-
* `undefined` for the default `deterministic` mode.
|
|
1175
1485
|
*
|
|
1176
1486
|
* The manifest's `scope` field tells the orchestrator which parts to feed:
|
|
1177
1487
|
* `frontmatter` extractors receive an empty string for body and vice versa.
|
|
@@ -1202,6 +1512,22 @@ interface IExtractorCallbacks {
|
|
|
1202
1512
|
* partials and `persistScanResult` upserts them.
|
|
1203
1513
|
*/
|
|
1204
1514
|
enrichNode(partial: Partial<Node>): void;
|
|
1515
|
+
/**
|
|
1516
|
+
* Emit a per-node view contribution. The first argument is the
|
|
1517
|
+
* extension-local Record key declared under
|
|
1518
|
+
* `extension.viewContributions[<contributionId>]`; the second is a
|
|
1519
|
+
* payload that conforms to the slot's payload schema in
|
|
1520
|
+
* `spec/schemas/view-slots.schema.json#/$defs/payloads/<slot>` —
|
|
1521
|
+
* where `<slot>` is the slot the manifest declared for this
|
|
1522
|
+
* contribution. The orchestrator validates the payload against the
|
|
1523
|
+
* slot's schema before persisting to `scan_contributions`; off-shape
|
|
1524
|
+
* payloads are silently dropped with an `extension.error` event
|
|
1525
|
+
* (mirror of `emitLink` rejecting off-`emitsLinkKinds` links).
|
|
1526
|
+
* Calling `emitContribution` with a `contributionId` that is not
|
|
1527
|
+
* declared in the manifest is also dropped with an `extension.error`.
|
|
1528
|
+
* See `architecture.md` §View contribution system → Emit path.
|
|
1529
|
+
*/
|
|
1530
|
+
emitContribution(contributionId: string, payload: unknown): void;
|
|
1205
1531
|
}
|
|
1206
1532
|
interface IExtractorContext extends IExtractorCallbacks {
|
|
1207
1533
|
node: Node;
|
|
@@ -1222,33 +1548,17 @@ interface IExtractorContext extends IExtractorCallbacks {
|
|
|
1222
1548
|
* it here.
|
|
1223
1549
|
*/
|
|
1224
1550
|
store?: unknown;
|
|
1225
|
-
/**
|
|
1226
|
-
* `RunnerPort` injection for `probabilistic` extractors. `undefined`
|
|
1227
|
-
* for `deterministic` mode (the default). The kernel rejects
|
|
1228
|
-
* probabilistic extractors that try to register scan-time hooks at
|
|
1229
|
-
* load time.
|
|
1230
|
-
*/
|
|
1231
|
-
runner?: unknown;
|
|
1232
1551
|
}
|
|
1233
1552
|
interface IExtractor extends IExtensionBase {
|
|
1234
1553
|
kind: 'extractor';
|
|
1235
|
-
/**
|
|
1236
|
-
* Execution mode. Optional in the manifest with a default of
|
|
1237
|
-
* `deterministic` per `spec/schemas/extensions/extractor.schema.json`.
|
|
1238
|
-
* `probabilistic` extractors invoke an LLM through the kernel's
|
|
1239
|
-
* `RunnerPort` and never participate in scan-time pipelines —
|
|
1240
|
-
* they dispatch only as queued jobs.
|
|
1241
|
-
*/
|
|
1242
|
-
mode?: TExecutionMode;
|
|
1243
1554
|
emitsLinkKinds: LinkKind[];
|
|
1244
1555
|
defaultConfidence: Confidence;
|
|
1245
1556
|
scope: 'frontmatter' | 'body' | 'both';
|
|
1246
1557
|
/**
|
|
1247
1558
|
* Optional opt-in filter on `node.kind`. When declared, the orchestrator
|
|
1248
1559
|
* skips invocation of `extract()` for any node whose `kind` is NOT in
|
|
1249
|
-
* this list — fail-fast, before context construction, so
|
|
1250
|
-
*
|
|
1251
|
-
* and a deterministic extractor wastes zero CPU.
|
|
1560
|
+
* this list — fail-fast, before context construction, so the extractor
|
|
1561
|
+
* wastes zero CPU on inapplicable nodes.
|
|
1252
1562
|
*
|
|
1253
1563
|
* Absent (`undefined`) is the default: the extractor applies to every
|
|
1254
1564
|
* kind. There are no wildcards — the absence of the field already
|
|
@@ -1271,28 +1581,29 @@ interface IExtractor extends IExtensionBase {
|
|
|
1271
1581
|
}
|
|
1272
1582
|
|
|
1273
1583
|
/**
|
|
1274
|
-
*
|
|
1275
|
-
* and extractor has completed; emits issues
|
|
1276
|
-
*
|
|
1277
|
-
*
|
|
1278
|
-
* `
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
1584
|
+
* Analyzer runtime contract. Runs against the whole graph after every
|
|
1585
|
+
* Provider and extractor has completed; emits issues and MAY project
|
|
1586
|
+
* findings into the UI via view contributions. Deterministic analyzers
|
|
1587
|
+
* are pure (same graph in → same issues out) and run synchronously
|
|
1588
|
+
* inside `sm scan` / `sm check`. Probabilistic analyzers invoke an LLM
|
|
1589
|
+
* through the kernel's `RunnerPort` and dispatch only as queued jobs —
|
|
1590
|
+
* they never participate in scan-time pipelines. Mode is declared in
|
|
1591
|
+
* the manifest (default `deterministic`).
|
|
1281
1592
|
*/
|
|
1282
1593
|
|
|
1283
1594
|
/**
|
|
1284
|
-
* Step 9.6.2 — orphan sidecar entry surfaced to
|
|
1595
|
+
* Step 9.6.2 — orphan sidecar entry surfaced to analyzers. A `.sm` file
|
|
1285
1596
|
* whose sibling `.md` does not exist on disk; the `annotation-orphan`
|
|
1286
|
-
* built-in
|
|
1287
|
-
* about orphan sidecars MAY consume the list too.
|
|
1597
|
+
* built-in analyzer emits one warning per entry. Other analyzers that
|
|
1598
|
+
* care about orphan sidecars MAY consume the list too.
|
|
1288
1599
|
*/
|
|
1289
|
-
interface
|
|
1600
|
+
interface IAnalyzerOrphanSidecar {
|
|
1290
1601
|
/** Relative path (POSIX-separated) of the orphan `.sm`. */
|
|
1291
1602
|
relativePath: string;
|
|
1292
1603
|
/** Absolute path of the missing `.md` the sidecar was anchored to. */
|
|
1293
1604
|
expectedMdPath: string;
|
|
1294
1605
|
}
|
|
1295
|
-
interface
|
|
1606
|
+
interface IAnalyzerContext {
|
|
1296
1607
|
nodes: Node[];
|
|
1297
1608
|
links: Link[];
|
|
1298
1609
|
/**
|
|
@@ -1300,35 +1611,101 @@ interface IRuleContext {
|
|
|
1300
1611
|
* Empty when sidecar discovery did not run (legacy callers) or
|
|
1301
1612
|
* when no orphans exist.
|
|
1302
1613
|
*/
|
|
1303
|
-
orphanSidecars?:
|
|
1614
|
+
orphanSidecars?: IAnalyzerOrphanSidecar[];
|
|
1304
1615
|
/**
|
|
1305
1616
|
* Step 9.6.6 — raw parsed sidecar root keyed by `node.path`. Populated
|
|
1306
1617
|
* by the orchestrator alongside the public `Node.sidecar` overlay so
|
|
1307
|
-
*
|
|
1308
|
-
* `core/unknown-field`
|
|
1309
|
-
* the file from disk. Absent (or `undefined` per node)
|
|
1310
|
-
* sidecar accompanies the node, or when the sidecar failed
|
|
1311
|
-
* Treat as read-only.
|
|
1618
|
+
* analyzers that inspect plugin namespaces (e.g. the built-in
|
|
1619
|
+
* `core/unknown-field` Analyzer) can walk the full tree without
|
|
1620
|
+
* re-reading the file from disk. Absent (or `undefined` per node)
|
|
1621
|
+
* when no sidecar accompanies the node, or when the sidecar failed
|
|
1622
|
+
* to parse. Treat as read-only.
|
|
1312
1623
|
*/
|
|
1313
1624
|
sidecarRoots?: ReadonlyMap<string, Record<string, unknown>>;
|
|
1314
1625
|
/**
|
|
1315
1626
|
* Step 9.6.6 — runtime catalog of plugin-contributed annotation keys,
|
|
1316
1627
|
* as exposed by `kernel.getRegisteredAnnotationKeys()`. Threaded
|
|
1317
|
-
* through so
|
|
1318
|
-
* without reaching back into the kernel. Empty array when no
|
|
1319
|
-
* declares contributions; absent for legacy callers (older
|
|
1320
|
-
* sites that never wired the catalog through).
|
|
1628
|
+
* through so analyzers can reason about the registered-vs-unknown
|
|
1629
|
+
* split without reaching back into the kernel. Empty array when no
|
|
1630
|
+
* plugin declares contributions; absent for legacy callers (older
|
|
1631
|
+
* runScan sites that never wired the catalog through).
|
|
1321
1632
|
*/
|
|
1322
1633
|
annotationContributions?: readonly IRegisteredAnnotationKey[];
|
|
1634
|
+
/**
|
|
1635
|
+
* Step 11.x — runtime catalog of plugin-contributed view contributions,
|
|
1636
|
+
* as exposed by `kernel.getRegisteredViewContributions()`. Threaded
|
|
1637
|
+
* through so analyzers can reason about emissions without reaching
|
|
1638
|
+
* back into the kernel: built-in `core/unknown-slot` walks this list
|
|
1639
|
+
* to detect deprecated slots in use, and `core/contribution-orphan`
|
|
1640
|
+
* joins it with the live node set to flag dangling emissions. Empty
|
|
1641
|
+
* array when no extension declares view contributions; absent for
|
|
1642
|
+
* legacy callers (older runScan sites that never wired the catalog
|
|
1643
|
+
* through).
|
|
1644
|
+
*/
|
|
1645
|
+
viewContributions?: readonly IRegisteredViewContribution[];
|
|
1646
|
+
/**
|
|
1647
|
+
* Absolute paths of `*.md` files under the project's
|
|
1648
|
+
* `.skill-map/jobs/` that no `state_jobs.filePath` references — the
|
|
1649
|
+
* built-in `core/job-orphan-file` analyzer projects each as a `warn`
|
|
1650
|
+
* issue. Pre-computed by the driving adapter (CLI / BFF) inside its
|
|
1651
|
+
* already-open storage transaction (mirrors the `orphanSidecars`
|
|
1652
|
+
* pattern: detection lives outside the analyzer, the analyzer only
|
|
1653
|
+
* projects). Absent (or empty) when the caller does not maintain a
|
|
1654
|
+
* jobs directory, when the storage path is unavailable, or when no
|
|
1655
|
+
* orphan files exist. Treat as read-only.
|
|
1656
|
+
*/
|
|
1657
|
+
orphanJobFiles?: readonly string[];
|
|
1658
|
+
/**
|
|
1659
|
+
* Set of absolute file paths the operator has opted into for
|
|
1660
|
+
* link-validation purposes via `scan.referencePaths`. The driving
|
|
1661
|
+
* adapter walks each configured path before the scan and collects
|
|
1662
|
+
* every existing file's absolute path here. Files in this set are
|
|
1663
|
+
* NOT indexed as graph nodes — the only consumer is
|
|
1664
|
+
* `core/broken-ref`, which suppresses its `warn` issue when a
|
|
1665
|
+
* path-style link target falls into the set. Absent / empty when
|
|
1666
|
+
* the operator left `scan.referencePaths` empty or when the
|
|
1667
|
+
* adapter does not maintain the side index. Treat as read-only.
|
|
1668
|
+
*/
|
|
1669
|
+
referenceablePaths?: ReadonlySet<string>;
|
|
1670
|
+
/**
|
|
1671
|
+
* Absolute path of the scan's project root (cwd of the invocation).
|
|
1672
|
+
* Threaded into the analyzer pass so an analyzer that needs to
|
|
1673
|
+
* resolve a relative `link.target` to an absolute filesystem path
|
|
1674
|
+
* (today only `core/broken-ref`, when consulting
|
|
1675
|
+
* `referenceablePaths`) does not have to derive it from
|
|
1676
|
+
* `nodes[0].path` heuristics. Absent for legacy callers (older
|
|
1677
|
+
* `runScan` sites that never wired the field through). Always an
|
|
1678
|
+
* absolute path when present.
|
|
1679
|
+
*/
|
|
1680
|
+
cwd?: string;
|
|
1681
|
+
/**
|
|
1682
|
+
* Emit a per-node view contribution declared in this analyzer's
|
|
1683
|
+
* manifest `viewContributions` map. Sync, void return; the
|
|
1684
|
+
* orchestrator validates the payload against the slot's schema at
|
|
1685
|
+
* call time and silently drops invalid emissions with a logged
|
|
1686
|
+
* `extension.error` event (parallel to
|
|
1687
|
+
* `IExtractorCallbacks.emitContribution`).
|
|
1688
|
+
*
|
|
1689
|
+
* Unlike Extractor's emit (which binds `nodePath` from `ctx.node.path`
|
|
1690
|
+
* implicitly because Extractors run per-node), Analyzer's `evaluate()`
|
|
1691
|
+
* sees the full graph at once. The analyzer walks `ctx.nodes` itself
|
|
1692
|
+
* and MUST supply the target node path explicitly per emission.
|
|
1693
|
+
*
|
|
1694
|
+
* Calling `emitContribution` with a `contributionId` that is not
|
|
1695
|
+
* declared in the manifest is dropped with an `extension.error`. The
|
|
1696
|
+
* kernel routes emitted contributions to the same persistence
|
|
1697
|
+
* pipeline as Extractor emissions (`scan_contributions`).
|
|
1698
|
+
*/
|
|
1699
|
+
emitContribution(nodePath: string, contributionId: string, payload: unknown): void;
|
|
1323
1700
|
}
|
|
1324
|
-
interface
|
|
1325
|
-
kind: '
|
|
1701
|
+
interface IAnalyzer extends IExtensionBase {
|
|
1702
|
+
kind: 'analyzer';
|
|
1326
1703
|
/**
|
|
1327
1704
|
* Execution mode. Optional in the manifest with a default of
|
|
1328
|
-
* `deterministic` per `spec/schemas/extensions/
|
|
1705
|
+
* `deterministic` per `spec/schemas/extensions/analyzer.schema.json`.
|
|
1329
1706
|
*/
|
|
1330
1707
|
mode?: TExecutionMode;
|
|
1331
|
-
evaluate(ctx:
|
|
1708
|
+
evaluate(ctx: IAnalyzerContext): Issue[] | Promise<Issue[]>;
|
|
1332
1709
|
}
|
|
1333
1710
|
|
|
1334
1711
|
/**
|
|
@@ -1558,12 +1935,16 @@ interface IFormatter extends IExtensionBase {
|
|
|
1558
1935
|
* are notification (Slack on `job.completed`), integration glue (CI
|
|
1559
1936
|
* webhook on `job.failed`), and bookkeeping (per-extractor metrics).
|
|
1560
1937
|
*
|
|
1561
|
-
* The hookable trigger set is INTENTIONALLY SMALL —
|
|
1562
|
-
*
|
|
1563
|
-
* `
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1938
|
+
* The hookable trigger set is INTENTIONALLY SMALL — ten events. Eight
|
|
1939
|
+
* are pipeline-driven (emitted from inside `runScan`); two
|
|
1940
|
+
* (`boot`, `shutdown`) are CLI-process-driven (emitted by the driving
|
|
1941
|
+
* binary before / after the verb runs, fire-and-forget so
|
|
1942
|
+
* `process.exit` is never blocked). The full `ProgressEmitterPort`
|
|
1943
|
+
* catalog (per-node `scan.progress`, `model.delta`, `run.*`, internal
|
|
1944
|
+
* job lifecycle) is deliberately not hookable: too verbose for a
|
|
1945
|
+
* reactive surface, internal to the runner, or covered elsewhere.
|
|
1946
|
+
* Declaring a trigger outside the curated set yields
|
|
1947
|
+
* `invalid-manifest` at load time.
|
|
1567
1948
|
*
|
|
1568
1949
|
* Dual-mode (declared in manifest):
|
|
1569
1950
|
*
|
|
@@ -1578,27 +1959,35 @@ interface IFormatter extends IExtensionBase {
|
|
|
1578
1959
|
*
|
|
1579
1960
|
* Curated trigger set (per spec § A.11):
|
|
1580
1961
|
*
|
|
1962
|
+
* 0. `boot` — once per CLI process, before verb routing.
|
|
1581
1963
|
* 1. `scan.started` — pre-scan setup (one per scan).
|
|
1582
1964
|
* 2. `scan.completed` — post-scan reaction (one per scan).
|
|
1583
1965
|
* 3. `extractor.completed` — aggregated per-Extractor outputs.
|
|
1584
|
-
* 4. `
|
|
1966
|
+
* 4. `analyzer.completed` — aggregated per-Rule outputs.
|
|
1585
1967
|
* 5. `action.completed` — Action executed on a node.
|
|
1586
1968
|
* 6. `job.spawning` — pre-spawn of runner subprocess.
|
|
1587
1969
|
* 7. `job.completed` — most common trigger.
|
|
1588
1970
|
* 8. `job.failed` — alerts, retry triggers.
|
|
1971
|
+
* 9. `shutdown` — once per CLI process, after the verb's
|
|
1972
|
+
* exit code resolves and before
|
|
1973
|
+
* `process.exit`.
|
|
1589
1974
|
*/
|
|
1590
1975
|
|
|
1591
1976
|
/**
|
|
1592
|
-
* The
|
|
1593
|
-
* `spec/schemas/extensions/hook.schema.json`.
|
|
1594
|
-
*
|
|
1977
|
+
* The ten hookable lifecycle events. Mirrors the `triggers[]` enum in
|
|
1978
|
+
* `spec/schemas/extensions/hook.schema.json`. Eight are pipeline-driven
|
|
1979
|
+
* (emitted from inside `runScan`); two (`boot`, `shutdown`) are
|
|
1980
|
+
* CLI-process-driven (emitted by the driving binary before / after the
|
|
1981
|
+
* verb runs). Anything outside this set is rejected at load time as
|
|
1982
|
+
* `invalid-manifest`.
|
|
1595
1983
|
*/
|
|
1596
|
-
type THookTrigger = 'scan.started' | 'scan.completed' | 'extractor.completed' | '
|
|
1984
|
+
type THookTrigger = 'boot' | 'scan.started' | 'scan.completed' | 'extractor.completed' | 'analyzer.completed' | 'action.completed' | 'job.spawning' | 'job.completed' | 'job.failed' | 'shutdown';
|
|
1597
1985
|
/**
|
|
1598
1986
|
* Frozen list mirror of `THookTrigger` for runtime introspection. The
|
|
1599
1987
|
* loader validates `manifest.triggers[]` against this set; the
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1988
|
+
* dispatcher iterates it in order when fanning an event out to
|
|
1989
|
+
* subscribed hooks. `boot` first / `shutdown` last so a debug log of
|
|
1990
|
+
* the array reads in lifecycle order.
|
|
1602
1991
|
*/
|
|
1603
1992
|
declare const HOOK_TRIGGERS: readonly THookTrigger[];
|
|
1604
1993
|
/**
|
|
@@ -1607,7 +1996,7 @@ declare const HOOK_TRIGGERS: readonly THookTrigger[];
|
|
|
1607
1996
|
*
|
|
1608
1997
|
* The `event` carries the raw `ProgressEvent` envelope (type, timestamp,
|
|
1609
1998
|
* runId/jobId when applicable, data). Optional `node` / `extractorId`
|
|
1610
|
-
* / `
|
|
1999
|
+
* / `analyzerId` / `actionId` are extracted from the event payload by the
|
|
1611
2000
|
* dispatcher when present so authors don't have to walk `event.data`.
|
|
1612
2001
|
*
|
|
1613
2002
|
* Probabilistic hooks additionally receive `runner` for LLM dispatch.
|
|
@@ -1634,9 +2023,9 @@ interface IHookContext {
|
|
|
1634
2023
|
*/
|
|
1635
2024
|
extractorId?: string;
|
|
1636
2025
|
/**
|
|
1637
|
-
* Set on `
|
|
2026
|
+
* Set on `analyzer.completed` events. Qualified extension id of the Rule.
|
|
1638
2027
|
*/
|
|
1639
|
-
|
|
2028
|
+
analyzerId?: string;
|
|
1640
2029
|
/**
|
|
1641
2030
|
* Set on `action.completed` events. Qualified extension id of the
|
|
1642
2031
|
* Action that just ran.
|
|
@@ -1704,7 +2093,7 @@ interface IHook extends IExtensionBase {
|
|
|
1704
2093
|
}
|
|
1705
2094
|
|
|
1706
2095
|
/**
|
|
1707
|
-
* Scan orchestrator — runs the Provider → extractor →
|
|
2096
|
+
* Scan orchestrator — runs the Provider → extractor → analyzer pipeline across
|
|
1708
2097
|
* every registered extension and emits `ProgressEmitterPort` events in
|
|
1709
2098
|
* canonical order. The callable extension set is injected via
|
|
1710
2099
|
* `RunScanOptions.extensions` — the Registry holds manifest metadata, the
|
|
@@ -1757,7 +2146,7 @@ interface IHook extends IExtensionBase {
|
|
|
1757
2146
|
interface IScanExtensions {
|
|
1758
2147
|
providers: IProvider[];
|
|
1759
2148
|
extractors: IExtractor[];
|
|
1760
|
-
|
|
2149
|
+
analyzers: IAnalyzer[];
|
|
1761
2150
|
/**
|
|
1762
2151
|
* Optional hooks (spec § A.11). When supplied, the orchestrator's
|
|
1763
2152
|
* lifecycle dispatcher invokes deterministic hooks subscribed to one
|
|
@@ -1800,6 +2189,20 @@ interface RunScanOptions {
|
|
|
1800
2189
|
* inside the rule.
|
|
1801
2190
|
*/
|
|
1802
2191
|
annotationContributions?: readonly IRegisteredAnnotationKey[];
|
|
2192
|
+
/**
|
|
2193
|
+
* Runtime catalog of plugin-contributed view contributions (the same
|
|
2194
|
+
* shape `kernel.getRegisteredViewContributions()` returns). Threaded
|
|
2195
|
+
* into the rule pass so:
|
|
2196
|
+
* - `core/unknown-slot` and `core/contribution-orphan` can
|
|
2197
|
+
* introspect the catalog (read-only).
|
|
2198
|
+
* - The orchestrator's per-rule emit closure can look up each
|
|
2199
|
+
* declared `(contributionId → slot)` pairing for AJV
|
|
2200
|
+
* payload validation.
|
|
2201
|
+
* Absent → empty catalog. Rules that emit contributions silently
|
|
2202
|
+
* drop emissions when the catalog has no entry for the rule's
|
|
2203
|
+
* declared contributionId.
|
|
2204
|
+
*/
|
|
2205
|
+
viewContributions?: readonly IRegisteredViewContribution[];
|
|
1803
2206
|
/**
|
|
1804
2207
|
* Scan scope. Defaults to `'project'`. The CLI flag wiring lands in
|
|
1805
2208
|
* the config layer wiring; `runScan` already accepts the override
|
|
@@ -1893,6 +2296,40 @@ interface RunScanOptions {
|
|
|
1893
2296
|
* mock without spinning up a DB.
|
|
1894
2297
|
*/
|
|
1895
2298
|
pluginStores?: ReadonlyMap<string, IPluginStore>;
|
|
2299
|
+
/**
|
|
2300
|
+
* Pre-computed absolute paths of orphan job MD files (files under
|
|
2301
|
+
* `.skill-map/jobs/` whose absolute path appears nowhere in
|
|
2302
|
+
* `state_jobs.filePath`). Threaded into the rule pass so the
|
|
2303
|
+
* built-in `core/job-orphan-file` rule can project each as a `warn`
|
|
2304
|
+
* issue without the kernel reaching for the storage port or doing
|
|
2305
|
+
* its own FS walk. The driving adapter (CLI, BFF) computes this
|
|
2306
|
+
* inside its already-open storage transaction via
|
|
2307
|
+
* `findOrphanJobFiles(jobsDir, await port.jobs.listReferencedFilePaths())`
|
|
2308
|
+
* — mirrors the `orphanSidecars` model where detection lives
|
|
2309
|
+
* outside the rule and the rule only projects. Absent / empty when
|
|
2310
|
+
* the caller has no jobs context (out-of-band tests, fresh DB,
|
|
2311
|
+
* `--no-built-ins`).
|
|
2312
|
+
*/
|
|
2313
|
+
orphanJobFiles?: readonly string[];
|
|
2314
|
+
/**
|
|
2315
|
+
* Side set of absolute file paths the operator opted into for
|
|
2316
|
+
* link-validation purposes via `scan.referencePaths`. Threaded
|
|
2317
|
+
* through to `IAnalyzerContext.referenceablePaths` so the built-in
|
|
2318
|
+
* `core/broken-ref` rule can suppress its `warn` for path-style
|
|
2319
|
+
* links whose target lands in the set. Files are NOT walked by
|
|
2320
|
+
* the kernel — the driving adapter populates the set before
|
|
2321
|
+
* calling `runScan`. Absent / empty when the operator left
|
|
2322
|
+
* `scan.referencePaths` unconfigured.
|
|
2323
|
+
*/
|
|
2324
|
+
referenceablePaths?: ReadonlySet<string>;
|
|
2325
|
+
/**
|
|
2326
|
+
* Absolute path of the scan's cwd / project root. Threaded onto
|
|
2327
|
+
* `IAnalyzerContext.cwd` so rules that need to resolve a relative
|
|
2328
|
+
* `link.target` to an absolute filesystem path can do so without
|
|
2329
|
+
* heuristics. Absent for callers that don't track a cwd
|
|
2330
|
+
* concept (out-of-band tests, embedders).
|
|
2331
|
+
*/
|
|
2332
|
+
cwd?: string;
|
|
1896
2333
|
}
|
|
1897
2334
|
/**
|
|
1898
2335
|
* Spec § A.9 — runs to persist into `scan_extractor_runs`. One entry
|
|
@@ -1919,8 +2356,6 @@ interface IExtractorRunRecord {
|
|
|
1919
2356
|
*
|
|
1920
2357
|
* - upsert a single row per pair (stable PRIMARY KEY conflict on
|
|
1921
2358
|
* re-extract);
|
|
1922
|
-
* - flag probabilistic rows `stale = 1` when the body changes between
|
|
1923
|
-
* scans (preserving the prior LLM cost);
|
|
1924
2359
|
* - feed `mergeNodeWithEnrichments` with `enrichedAt`-sorted partials
|
|
1925
2360
|
* for last-write-wins per field at read time.
|
|
1926
2361
|
*
|
|
@@ -1930,10 +2365,12 @@ interface IExtractorRunRecord {
|
|
|
1930
2365
|
* fold into a single row, but two different Extractors hitting the
|
|
1931
2366
|
* same node yield two distinct rows.
|
|
1932
2367
|
*
|
|
1933
|
-
* `isProbabilistic` is
|
|
1934
|
-
*
|
|
1935
|
-
*
|
|
1936
|
-
*
|
|
2368
|
+
* `isProbabilistic` is reserved: Extractors are deterministic-only, so
|
|
2369
|
+
* every record produced by the orchestrator sets it to `false`. The
|
|
2370
|
+
* field is kept on the record (and the row in `node_enrichments`) so a
|
|
2371
|
+
* future Action-issued enrichment can populate it without reshaping
|
|
2372
|
+
* the persistence contract — see spec `architecture.md`
|
|
2373
|
+
* §Extractor · enrichment layer.
|
|
1937
2374
|
*/
|
|
1938
2375
|
interface IEnrichmentRecord {
|
|
1939
2376
|
nodePath: string;
|
|
@@ -1961,6 +2398,8 @@ declare function runScanWithRenames(_kernel: Kernel, options: RunScanOptions): P
|
|
|
1961
2398
|
renameOps: RenameOp[];
|
|
1962
2399
|
extractorRuns: IExtractorRunRecord[];
|
|
1963
2400
|
enrichments: IEnrichmentRecord[];
|
|
2401
|
+
contributions: IContributionRecord[];
|
|
2402
|
+
freshlyRunTuples: ReadonlySet<string>;
|
|
1964
2403
|
}>;
|
|
1965
2404
|
declare function runScan(_kernel: Kernel, options: RunScanOptions): Promise<ScanResult>;
|
|
1966
2405
|
/**
|
|
@@ -1997,6 +2436,7 @@ declare function runExtractorsForNode(opts: {
|
|
|
1997
2436
|
internalLinks: Link[];
|
|
1998
2437
|
externalLinks: Link[];
|
|
1999
2438
|
enrichments: IEnrichmentRecord[];
|
|
2439
|
+
contributions: IContributionRecord[];
|
|
2000
2440
|
}>;
|
|
2001
2441
|
/**
|
|
2002
2442
|
* Pure rename / orphan classification per `spec/db-schema.md` §Rename
|
|
@@ -2037,10 +2477,11 @@ declare function detectRenamesAndOrphans(prior: ScanResult, current: Node[], iss
|
|
|
2037
2477
|
* Algorithm:
|
|
2038
2478
|
*
|
|
2039
2479
|
* 1. Filter `enrichments` down to rows targeting this node AND not
|
|
2040
|
-
* flagged `stale`.
|
|
2041
|
-
*
|
|
2042
|
-
*
|
|
2043
|
-
*
|
|
2480
|
+
* flagged `stale`. With Extractors deterministic-only no row is
|
|
2481
|
+
* stale-flagged in this revision; the filter is preserved for the
|
|
2482
|
+
* future Action-issued enrichment revision (queued LLM jobs whose
|
|
2483
|
+
* output must survive body changes), where stale visibility
|
|
2484
|
+
* belongs to the UI layer next to the value.
|
|
2044
2485
|
* 2. Sort the survivors by `enrichedAt` ASC so iteration order is
|
|
2045
2486
|
* "oldest first". This makes the spread merge below
|
|
2046
2487
|
* last-write-wins per field — the freshest Extractor's value
|
|
@@ -2208,7 +2649,7 @@ declare function createChokidarWatcher(opts: ICreateFsWatcherOptions): IFsWatche
|
|
|
2208
2649
|
* are presentation facets that can churn without making the link
|
|
2209
2650
|
* "different" for delta purposes.
|
|
2210
2651
|
*
|
|
2211
|
-
* - **Issue**: `(
|
|
2652
|
+
* - **Issue**: `(analyzerId, sorted nodeIds, message)`. Mirrors
|
|
2212
2653
|
* `spec/job-events.md` §issue.* — same key → same issue, even when
|
|
2213
2654
|
* `data` / `severity` / `linkIndices` shift. A meaningful change in
|
|
2214
2655
|
* `message` (or a different set of node ids) is a different issue.
|
|
@@ -2334,6 +2775,72 @@ declare function applyExportQuery(scan: {
|
|
|
2334
2775
|
issues: Issue[];
|
|
2335
2776
|
}, query: IExportQuery): IExportSubset;
|
|
2336
2777
|
|
|
2778
|
+
/**
|
|
2779
|
+
* `scan_node_tags` adapter — tags · dual-source persistence layer.
|
|
2780
|
+
*
|
|
2781
|
+
* One row per `(node_path, tag, source)` triple. Projected at persist
|
|
2782
|
+
* time from BOTH `frontmatter.tags` (with `source='author'`) and
|
|
2783
|
+
* `sidecar.annotations.tags` (with `source='user'`). The same tag
|
|
2784
|
+
* string MAY appear under both sources for the same node — the PK
|
|
2785
|
+
* accepts the pair; search returns the node once via DISTINCT, the
|
|
2786
|
+
* UI renders both chips with their attribution.
|
|
2787
|
+
*
|
|
2788
|
+
* Belongs to the `scan_*` family — replaced wholesale per scan.
|
|
2789
|
+
* Cached nodes' tag rows are projected from the cached
|
|
2790
|
+
* `node.frontmatter.tags` / `node.sidecar.annotations.tags` (both
|
|
2791
|
+
* already in memory at persist time), so the rebuild is cheap
|
|
2792
|
+
* regardless of cache hit / miss. See `spec/db-schema.md`
|
|
2793
|
+
* § scan_node_tags for the normative shape and replace-all semantics.
|
|
2794
|
+
*/
|
|
2795
|
+
|
|
2796
|
+
/**
|
|
2797
|
+
* In-memory tag record buffered during scan and flushed to
|
|
2798
|
+
* `scan_node_tags` by `persistScanResult`. One entry per
|
|
2799
|
+
* `(node_path, tag, source)` projected from a node's frontmatter tags
|
|
2800
|
+
* (`source: 'author'`) or sidecar annotations tags
|
|
2801
|
+
* (`source: 'user'`).
|
|
2802
|
+
*/
|
|
2803
|
+
interface ITagRecord {
|
|
2804
|
+
nodePath: string;
|
|
2805
|
+
tag: string;
|
|
2806
|
+
source: 'author' | 'user';
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
/**
|
|
2810
|
+
* Pure helpers for the "update available" notification feature.
|
|
2811
|
+
*
|
|
2812
|
+
* Three responsibilities:
|
|
2813
|
+
* - `fetchLatestVersion` — query `https://registry.npmjs.org/<pkg>/latest`
|
|
2814
|
+
* with `AbortController` + timeout. Throws on
|
|
2815
|
+
* non-200 / parse failure / abort.
|
|
2816
|
+
* - `compareVersions` — semver compare (-1 / 0 / 1). Pre-1.0 aware:
|
|
2817
|
+
* treats prereleases via the standard rules
|
|
2818
|
+
* (release > prerelease at the same triple).
|
|
2819
|
+
* - `isOutdated` — sugar over `compareVersions` for the common
|
|
2820
|
+
* "is `latest` strictly greater than `current`"
|
|
2821
|
+
* check the banner runs against.
|
|
2822
|
+
*
|
|
2823
|
+
* Pure kernel module — NO `process.env` reads, NO Node globals beyond the
|
|
2824
|
+
* built-in `fetch` / `AbortController` (Node 22+). Every env / settings
|
|
2825
|
+
* lookup happens in `src/cli/util/update-check-banner.ts`, the CLI-side
|
|
2826
|
+
* adapter that owns side effects.
|
|
2827
|
+
*
|
|
2828
|
+
* The shared cache type (`IUpdateCheckCache`) is used by the storage
|
|
2829
|
+
* helpers under `kernel/storage/update-check.ts` and by the BFF's
|
|
2830
|
+
* `GET /api/update-status` projection. A second type
|
|
2831
|
+
* (`IUpdateStatus`) shapes the BFF response — it merges `current`
|
|
2832
|
+
* (from `VERSION`) into the cache so the UI can render without a
|
|
2833
|
+
* second lookup. Both stay flat — no nested objects — so JSON
|
|
2834
|
+
* serialization is trivial.
|
|
2835
|
+
*/
|
|
2836
|
+
interface IUpdateCheckCache {
|
|
2837
|
+
latestVersion: string;
|
|
2838
|
+
/** Epoch ms — when the registry was last successfully probed. */
|
|
2839
|
+
checkedAt: number;
|
|
2840
|
+
/** Epoch ms — when the banner was last printed; null = never shown yet. */
|
|
2841
|
+
shownAt: number | null;
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2337
2844
|
/**
|
|
2338
2845
|
* `PluginLoaderPort` — discovers plugin directories and loads their
|
|
2339
2846
|
* extensions. The shape mirrors what the concrete loader actually
|
|
@@ -2427,6 +2934,44 @@ interface IPersistOptions {
|
|
|
2427
2934
|
renameOps?: RenameOp[];
|
|
2428
2935
|
extractorRuns?: IExtractorRunRecord[];
|
|
2429
2936
|
enrichments?: IEnrichmentRecord[];
|
|
2937
|
+
contributions?: IContributionRecord[];
|
|
2938
|
+
/**
|
|
2939
|
+
* Phase 3 / View contribution system — active runtime catalog of
|
|
2940
|
+
* registered view contributions, keyed by qualified id
|
|
2941
|
+
* `<pluginId>/<extensionId>/<contributionId>`. Passed to the
|
|
2942
|
+
* `scan_contributions` upsert so the catalog sweep can drop rows
|
|
2943
|
+
* belonging to plugins / extensions that are no longer in the
|
|
2944
|
+
* catalog (uninstalled plugins, disabled bundles, removed
|
|
2945
|
+
* contributions). Empty / absent set = no catalog sweep (legacy
|
|
2946
|
+
* behaviour, leaves disabled-plugin rows stale per design F24
|
|
2947
|
+
* pre-fix).
|
|
2948
|
+
*/
|
|
2949
|
+
registeredContributionKeys?: ReadonlySet<string>;
|
|
2950
|
+
/**
|
|
2951
|
+
* Phase 3 / View contribution system — set of `(plugin, extension,
|
|
2952
|
+
* node)` tuples where the extension actually RAN against that node
|
|
2953
|
+
* in this scan. Format: `<pluginId>/<extensionId>/<nodePath>` (no
|
|
2954
|
+
* contribution-id segment — the sweep operates at the (plugin,
|
|
2955
|
+
* extension, node) level and inspects the buffer to decide which
|
|
2956
|
+
* contribution-ids survive).
|
|
2957
|
+
*
|
|
2958
|
+
* Membership rules:
|
|
2959
|
+
* - Extractor + cache miss: tuple INCLUDED (extract() ran).
|
|
2960
|
+
* - Extractor + cache hit: tuple OMITTED (extract() skipped, prior
|
|
2961
|
+
* rows must be preserved).
|
|
2962
|
+
* - Rule, every node in `ctx.nodes`: tuple INCLUDED (rules always
|
|
2963
|
+
* run and see the full graph).
|
|
2964
|
+
*
|
|
2965
|
+
* Drives the per-tuple sweep documented in `spec/architecture.md`
|
|
2966
|
+
* §View contribution system → Persistence (sweep #3): rows whose
|
|
2967
|
+
* `(plugin_id, extension_id, node_path)` is in this set but whose
|
|
2968
|
+
* `(plugin_id, extension_id, node_path, contribution_id)` is NOT in
|
|
2969
|
+
* the buffer get DELETEd before the upsert. Catches the "extractor
|
|
2970
|
+
* used to emit, now does not" case (e.g. body change removes the
|
|
2971
|
+
* trigger). Empty / absent set = no per-tuple sweep (legacy
|
|
2972
|
+
* callers preserve the pre-fix behaviour where stale rows linger).
|
|
2973
|
+
*/
|
|
2974
|
+
freshlyRunTuples?: ReadonlySet<string>;
|
|
2430
2975
|
}
|
|
2431
2976
|
/**
|
|
2432
2977
|
* Issue row as the storage layer sees it — paired with its DB-assigned
|
|
@@ -2567,28 +3112,6 @@ interface IPluginApplyResult {
|
|
|
2567
3112
|
intrusions: string[];
|
|
2568
3113
|
}
|
|
2569
3114
|
|
|
2570
|
-
/**
|
|
2571
|
-
* `StoragePort` — the kernel's persistence boundary. Driving adapters
|
|
2572
|
-
* (CLI, future server, in-memory test harness) consume this surface
|
|
2573
|
-
* exclusively; nothing in `cli/**` should reach into the SQLite
|
|
2574
|
-
* adapter's internal helpers (free functions on
|
|
2575
|
-
* `kernel/adapters/sqlite/*`) directly. Phase F of the
|
|
2576
|
-
* storage-port-promotion refactor finishes that hardening; A-E grow
|
|
2577
|
-
* the port enough that the CLI has somewhere to land.
|
|
2578
|
-
*
|
|
2579
|
-
* The port is namespaced by domain (`scans`, `issues`, `enrichments`,
|
|
2580
|
-
* etc.) — explicitly NOT a generic `port.query<T>(sql)`. Each
|
|
2581
|
-
* namespace's methods name an operation the kernel cares about; the
|
|
2582
|
-
* adapter translates to its persistence engine's idioms.
|
|
2583
|
-
*
|
|
2584
|
-
* Phase A lands the **scans / issues / enrichments / transaction**
|
|
2585
|
-
* namespaces — the core scan pipeline. The remaining namespaces
|
|
2586
|
-
* (history / jobs / pluginConfig / migrations / pluginMigrations)
|
|
2587
|
-
* arrive in subsequent phases. The port shape declared here is the
|
|
2588
|
-
* Phase A subset; later phases extend it without reshaping what
|
|
2589
|
-
* lands today.
|
|
2590
|
-
*/
|
|
2591
|
-
|
|
2592
3115
|
/**
|
|
2593
3116
|
* Subset of `StoragePort` exposed inside a `transaction(fn)` callback.
|
|
2594
3117
|
* Lifecycle methods are intentionally omitted — a transaction that
|
|
@@ -2665,12 +3188,56 @@ interface StoragePort {
|
|
|
2665
3188
|
*/
|
|
2666
3189
|
findNode(path: string): Promise<INodeBundle | null>;
|
|
2667
3190
|
};
|
|
3191
|
+
/**
|
|
3192
|
+
* Phase 3 / View contribution system — read access to
|
|
3193
|
+
* `scan_contributions`. Writes happen exclusively via
|
|
3194
|
+
* `scans.persist({ contributions })` to keep the replace-all
|
|
3195
|
+
* semantics intact; this namespace is read-only.
|
|
3196
|
+
*/
|
|
3197
|
+
contributions: {
|
|
3198
|
+
/** Every contribution row for a single node. Stable order. */
|
|
3199
|
+
listForNode(nodePath: string): Promise<IPersistedContribution[]>;
|
|
3200
|
+
/**
|
|
3201
|
+
* Bulk variant for the BFF nodes-list route. Returns rows for
|
|
3202
|
+
* every path in `paths`, sorted `nodePath` ASC, then qualified-id
|
|
3203
|
+
* ASC. Empty `paths` returns `[]` without a query.
|
|
3204
|
+
*/
|
|
3205
|
+
listForPaths(paths: readonly string[]): Promise<IPersistedContribution[]>;
|
|
3206
|
+
/**
|
|
3207
|
+
* Lookup by qualified id + path. Used by
|
|
3208
|
+
* `GET /api/contributions/:pluginId/:contributionId?path=...`.
|
|
3209
|
+
*/
|
|
3210
|
+
lookup(pluginId: string, contributionId: string, nodePath: string, extensionId?: string): Promise<IPersistedContribution[]>;
|
|
3211
|
+
};
|
|
3212
|
+
/**
|
|
3213
|
+
* Read-only access to `scan_node_tags`. Writes happen exclusively
|
|
3214
|
+
* via `scans.persist({...})` (the persistence layer projects from
|
|
3215
|
+
* `node.frontmatter.tags` and `node.sidecar.annotations.tags`); this
|
|
3216
|
+
* namespace is read-only.
|
|
3217
|
+
*/
|
|
3218
|
+
tags: {
|
|
3219
|
+
/** Every tag row for a single node. Author entries first, then user. */
|
|
3220
|
+
listForNode(nodePath: string): Promise<ITagRecord[]>;
|
|
3221
|
+
/**
|
|
3222
|
+
* Bulk variant for the BFF nodes-list route. Returns rows for every
|
|
3223
|
+
* path in `paths`, sorted `nodePath` ASC, then `source` ASC, then
|
|
3224
|
+
* `tag` ASC. Empty `paths` returns `[]` without a query.
|
|
3225
|
+
*/
|
|
3226
|
+
listForPaths(paths: readonly string[]): Promise<ITagRecord[]>;
|
|
3227
|
+
/**
|
|
3228
|
+
* Find every node carrying `tag`. Optional `source` narrows to one
|
|
3229
|
+
* side of the dual surface (matches `sm list --tag <name>
|
|
3230
|
+
* --tag-source author|user`); absent matches the union (default
|
|
3231
|
+
* `sm list --tag`).
|
|
3232
|
+
*/
|
|
3233
|
+
findNodes(tag: string, source?: 'author' | 'user'): Promise<string[]>;
|
|
3234
|
+
};
|
|
2668
3235
|
issues: {
|
|
2669
3236
|
/** Every issue from the latest scan, in insertion order. */
|
|
2670
3237
|
listAll(): Promise<Issue[]>;
|
|
2671
3238
|
/**
|
|
2672
3239
|
* Issue rows whose runtime `Issue` shape passes `predicate`.
|
|
2673
|
-
* `port.issues.findActive((i) => i.
|
|
3240
|
+
* `port.issues.findActive((i) => i.analyzerId === 'orphan')` is the
|
|
2674
3241
|
* canonical use; `sm orphans` consumes this. The returned shape
|
|
2675
3242
|
* carries the DB-assigned `id` so a follow-up
|
|
2676
3243
|
* `transaction(tx => tx.issues.deleteById(row.id))` can target
|
|
@@ -2721,6 +3288,36 @@ interface StoragePort {
|
|
|
2721
3288
|
*/
|
|
2722
3289
|
listReferencedFilePaths(): Promise<Set<string>>;
|
|
2723
3290
|
};
|
|
3291
|
+
/**
|
|
3292
|
+
* Generic key/value preferences keyed by a stable string. Backs the
|
|
3293
|
+
* `config_preferences` table — one row per `key`, `value_json` is a
|
|
3294
|
+
* single JSON blob the caller serialises. Keys with the `_kernel.`
|
|
3295
|
+
* prefix are reserved for kernel-managed entries (today: the
|
|
3296
|
+
* update-check cache); user-set preferences land under unprefixed
|
|
3297
|
+
* keys when those ship.
|
|
3298
|
+
*
|
|
3299
|
+
* Read-only by design at the port level — the only writer is the
|
|
3300
|
+
* CLI's post-run hook (`cli/util/update-check-banner.ts`), which
|
|
3301
|
+
* reaches the persistence helpers directly. The port surfaces the
|
|
3302
|
+
* read so the BFF's `GET /api/update-status` projection can stay
|
|
3303
|
+
* inside the abstract contract.
|
|
3304
|
+
*/
|
|
3305
|
+
preferences: {
|
|
3306
|
+
/**
|
|
3307
|
+
* Load the update-check cache row. Returns `null` when the row
|
|
3308
|
+
* is absent, malformed JSON, or fails the shape guard. Never
|
|
3309
|
+
* throws — read failures degrade silently because the banner is
|
|
3310
|
+
* a non-essential surface.
|
|
3311
|
+
*/
|
|
3312
|
+
loadUpdateCheckCache(): Promise<IUpdateCheckCache | null>;
|
|
3313
|
+
/**
|
|
3314
|
+
* Upsert the update-check cache row. Always overwrites the
|
|
3315
|
+
* existing JSON blob in place. `updated_at` tracks wall-clock
|
|
3316
|
+
* now — separate from the embedded `checkedAt` field, which
|
|
3317
|
+
* the caller controls.
|
|
3318
|
+
*/
|
|
3319
|
+
saveUpdateCheckCache(cache: IUpdateCheckCache): Promise<void>;
|
|
3320
|
+
};
|
|
2724
3321
|
favorites: {
|
|
2725
3322
|
/**
|
|
2726
3323
|
* Mark `path` as favorited. Idempotent — a second call refreshes
|
|
@@ -2940,6 +3537,51 @@ declare function resetLogger(): void;
|
|
|
2940
3537
|
/** Inspect the active logger. Test-only — production code uses `log`. */
|
|
2941
3538
|
declare function getActiveLogger(): LoggerPort;
|
|
2942
3539
|
|
|
3540
|
+
/**
|
|
3541
|
+
* Hook lifecycle dispatcher (spec § A.11). Indexes the supplied hooks
|
|
3542
|
+
* by trigger and fans the matching event out to every subscribed
|
|
3543
|
+
* deterministic hook in registration order. Probabilistic hooks are
|
|
3544
|
+
* skipped here with a stderr advisory; they will dispatch via the job
|
|
3545
|
+
* subsystem once it ships (Decision #114).
|
|
3546
|
+
*
|
|
3547
|
+
* Filter handling: when the hook declares a `filter` map, the dispatcher
|
|
3548
|
+
* walks `event.data` for each declared key and short-circuits the
|
|
3549
|
+
* invocation when any value disagrees. Top-level fields only in v0.x
|
|
3550
|
+
* (deep-path matching is deferred until a real use case justifies the
|
|
3551
|
+
* complexity).
|
|
3552
|
+
*
|
|
3553
|
+
* Error policy: a hook that throws is caught here, logged through a
|
|
3554
|
+
* synthetic `extension.error` event with kind `hook-error`, and the
|
|
3555
|
+
* caller continues. A buggy hook MUST NOT block the main pipeline (or
|
|
3556
|
+
* the CLI exit path) — that would invert the design intent (hooks
|
|
3557
|
+
* REACT to events, they never steer them).
|
|
3558
|
+
*
|
|
3559
|
+
* The module lives under `kernel/extensions/` (alongside the `IHook`
|
|
3560
|
+
* contract itself) so two callers share it: `runScan` for the eight
|
|
3561
|
+
* pipeline-driven triggers (`scan.*`, `extractor.completed`,
|
|
3562
|
+
* `analyzer.completed`, `action.completed`, `job.*`) and the CLI entry
|
|
3563
|
+
* for the two CLI-process-driven triggers (`boot`, `shutdown`).
|
|
3564
|
+
* Pulling the dispatcher out of the orchestrator keeps both consumers
|
|
3565
|
+
* symmetric — same indexing, same filter semantics, same error
|
|
3566
|
+
* policy.
|
|
3567
|
+
*/
|
|
3568
|
+
|
|
3569
|
+
interface IHookDispatcher {
|
|
3570
|
+
/**
|
|
3571
|
+
* Fan the event out to every hook subscribed to `trigger`. Awaits each
|
|
3572
|
+
* hook's `on(ctx)` in registration order. Errors are caught and
|
|
3573
|
+
* logged via `extension.error`; they never propagate.
|
|
3574
|
+
*/
|
|
3575
|
+
dispatch(trigger: THookTrigger, event: ProgressEvent): Promise<void>;
|
|
3576
|
+
}
|
|
3577
|
+
/**
|
|
3578
|
+
* Build a dispatcher over the given hooks. Empty `hooks` returns a
|
|
3579
|
+
* cheap no-op shape so the call sites can dispatch unconditionally.
|
|
3580
|
+
*/
|
|
3581
|
+
declare function makeHookDispatcher(hooks: IHook[], emitter: ProgressEmitterPort): IHookDispatcher;
|
|
3582
|
+
/** Construct a `ProgressEvent` envelope. Mirrors the orchestrator helper. */
|
|
3583
|
+
declare function makeEvent(type: string, data: unknown): ProgressEvent;
|
|
3584
|
+
|
|
2943
3585
|
/**
|
|
2944
3586
|
* Kernel entry point. `createKernel()` returns a shell with an empty registry
|
|
2945
3587
|
* and no bound ports. Driving adapters (CLI, Server, Skill) are expected to
|
|
@@ -2961,7 +3603,22 @@ interface Kernel {
|
|
|
2961
3603
|
* MUST treat the resulting array as immutable.
|
|
2962
3604
|
*/
|
|
2963
3605
|
setRegisteredAnnotationKeys: (entries: readonly IRegisteredAnnotationKey[]) => void;
|
|
3606
|
+
/**
|
|
3607
|
+
* Step 11.x — read-only catalog of plugin-contributed view
|
|
3608
|
+
* contributions, keyed by `(pluginId, extensionId, contributionId)`.
|
|
3609
|
+
* Populated at plugin-load time; pure read with no side effects.
|
|
3610
|
+
* Mirror of `getRegisteredAnnotationKeys` for the view contribution
|
|
3611
|
+
* surface (see `architecture.md` §View contribution system →
|
|
3612
|
+
* Runtime catalog).
|
|
3613
|
+
*/
|
|
3614
|
+
getRegisteredViewContributions: () => readonly IRegisteredViewContribution[];
|
|
3615
|
+
/**
|
|
3616
|
+
* Internal — replace the frozen view-contribution catalog. Called
|
|
3617
|
+
* once by the plugin runtime composer after every plugin has loaded;
|
|
3618
|
+
* consumers MUST treat the resulting array as immutable.
|
|
3619
|
+
*/
|
|
3620
|
+
setRegisteredViewContributions: (entries: readonly IRegisteredViewContribution[]) => void;
|
|
2964
3621
|
}
|
|
2965
3622
|
declare function createKernel(): Kernel;
|
|
2966
3623
|
|
|
2967
|
-
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
|
|
3624
|
+
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 IAnalyzer, type IAnalyzerContext, 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 IHookDispatcher, 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 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 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 TSlotName, type TWatchEventKind, type TripleSplit, applyExportQuery, computeScanDelta, configureLogger, createChokidarWatcher, createKernel, detectRenamesAndOrphans, getActiveLogger, isEmptyDelta, isLogLevel, log, logLevelRank, makeDedicatedStoreWrapper, makeEvent, makeHookDispatcher, makeKvStoreWrapper, makePluginStore, mergeNodeWithEnrichments, parseExportQuery, parseLogLevel, qualifiedExtensionId, resetLogger, runExtractorsForNode, runScan, runScanWithRenames };
|