@skill-map/cli 0.55.0 → 0.57.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.
@@ -7,7 +7,7 @@ type TSlotName = 'card.title.right' | 'card.subtitle.left' | 'card.footer.left'
7
7
  * Closed enum of input-type names for plugin settings. Mirror of
8
8
  * `spec/schemas/input-types.schema.json#/$defs/InputTypeName`.
9
9
  */
10
- type TInputTypeName = 'string-list' | 'single-string' | 'boolean-flag' | 'integer' | 'enum-pick' | 'enum-multipick' | 'path-glob' | 'regex' | 'secret' | 'key-value-list';
10
+ type TInputTypeName = 'string-list' | 'single-string' | 'boolean-flag' | 'integer' | 'number' | 'enum-pick' | 'enum-multipick' | 'path-glob' | 'regex' | 'secret' | 'key-value-list';
11
11
  /**
12
12
  * Closed severity palette aligned with PrimeNG `<p-tag>` / `<p-message>` severities. Used by counter, tag, alert, and icon slots for color/contrast hints. The UI maps each severity to a theme-aware tint; plugins do not pick raw colors.
13
13
  */
@@ -115,7 +115,7 @@ interface ActionPrompt {
115
115
  /**
116
116
  * Input-type id from the closed catalog. The UI renders the matching control before dispatch (`single-string`, `enum-pick` and `string-list` today; other types degrade to a graceful 'unsupported' notice).
117
117
  */
118
- inputType: ('string-list' | 'single-string' | 'boolean-flag' | 'integer' | 'enum-pick' | 'enum-multipick' | 'path-glob' | 'regex' | 'secret' | 'key-value-list') & string;
118
+ inputType: ('string-list' | 'single-string' | 'boolean-flag' | 'integer' | 'number' | 'enum-pick' | 'enum-multipick' | 'path-glob' | 'regex' | 'secret' | 'key-value-list') & string;
119
119
  /**
120
120
  * Key under which the collected value is placed in the dispatch `input` body.
121
121
  */
@@ -390,6 +390,13 @@ interface ISetting_Integer extends ISettingCommon {
390
390
  max?: number;
391
391
  step?: number;
392
392
  }
393
+ interface ISetting_Number extends ISettingCommon {
394
+ type: 'number';
395
+ default?: number;
396
+ min?: number;
397
+ max?: number;
398
+ step?: number;
399
+ }
393
400
  interface ISetting_EnumOption {
394
401
  value: string;
395
402
  label: string;
@@ -446,7 +453,7 @@ interface ISetting_KeyValueList extends ISettingCommon {
446
453
  *
447
454
  * Mirror of `input-types.schema.json#/$defs/ISettingDeclaration`.
448
455
  */
449
- type TSettingDeclaration = ISetting_StringList | ISetting_SingleString | ISetting_BooleanFlag | ISetting_Integer | ISetting_EnumPick | ISetting_EnumMultipick | ISetting_PathGlob | ISetting_Regex | ISetting_Secret | ISetting_KeyValueList;
456
+ type TSettingDeclaration = ISetting_StringList | ISetting_SingleString | ISetting_BooleanFlag | ISetting_Integer | ISetting_Number | ISetting_EnumPick | ISetting_EnumMultipick | ISetting_PathGlob | ISetting_Regex | ISetting_Secret | ISetting_KeyValueList;
450
457
  /**
451
458
  * Runtime value type for a setting, derived from its declaration. The
452
459
  * kernel exposes settings to extractors as `Record<string, TSettingValue>`
@@ -809,6 +816,29 @@ type LinkKind = 'invokes' | 'references' | 'mentions' | 'supersedes' | 'points';
809
816
  * enum check. Missing confidence defaults to `ConfidenceTier.MEDIUM`.
810
817
  */
811
818
  type Confidence = number;
819
+ /**
820
+ * A single confidence-scoring operation a `score`-phase analyzer
821
+ * contributes via `ctx.adjustConfidence(link, op)`. The orchestrator
822
+ * folds every op on a link into the final `confidence` (see
823
+ * `orchestrator/confidence-score.ts` for the algebra):
824
+ * - `set` hard override (resolved → 1.0, reserved → 0.1)
825
+ * - `delta` additive, may be negative (third-party heuristics)
826
+ * - `ceil` upper cap, lowers only (broken → 0.5)
827
+ * - `floor` lower bound, raises only
828
+ */
829
+ type TConfidenceOp = {
830
+ readonly kind: 'set';
831
+ readonly value: number;
832
+ } | {
833
+ readonly kind: 'delta';
834
+ readonly value: number;
835
+ } | {
836
+ readonly kind: 'ceil';
837
+ readonly value: number;
838
+ } | {
839
+ readonly kind: 'floor';
840
+ readonly value: number;
841
+ };
812
842
  type Severity = 'error' | 'warn' | 'info';
813
843
  type Stability = 'experimental' | 'stable' | 'deprecated';
814
844
  /**
@@ -1125,11 +1155,10 @@ interface Signal {
1125
1155
  * raw extractor emissions (before the resolver runs). When
1126
1156
  * `outcome === 'materialised'`, `winnerIndex` points into `candidates[]`
1127
1157
  * of the candidate the resolver chose; a corresponding `Link` was added
1128
- * to the graph. When `outcome === 'rejected'`, one of `rejectedBy` /
1129
- * `extractorDisabled` / `belowFloor` is set and no Link materialised.
1130
- * Both materialised and rejected Signals remain on
1131
- * `IAnalyzerContext.signals` so the `core/signal-collision` analyzer can
1132
- * surface losers as `warn` issues. Mirrors
1158
+ * to the graph. When `outcome === 'rejected'`, `rejectedBy` is set and
1159
+ * no Link materialised. Both materialised and rejected Signals remain on
1160
+ * `IAnalyzerContext.signals` so the `core/extractor-collision` analyzer
1161
+ * can surface losers as `warn` issues. Mirrors
1133
1162
  * `signal.schema.json#/properties/resolution`.
1134
1163
  */
1135
1164
  resolution?: ISignalResolution;
@@ -1155,24 +1184,6 @@ interface ISignalResolution {
1155
1184
  extractorId: string;
1156
1185
  reason: 'kind-priority' | 'higher-confidence' | 'longer-range' | 'earlier-declaration';
1157
1186
  };
1158
- /**
1159
- * Phase 4+ stub: populated when every candidate of this Signal came from
1160
- * an extractor the operator disabled via
1161
- * `plugins.<id>.extensions.<extId>.enabled`. Today the resolver never
1162
- * sets this; documented so analyzer surfaces can be built when the filter
1163
- * lands.
1164
- */
1165
- extractorDisabled?: {
1166
- extractorId: string;
1167
- };
1168
- /**
1169
- * Phase 4+ stub: populated when every candidate's `confidence` fell below
1170
- * the configured floor. Today the resolver materialises every Signal that
1171
- * survives overlap regardless of confidence.
1172
- */
1173
- belowFloor?: {
1174
- threshold: number;
1175
- };
1176
1187
  }
1177
1188
  interface IssueFix {
1178
1189
  summary?: string;
@@ -1672,6 +1683,55 @@ declare function makePluginStore(opts: {
1672
1683
  persistDedicated?: IDedicatedStorePersist;
1673
1684
  }): TPluginStore | undefined;
1674
1685
 
1686
+ /**
1687
+ * Combination algebra for plugin-contributed link-confidence adjustments.
1688
+ *
1689
+ * Confidence ([0,1]) starts at the kernel's 1.0 baseline (seeded on every
1690
+ * link by `liftResolvedLinkConfidence`). `score`-phase analyzers then
1691
+ * contribute attributed operations via `ctx.adjustConfidence(link, op)`;
1692
+ * the orchestrator buffers them and folds all ops for a link into a final
1693
+ * value with `foldConfidence`. The kernel dogfoods this exact API through
1694
+ * two built-in score-phase detectors, each co-locating its penalty op
1695
+ * with the finding it reports: `core/name-reserved`
1696
+ * (reserved → `delta -0.9` → 0.1), `core/reference-broken`
1697
+ * (broken → `delta -0.5` → 0.5). A clean-resolved or untouched link folds
1698
+ * to `clamp(base)` and keeps the 1.0 baseline.
1699
+ *
1700
+ * The fold is deterministic and order-independent across the four
1701
+ * buckets (set / delta / floor / ceil are each commutative):
1702
+ * 1. base = the extractor-emitted confidence.
1703
+ * 2. `set`: a hard override. When more than one `set` lands on a link
1704
+ * the LAST in the caller's canonical order wins (the caller pre-
1705
+ * sorts ops by `(pluginId, extensionId)` so the winner is stable);
1706
+ * a single `set` simply replaces the base.
1707
+ * 3. `delta`: additive (may be negative), summed.
1708
+ * 4. `floor`: raise to at least the value (`max`).
1709
+ * 5. `ceil`: lower to at most the value (`min`) — today's broken cap.
1710
+ * Applied AFTER floor so a cap dominates a floor/ceil collision.
1711
+ * 6. clamp to [0,1] ONCE at the end, so opposing deltas round-trip
1712
+ * (e.g. `-0.4` then `+0.4` returns to base, never clipped midway).
1713
+ *
1714
+ * A link no scorer touches folds to `clamp(base)` (the kernel's 1.0
1715
+ * baseline), identical to a clean-resolved link. With only the built-in
1716
+ * detectors active a link is at most reserved OR broken (mutually
1717
+ * exclusive), so it gets at most one penalty delta and folds to 0.1 / 0.5
1718
+ * respectively; a clean link keeps the 1.0 base. Third-party scorers layer
1719
+ * additional ops on top, summed deterministically before the single clamp.
1720
+ */
1721
+
1722
+ /**
1723
+ * One attributed adjustment, buffered by the orchestrator as a scorer
1724
+ * calls `adjustConfidence`. `link` is held by object identity (the same
1725
+ * link objects flow through the post-walk pipeline). Persisted to
1726
+ * `scan_link_scores` for the "why is this link at X?" audit trail.
1727
+ */
1728
+ interface IConfidenceAdjustment {
1729
+ readonly link: Link;
1730
+ readonly pluginId: string;
1731
+ readonly extensionId: string;
1732
+ readonly op: TConfidenceOp;
1733
+ }
1734
+
1675
1735
  /**
1676
1736
  * Row-level filter for `port.scans.findNodes(...)` (driven by
1677
1737
  * `sm list`'s flags). All fields are optional, an empty filter
@@ -1720,11 +1780,11 @@ interface INodeCounts {
1720
1780
  issues: number;
1721
1781
  }
1722
1782
  /**
1723
- * Lightweight option bag for `port.scans.persist`. Mirrors the trailing
1724
- * arguments of the legacy `persistScanResult(db, result, renameOps,
1725
- * extractorRuns, enrichments)` free function so the adapter
1726
- * implementation is a one-line delegation today; the named-bag shape
1727
- * tomorrow lets new optional inputs land without breaking callers.
1783
+ * Lightweight option bag for `port.scans.persist`. Mirrors the optional
1784
+ * inputs of the `persistScanResult(db, result, inputs)` free function
1785
+ * (`IPersistScanInputs` in `kernel/adapters/sqlite/scan-persistence.ts`),
1786
+ * so the adapter implementation is a one-line delegation; the named-bag
1787
+ * shape lets new optional inputs land without breaking callers.
1728
1788
  */
1729
1789
  interface IPersistOptions {
1730
1790
  renameOps?: RenameOp[];
@@ -1740,6 +1800,18 @@ interface IPersistOptions {
1740
1800
  * scan clears any stale rows). Surfaced by `sm plugins doctor`.
1741
1801
  */
1742
1802
  contributionErrors?: IContributionErrorRecord[];
1803
+ /**
1804
+ * Per-op confidence-attribution audit trail for `scan_link_scores`.
1805
+ * One entry per attributed `ctx.adjustConfidence(link, op)` call a
1806
+ * `score`-phase analyzer buffered this scan; the orchestrator already
1807
+ * folded them into `link.confidence`, so these rows are the attribution
1808
+ * (which plugin / extension / op moved a given link, plus the folded
1809
+ * `result_confidence`). Plain REPLACE-ALL into `scan_link_scores`
1810
+ * (delete all, then insert), the same posture as `scan_issues`. Empty /
1811
+ * absent wipes the table (a scan whose scorers touched nothing clears
1812
+ * any stale rows).
1813
+ */
1814
+ linkScores?: IConfidenceAdjustment[];
1743
1815
  /**
1744
1816
  * Phase 3 / View contribution system, active runtime catalog of
1745
1817
  * registered view contributions, keyed by qualified id
@@ -2933,6 +3005,12 @@ interface IAnalyzerOrphanSidecar {
2933
3005
  interface IAnalyzerContext {
2934
3006
  nodes: Node[];
2935
3007
  links: Link[];
3008
+ /**
3009
+ * Resolved values of the analyzer's declared `settings`, populated
3010
+ * from project config + user overrides. Empty object when no settings
3011
+ * are declared.
3012
+ */
3013
+ settings: Record<string, unknown>;
2936
3014
  /**
2937
3015
  * Step 9.6.2, orphaned sidecars discovered during the scan walk.
2938
3016
  * Empty when sidecar discovery did not run (legacy callers) or
@@ -2970,18 +3048,6 @@ interface IAnalyzerContext {
2970
3048
  * runScan sites that never wired the catalog through).
2971
3049
  */
2972
3050
  viewContributions?: readonly IRegisteredViewContribution[];
2973
- /**
2974
- * Absolute paths of `*.md` files under the project's
2975
- * `.skill-map/jobs/` that no `state_jobs.filePath` references, the
2976
- * built-in `core/job-file-orphan` analyzer projects each as a `warn`
2977
- * issue. Pre-computed by the driving adapter (CLI / BFF) inside its
2978
- * already-open storage transaction (mirrors the `orphanSidecars`
2979
- * pattern: detection lives outside the analyzer, the analyzer only
2980
- * projects). Absent (or empty) when the caller does not maintain a
2981
- * jobs directory, when the storage path is unavailable, or when no
2982
- * orphan files exist. Treat as read-only.
2983
- */
2984
- orphanJobFiles?: readonly string[];
2985
3051
  /**
2986
3052
  * Issues emitted by analyzers that already ran in the current pass.
2987
3053
  * Lets a late-phase analyzer (`core/issue-counter`) compute
@@ -3024,6 +3090,37 @@ interface IAnalyzerContext {
3024
3090
  * `runScan` sites that never wired the field through).
3025
3091
  */
3026
3092
  reservedNodePaths?: ReadonlySet<string>;
3093
+ /**
3094
+ * Links the post-walk lift judged genuinely broken: target matches no
3095
+ * node `path` AND the stripped trigger matches no entry in the cross-
3096
+ * kind name index (`spec/architecture.md` §Provider · resolution
3097
+ * rules). Computed once per scan by the orchestrator from the same
3098
+ * `deriveNodeIdentifiers`-backed index the confidence-lift transform
3099
+ * uses, so a link that resolves only via a filename / dirname
3100
+ * identifier is NOT in the set. Membership is by object identity (the
3101
+ * orchestrator threads the SAME link objects). The single consumer is
3102
+ * `core/reference-broken`, which projects one issue per member (after
3103
+ * its `referenceablePaths` escape hatch). Absent for legacy callers
3104
+ * that never wired the field through, the rule then emits nothing.
3105
+ */
3106
+ brokenLinks?: ReadonlySet<Link>;
3107
+ /**
3108
+ * Names claimed by two or more distinct nodes, keyed by the normalised
3109
+ * name. A node contributes only when its kind declares `frontmatter.name`
3110
+ * as a resolution identifier (so plain `core/markdown` nodes, addressed
3111
+ * by path, never collide) and it carries a non-empty `name`. Names that
3112
+ * normalise to the same value (e.g. `Deploy` / `deploy`) collide, mirroring
3113
+ * how the resolver keys on the normalised identifier. Computed once per
3114
+ * scan by the orchestrator from the same kind registry the resolver uses,
3115
+ * so analyzers project it without re-deriving (the `brokenLinks` /
3116
+ * `reservedNodePaths` precompute-and-project pattern). The single consumer
3117
+ * is `core/name-collision`, which emits one `error` per entry. Absent for
3118
+ * legacy callers that never wired the field through.
3119
+ */
3120
+ nameCollisions?: ReadonlyMap<string, readonly {
3121
+ readonly path: string;
3122
+ readonly kind: string;
3123
+ }[]>;
3027
3124
  /**
3028
3125
  * Absolute path of the scan's project root (cwd of the invocation).
3029
3126
  * Threaded into the analyzer pass so an analyzer that needs to
@@ -3068,6 +3165,17 @@ interface IAnalyzerContext {
3068
3165
  * emissions (`scan_contributions`).
3069
3166
  */
3070
3167
  emitContribution<C extends IViewContribution>(nodePath: string, ref: C, payload: SlotPayload<C['slot']>): void;
3168
+ /**
3169
+ * Contribute a confidence adjustment to a link. Usable ONLY from a
3170
+ * `score`-phase analyzer; the orchestrator records it attributed to
3171
+ * the calling extension (`pluginId` / `extensionId`, like
3172
+ * `emitContribution`) and folds every op on a link into the final
3173
+ * `link.confidence` before the `detect` phase. `link` must be one of
3174
+ * `ctx.links` (matched by object identity). Present ONLY in the
3175
+ * `score` phase (absent for `detect` / `aggregate` and legacy
3176
+ * callers), mirroring the other orchestrator-injected ctx fields.
3177
+ */
3178
+ adjustConfidence?(link: Link, op: TConfidenceOp): void;
3071
3179
  }
3072
3180
  interface IAnalyzer extends IExtensionBase {
3073
3181
  /** Discriminant injected by the loader from the folder structure. */
@@ -3094,22 +3202,31 @@ interface IAnalyzer extends IExtensionBase {
3094
3202
  * Execution phase. Drives the order the orchestrator schedules
3095
3203
  * analyzers in:
3096
3204
  *
3205
+ * - `'score'`, runs strictly BEFORE every `detect`-phase analyzer.
3206
+ * The ONLY phase permitted to write: it adjusts link confidence
3207
+ * via `ctx.adjustConfidence(link, op)`. The orchestrator folds
3208
+ * every score-phase op into `link.confidence` before the read-
3209
+ * only `detect` phase runs, so the `detect` analyzers see the
3210
+ * final value. The kernel seeds the 1.0 confidence baseline on
3211
+ * every link, then dogfoods this phase via two built-in score-phase
3212
+ * detectors (`core/name-reserved`, `core/reference-broken`), each
3213
+ * co-locating its penalty `delta` with the finding it reports.
3097
3214
  * - `'detect'` (default), the main pass. Walks nodes / links and
3098
- * emits its own findings. Most analyzers live here.
3215
+ * emits its own findings. Most analyzers live here. Read-only.
3099
3216
  * - `'aggregate'`, runs strictly AFTER every `detect`-phase
3100
3217
  * analyzer has finished. The orchestrator passes the full
3101
3218
  * issue accumulator on `ctx.accumulatedIssues`, so an
3102
3219
  * aggregator can compute cross-analyzer summaries (per-node
3103
3220
  * severity totals, etc.) without re-reading the persisted DB.
3104
3221
  * Aggregators emit contributions; emitting issues is allowed
3105
- * but uncommon.
3222
+ * but uncommon. Read-only.
3106
3223
  *
3107
- * Two-phase scheduling is the clean alternative to ordering
3108
- * analyzers by hand in the built-ins registry: filesystem-sorted
3109
- * generators can keep their alphabetical output, the orchestrator
3110
- * applies the phase sort at run-time.
3224
+ * Phase scheduling is the clean alternative to ordering analyzers by
3225
+ * hand in the built-ins registry: filesystem-sorted generators can
3226
+ * keep their alphabetical output, the orchestrator applies the phase
3227
+ * sort (`score` < `detect` < `aggregate`) at run-time.
3111
3228
  */
3112
- phase?: 'detect' | 'aggregate';
3229
+ phase?: 'score' | 'detect' | 'aggregate';
3113
3230
  evaluate(ctx: IAnalyzerContext): Issue[] | Promise<Issue[]>;
3114
3231
  }
3115
3232
 
@@ -3285,6 +3402,12 @@ interface IFormatterContext {
3285
3402
  nodes: Node[];
3286
3403
  links: Link[];
3287
3404
  issues: Issue[];
3405
+ /**
3406
+ * Resolved values of the formatter's declared `settings`, populated
3407
+ * from project config + user overrides. Empty object when no settings
3408
+ * are declared.
3409
+ */
3410
+ settings: Record<string, unknown>;
3288
3411
  /**
3289
3412
  * Full persisted scan, when the caller has it on hand. Optional so
3290
3413
  * formatters that only consume (nodes, links, issues) keep working
@@ -3388,6 +3511,12 @@ declare const HOOK_TRIGGERS: readonly THookTrigger[];
3388
3511
  * Deterministic hooks SHOULD ignore the field.
3389
3512
  */
3390
3513
  interface IHookContext {
3514
+ /**
3515
+ * Resolved values of the hook's declared `settings`, populated from
3516
+ * project config + user overrides. Empty object when no settings are
3517
+ * declared.
3518
+ */
3519
+ settings: Record<string, unknown>;
3391
3520
  /** The raw event the dispatcher matched. */
3392
3521
  event: {
3393
3522
  type: THookTrigger;
@@ -3882,21 +4011,6 @@ interface RunScanOptions {
3882
4011
  * mock without spinning up a DB.
3883
4012
  */
3884
4013
  pluginStores?: ReadonlyMap<string, TPluginStore>;
3885
- /**
3886
- * Pre-computed absolute paths of orphan job MD files (files under
3887
- * `.skill-map/jobs/` whose absolute path appears nowhere in
3888
- * `state_jobs.filePath`). Threaded into the rule pass so the
3889
- * built-in `core/job-file-orphan` rule can project each as a `warn`
3890
- * issue without the kernel reaching for the storage port or doing
3891
- * its own FS walk. The driving adapter (CLI, BFF) computes this
3892
- * inside its already-open storage transaction via
3893
- * `findOrphanJobFiles(jobsDir, await port.jobs.listReferencedFilePaths())`
3894
- * mirrors the `orphanSidecars` model where detection lives
3895
- * outside the rule and the rule only projects. Absent / empty when
3896
- * the caller has no jobs context (out-of-band tests, fresh DB,
3897
- * `--no-built-ins`).
3898
- */
3899
- orphanJobFiles?: readonly string[];
3900
4014
  /**
3901
4015
  * Side set of absolute file paths the operator opted into for
3902
4016
  * link-validation purposes via `scan.referencePaths`. Threaded
@@ -3980,6 +4094,7 @@ declare function runScanWithRenames(_kernel: Kernel, options: RunScanOptions): P
3980
4094
  enrichments: IEnrichmentRecord[];
3981
4095
  contributions: IContributionRecord[];
3982
4096
  contributionErrors: IContributionErrorRecord[];
4097
+ linkScores: IConfidenceAdjustment[];
3983
4098
  freshlyRunTuples: ReadonlySet<string>;
3984
4099
  }>;
3985
4100
  declare function runScan(_kernel: Kernel, options: RunScanOptions): Promise<ScanResult>;
@@ -4178,7 +4293,7 @@ declare function createChokidarWatcher(opts: ICreateFsWatcherOptions): IFsWatche
4178
4293
  * a reason narrowing what diverged.
4179
4294
  *
4180
4295
  * - **Link**: `(source, target, kind, normalizedTrigger ?? '')`. This
4181
- * mirrors the link-conflict rule and `sm show` aggregation,
4296
+ * mirrors the link-kind-conflict rule and `sm show` aggregation,
4182
4297
  * two links with identical endpoints, kind, and (optional) trigger
4183
4298
  * are the same link, even if emitted by different extractors. The
4184
4299
  * `sources[]` union and confidence are NOT part of identity; they