@saasontools/strauss-kb 0.1.10 → 0.1.12
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/ARCHITECTURE.md +17 -0
- package/README.md +96 -27
- package/dist/{chunk-EJQPZWN5.js → chunk-33ZCBEUV.js} +1183 -375
- package/dist/chunk-33ZCBEUV.js.map +1 -0
- package/dist/{chunk-NMTP7V7E.js → chunk-EXKK2KUN.js} +2 -2
- package/dist/{chunk-RGK3K6LN.js → chunk-F2U2YLWV.js} +2 -2
- package/dist/cli-main.cjs +1195 -395
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +2 -2
- package/dist/index.cjs +991 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +204 -10
- package/dist/index.d.ts +204 -10
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-main.cjs +1190 -390
- package/dist/mcp-main.cjs.map +1 -1
- package/dist/mcp-main.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-EJQPZWN5.js.map +0 -1
- /package/dist/{chunk-NMTP7V7E.js.map → chunk-EXKK2KUN.js.map} +0 -0
- /package/dist/{chunk-RGK3K6LN.js.map → chunk-F2U2YLWV.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -47,13 +47,29 @@ declare const kbVerifiedEventSchema: z.ZodObject<{
|
|
|
47
47
|
*
|
|
48
48
|
* Symbolic on purpose. These are written while the code is still moving: a
|
|
49
49
|
* `line: 379` recorded at minute five is wrong by minute forty, but
|
|
50
|
-
* `OrderService.cancel` survives every edit that does not rename it.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* `OrderService.cancel` survives every edit that does not rename it. Once the
|
|
51
|
+
* change settles, a resolution pass (`anchor-resolver.ts`) stamps `hash`,
|
|
52
|
+
* `resolved_at`, and `lines`; drift detection later re-resolves and compares.
|
|
53
|
+
*
|
|
54
|
+
* `hash` is prefixed with the algorithm so a future one can coexist with
|
|
55
|
+
* stored values. `lines` exists because the anchor keeps a hash, not the text:
|
|
56
|
+
* without the line count at hash time, a drift report could say "changed" but
|
|
57
|
+
* never how much.
|
|
58
|
+
*
|
|
59
|
+
* `repo` and `ref` say *which* code, for bases that describe more than one
|
|
60
|
+
* repository. They are author-owned identity: a resolver stamps `hash`,
|
|
61
|
+
* `lines`, and `resolved_at`, and never writes these two. Both are optional and
|
|
62
|
+
* independent of each other, so every anchor written before they existed stays
|
|
63
|
+
* valid.
|
|
53
64
|
*/
|
|
54
65
|
declare const kbAnchorSchema: z.ZodObject<{
|
|
55
66
|
file: z.ZodString;
|
|
56
67
|
symbol: z.ZodOptional<z.ZodString>;
|
|
68
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
69
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
70
|
+
hash: z.ZodOptional<z.ZodString>;
|
|
71
|
+
resolved_at: z.ZodOptional<z.ZodString>;
|
|
72
|
+
lines: z.ZodOptional<z.ZodNumber>;
|
|
57
73
|
}, z.core.$strict>;
|
|
58
74
|
declare const KB_RECORD_TYPES: readonly ["fact", "requirement", "constraint", "decision", "assumption", "open-question", "risk", "contract", "flow", "affected-system", "test-obligation", "source-note"];
|
|
59
75
|
type KbRecordType = (typeof KB_RECORD_TYPES)[number];
|
|
@@ -103,6 +119,11 @@ declare const kbRecordFrontmatterSchema: z.ZodObject<{
|
|
|
103
119
|
strauss_anchors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
104
120
|
file: z.ZodString;
|
|
105
121
|
symbol: z.ZodOptional<z.ZodString>;
|
|
122
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
123
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
124
|
+
hash: z.ZodOptional<z.ZodString>;
|
|
125
|
+
resolved_at: z.ZodOptional<z.ZodString>;
|
|
126
|
+
lines: z.ZodOptional<z.ZodNumber>;
|
|
106
127
|
}, z.core.$strict>>>;
|
|
107
128
|
strauss_verify: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
108
129
|
strauss_status: z.ZodDefault<z.ZodEnum<{
|
|
@@ -145,6 +166,104 @@ type KbRecord = {
|
|
|
145
166
|
body: string;
|
|
146
167
|
};
|
|
147
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Resolves symbolic anchors to text and detects drift against stored hashes.
|
|
171
|
+
*
|
|
172
|
+
* Resolvers are pure — source string in, range out; only the file readers touch
|
|
173
|
+
* disk. Any shape the lexer cannot handle confidently returns `null` and is
|
|
174
|
+
* reported `unresolved` rather than guessed at.
|
|
175
|
+
*/
|
|
176
|
+
type ResolvedSymbol = {
|
|
177
|
+
text: string;
|
|
178
|
+
/** 1-based, inclusive. */
|
|
179
|
+
startLine: number;
|
|
180
|
+
endLine: number;
|
|
181
|
+
};
|
|
182
|
+
interface AnchorResolver {
|
|
183
|
+
name: string;
|
|
184
|
+
resolve(source: string, symbol: string): ResolvedSymbol | null;
|
|
185
|
+
}
|
|
186
|
+
/** Why an anchor could not be compared. Never an error — always a finding. */
|
|
187
|
+
type AnchorUnresolvedReason = "file-missing" | "symbol-not-found" | "outside-repo" | "file-too-large" | "file-unreadable"
|
|
188
|
+
/**
|
|
189
|
+
* The anchor names a repository this root is not. Expected rather than
|
|
190
|
+
* wrong — a base describing several repositories resolves against one tree
|
|
191
|
+
* at a time — so it is never a drift finding and never reaches a reader as
|
|
192
|
+
* a warning. Multi-root resolution is SAA-709.
|
|
193
|
+
*/
|
|
194
|
+
| "foreign-repo";
|
|
195
|
+
/**
|
|
196
|
+
* v1 heuristic resolver. A dotted symbol like `OrderService.cancel` matches on
|
|
197
|
+
* its last segment, with the parent used to scope the search: a candidate
|
|
198
|
+
* counts only if the parent name appears in the fifty lines above it, when any
|
|
199
|
+
* candidate satisfies that at all.
|
|
200
|
+
*
|
|
201
|
+
* Deterministic, and ambiguity is not resolved by guessing — two lines of
|
|
202
|
+
* equally good shape mean the resolver cannot tell which one the record meant,
|
|
203
|
+
* and it says so by returning `null`.
|
|
204
|
+
*/
|
|
205
|
+
declare const regexResolver: AnchorResolver;
|
|
206
|
+
/** CRLF normalized to LF before hashing, so checkout style cannot read as drift. */
|
|
207
|
+
declare function hashAnchorText(text: string): string;
|
|
208
|
+
/**
|
|
209
|
+
* An anchor without a symbol is about the whole file; with one, the resolver
|
|
210
|
+
* decides. Source newlines are normalized first so line counts and hashes
|
|
211
|
+
* agree with `hashAnchorText`.
|
|
212
|
+
*
|
|
213
|
+
* A file's last line is the last line with content: a trailing newline is a
|
|
214
|
+
* terminator, not an empty line, and counting it would have made every
|
|
215
|
+
* whole-file anchor's `lines` one larger than the file.
|
|
216
|
+
*/
|
|
217
|
+
declare function resolveAnchor(source: string, anchor: KbAnchor, resolver?: AnchorResolver): ResolvedSymbol | null;
|
|
218
|
+
type KbAnchorDriftEntry = {
|
|
219
|
+
file: string;
|
|
220
|
+
symbol?: string;
|
|
221
|
+
state: "match" | "drifted" | "unresolved";
|
|
222
|
+
storedHash: string;
|
|
223
|
+
currentHash?: string;
|
|
224
|
+
/** `null` when the anchor recorded no `lines` — size unknown, not zero. */
|
|
225
|
+
diffSize: number | null;
|
|
226
|
+
reason?: AnchorUnresolvedReason;
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* An anchor's `file` must stay inside the repository root — a record points
|
|
230
|
+
* at code, not at arbitrary files on the machine reading it. Bundles are
|
|
231
|
+
* data, so a traversal or absolute path here is untrusted input, not a bug
|
|
232
|
+
* in the caller. Returns the resolved path, or `null` when it escapes.
|
|
233
|
+
*
|
|
234
|
+
* Lexical only, and therefore not the whole containment check: see
|
|
235
|
+
* `readAnchorFile`, which re-tests the real path after following symlinks.
|
|
236
|
+
*/
|
|
237
|
+
declare function anchorFilePath(repoRoot: string, file: string): string | null;
|
|
238
|
+
type AnchorRead = {
|
|
239
|
+
ok: true;
|
|
240
|
+
source: string;
|
|
241
|
+
} | {
|
|
242
|
+
ok: false;
|
|
243
|
+
reason: AnchorUnresolvedReason;
|
|
244
|
+
};
|
|
245
|
+
type AnchorFileReader = (file: string) => Promise<AnchorRead>;
|
|
246
|
+
/**
|
|
247
|
+
* Re-resolves every hash-carrying anchor and compares against the stored hash.
|
|
248
|
+
*
|
|
249
|
+
* Anchors without a `hash` are skipped; one naming another repository is
|
|
250
|
+
* reported `foreign-repo` and never read, and `origin` is asked for once per
|
|
251
|
+
* run, only when some anchor declares a `repo`. A missing file or unresolvable
|
|
252
|
+
* symbol is a finding (`unresolved`), never a throw. Each distinct file is read
|
|
253
|
+
* once per run; all checked entries are returned per record, callers filter.
|
|
254
|
+
*
|
|
255
|
+
* Three phases: collect the checkable anchors, read their distinct files with
|
|
256
|
+
* a bounded pool, then resolve and hash in record order — so the output does
|
|
257
|
+
* not depend on which read finished first.
|
|
258
|
+
*/
|
|
259
|
+
declare function detectAnchorDrift(records: KbRecord[], options?: {
|
|
260
|
+
repoRoot?: string;
|
|
261
|
+
resolver?: AnchorResolver;
|
|
262
|
+
concurrency?: number;
|
|
263
|
+
/** Test seam: replaces the disk reader. */
|
|
264
|
+
reader?: AnchorFileReader;
|
|
265
|
+
}): Promise<Map<string, KbAnchorDriftEntry[]>>;
|
|
266
|
+
|
|
148
267
|
/**
|
|
149
268
|
* Why a matched record must not be read as a plain answer.
|
|
150
269
|
*
|
|
@@ -189,6 +308,18 @@ type KbWarning =
|
|
|
189
308
|
staleAfter: string;
|
|
190
309
|
} | {
|
|
191
310
|
kind: "unverified";
|
|
311
|
+
}
|
|
312
|
+
/** The code this record anchors to has changed since its hash was recorded —
|
|
313
|
+
* the record may describe code that no longer exists in that form. */
|
|
314
|
+
| {
|
|
315
|
+
kind: "drifted";
|
|
316
|
+
anchors: {
|
|
317
|
+
file: string;
|
|
318
|
+
symbol?: string;
|
|
319
|
+
/** `null` when the anchor recorded no line count — size unknown. */
|
|
320
|
+
diffSize: number | null;
|
|
321
|
+
reason?: string;
|
|
322
|
+
}[];
|
|
192
323
|
};
|
|
193
324
|
type KbStanding = "current" | "superseded" | "rejected" | "unsettled" | "open";
|
|
194
325
|
type KbAdjudicated = {
|
|
@@ -205,7 +336,7 @@ type KbAdjudicated = {
|
|
|
205
336
|
* invisible: the caller cannot tell it missed anything, so a dropped record is
|
|
206
337
|
* worse than a flagged one — it turns a knowable gap into an unknowable one.
|
|
207
338
|
*/
|
|
208
|
-
declare function adjudicate(hits: KbRecord[], bundle: KbRecord[], now?: Date): KbAdjudicated[];
|
|
339
|
+
declare function adjudicate(hits: KbRecord[], bundle: KbRecord[], now?: Date, anchorDrift?: Map<string, KbAnchorDriftEntry[]>): KbAdjudicated[];
|
|
209
340
|
/**
|
|
210
341
|
* Walks a supersession chain to whatever currently stands in its place.
|
|
211
342
|
*
|
|
@@ -459,6 +590,12 @@ type KbLoadResult = {
|
|
|
459
590
|
tokensLoaded: number;
|
|
460
591
|
/** `null` when loaded via `all`: no ceiling was applied. */
|
|
461
592
|
budgetTokens: number | null;
|
|
593
|
+
/**
|
|
594
|
+
* Sha256 over every record's content and standing, sorted by concept
|
|
595
|
+
* id. Flips when a body, frontmatter, or standing changes; it's the
|
|
596
|
+
* base's content stamp, compared by hooks and `kb_stamp` (SAA-719).
|
|
597
|
+
*/
|
|
598
|
+
digest: string;
|
|
462
599
|
} | {
|
|
463
600
|
loaded: false;
|
|
464
601
|
recordCount: number;
|
|
@@ -466,6 +603,13 @@ type KbLoadResult = {
|
|
|
466
603
|
budgetTokens: number;
|
|
467
604
|
/** The refusal in words, naming the budget and what to call next. */
|
|
468
605
|
message: string;
|
|
606
|
+
/**
|
|
607
|
+
* Same digest a successful load would carry, computed over what would
|
|
608
|
+
* have been handed back. Cheap here — adjudication already ran before
|
|
609
|
+
* the refusal — and it lets a caller notice a refused bundle's content
|
|
610
|
+
* changed (say, after a narrower `type` filter) without loading it.
|
|
611
|
+
*/
|
|
612
|
+
digest: string;
|
|
469
613
|
};
|
|
470
614
|
type KbWriteInput = {
|
|
471
615
|
type: string;
|
|
@@ -526,6 +670,14 @@ declare class KbStore {
|
|
|
526
670
|
* timeouts.
|
|
527
671
|
*/
|
|
528
672
|
setStatus(bundlePath: string, conceptId: string, status: KbRecordStatus, actor?: string): Promise<KbRecord>;
|
|
673
|
+
/**
|
|
674
|
+
* Replaces a record's anchors wholesale, preserving everything else.
|
|
675
|
+
*
|
|
676
|
+
* Wholesale rather than merged: the caller just resolved the anchors it is
|
|
677
|
+
* writing, so it holds the complete current set, and a merge would keep
|
|
678
|
+
* stale entries the resolution pass deliberately dropped.
|
|
679
|
+
*/
|
|
680
|
+
updateAnchors(bundlePath: string, conceptId: string, anchors: KbAnchor[], actor?: string): Promise<KbRecord>;
|
|
529
681
|
/**
|
|
530
682
|
* Appends one `verified[]` event: who checked the record, when, and what the
|
|
531
683
|
* check found. Append-only — prior events are history, and are spread into
|
|
@@ -563,8 +715,31 @@ declare class KbStore {
|
|
|
563
715
|
query(bundlePath: string, text: string, options?: {
|
|
564
716
|
type?: string;
|
|
565
717
|
includeNonCurrent?: boolean;
|
|
718
|
+
repoRoot?: string;
|
|
566
719
|
}): Promise<KbAdjudicated[]>;
|
|
567
720
|
private rank;
|
|
721
|
+
/**
|
|
722
|
+
* Anchor drift over the records about to be handed back. Like the search
|
|
723
|
+
* index, this is an enrichment: a filesystem failure degrades to "no drift
|
|
724
|
+
* reported" rather than failing the read. Anchors without a stored hash are
|
|
725
|
+
* skipped inside `detectAnchorDrift`, so a base nobody has stamped pays no
|
|
726
|
+
* fs cost here. `repoRoot` defaults to the working directory — the CLI runs
|
|
727
|
+
* at the repo root, and the MCP server's cwd is the workspace.
|
|
728
|
+
*
|
|
729
|
+
* Public because `doctor` needs the same map with the same degradation: a
|
|
730
|
+
* sweep that failed to read the tree should report no drift, not fail.
|
|
731
|
+
*
|
|
732
|
+
* When no root was given and not one anchored file was found, the finding is
|
|
733
|
+
* discarded. A base read from somewhere other than the tree it describes
|
|
734
|
+
* misses every file at once, and that shape is far likelier to be a wrong
|
|
735
|
+
* default root than a repository where every anchored file was deleted on
|
|
736
|
+
* the same day. Reporting it would put a drift warning on every record in
|
|
737
|
+
* the base, which teaches a reader to ignore the warning — the one outcome
|
|
738
|
+
* worse than not having it. One file found anywhere makes the root
|
|
739
|
+
* plausible, and the misses become findings again; an explicit `repoRoot` is
|
|
740
|
+
* taken at its word either way.
|
|
741
|
+
*/
|
|
742
|
+
detectDrift(records: KbRecord[], repoRoot?: string): Promise<Map<string, KbAnchorDriftEntry[]> | undefined>;
|
|
568
743
|
/**
|
|
569
744
|
* The whole base, adjudicated, when it is small enough to hand over.
|
|
570
745
|
*
|
|
@@ -599,6 +774,7 @@ declare class KbStore {
|
|
|
599
774
|
budgetTokens?: number;
|
|
600
775
|
type?: string;
|
|
601
776
|
all?: boolean;
|
|
777
|
+
repoRoot?: string;
|
|
602
778
|
}): Promise<KbLoadResult>;
|
|
603
779
|
/** How a position was arrived at, as a timeline. See `trace.ts`. */
|
|
604
780
|
trace(bundlePath: string, seedId: string, options?: KbTraceOptions): Promise<KbTraceStep[]>;
|
|
@@ -842,6 +1018,11 @@ declare const composeInputSchema: z.ZodObject<{
|
|
|
842
1018
|
anchors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
843
1019
|
file: z.ZodString;
|
|
844
1020
|
symbol: z.ZodOptional<z.ZodString>;
|
|
1021
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
1022
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
1023
|
+
hash: z.ZodOptional<z.ZodString>;
|
|
1024
|
+
resolved_at: z.ZodOptional<z.ZodString>;
|
|
1025
|
+
lines: z.ZodOptional<z.ZodNumber>;
|
|
845
1026
|
}, z.core.$strict>>>;
|
|
846
1027
|
sources: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
847
1028
|
id: z.ZodString;
|
|
@@ -1281,9 +1462,10 @@ declare function validateBundle(records: KbRecord[]): KbValidationProblem[];
|
|
|
1281
1462
|
* decay silently, because a stale record reads exactly like a live one and a
|
|
1282
1463
|
* question nobody answered reads exactly like one nobody asked.
|
|
1283
1464
|
*
|
|
1284
|
-
* Grouped and counted rather than merged into one list: the
|
|
1285
|
-
*
|
|
1286
|
-
* and a flat list of "problems" would leave the reader sorting
|
|
1465
|
+
* Grouped and counted rather than merged into one list: the eight checks are
|
|
1466
|
+
* eight different repairs — re-verify, re-date, answer, link, supersede, or
|
|
1467
|
+
* re-anchor — and a flat list of "problems" would leave the reader sorting
|
|
1468
|
+
* them again.
|
|
1287
1469
|
*
|
|
1288
1470
|
* Every group is emitted even when empty. A check that found nothing and a
|
|
1289
1471
|
* check that never ran look identical in a report that only lists findings,
|
|
@@ -1292,7 +1474,7 @@ declare function validateBundle(records: KbRecord[]): KbValidationProblem[];
|
|
|
1292
1474
|
declare const DEFAULT_EXPIRING_DAYS = 30;
|
|
1293
1475
|
declare const DEFAULT_UNVERIFIED_DAYS = 90;
|
|
1294
1476
|
declare const DEFAULT_AGING_DAYS = 90;
|
|
1295
|
-
declare const KB_DOCTOR_CHECKS: readonly ["expired", "expiring", "unverified", "aging", "orphaned", "broken-supersession", "superseded-but-cited"];
|
|
1477
|
+
declare const KB_DOCTOR_CHECKS: readonly ["expired", "expiring", "unverified", "aging", "orphaned", "broken-supersession", "superseded-but-cited", "drifted"];
|
|
1296
1478
|
type KbDoctorCheck = (typeof KB_DOCTOR_CHECKS)[number];
|
|
1297
1479
|
type KbDoctorFinding = {
|
|
1298
1480
|
conceptId: string;
|
|
@@ -1317,7 +1499,7 @@ type KbDoctorReport = {
|
|
|
1317
1499
|
recordCount: number;
|
|
1318
1500
|
thresholds: KbDoctorThresholds;
|
|
1319
1501
|
counts: Record<KbDoctorCheck, number>;
|
|
1320
|
-
/** All
|
|
1502
|
+
/** All eight, in `KB_DOCTOR_CHECKS` order, empty ones included. */
|
|
1321
1503
|
groups: KbDoctorGroup[];
|
|
1322
1504
|
findingCount: number;
|
|
1323
1505
|
healthy: boolean;
|
|
@@ -1330,6 +1512,13 @@ type KbDoctorOptions = {
|
|
|
1330
1512
|
/** How long `open` or `proposed` may stand before `aging` reports it. */
|
|
1331
1513
|
agingDays?: number;
|
|
1332
1514
|
now?: Date;
|
|
1515
|
+
/**
|
|
1516
|
+
* Anchor drift, precomputed by the caller. `doctor` stays pure and sync for
|
|
1517
|
+
* the same reason `adjudicate` does — the filesystem work of re-resolving
|
|
1518
|
+
* anchors belongs to `detectAnchorDrift`, and a sweep with no map simply
|
|
1519
|
+
* reports the `drifted` check as clean rather than half-running it.
|
|
1520
|
+
*/
|
|
1521
|
+
anchorDrift?: Map<string, KbAnchorDriftEntry[]>;
|
|
1333
1522
|
};
|
|
1334
1523
|
declare function doctor(bundle: KbRecord[], options?: KbDoctorOptions): KbDoctorReport;
|
|
1335
1524
|
|
|
@@ -1377,6 +1566,11 @@ declare const decisionInputSchema: z.ZodObject<{
|
|
|
1377
1566
|
anchors: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1378
1567
|
file: z.ZodString;
|
|
1379
1568
|
symbol: z.ZodOptional<z.ZodString>;
|
|
1569
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
1570
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
1571
|
+
hash: z.ZodOptional<z.ZodString>;
|
|
1572
|
+
resolved_at: z.ZodOptional<z.ZodString>;
|
|
1573
|
+
lines: z.ZodOptional<z.ZodNumber>;
|
|
1380
1574
|
}, z.core.$strict>>>;
|
|
1381
1575
|
verify: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1382
1576
|
relatedConceptIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -1611,4 +1805,4 @@ declare function parseMarkdownWithFrontmatter<S extends z.ZodType>(text: string,
|
|
|
1611
1805
|
frontmatter: ReturnType<S["safeParse"]>;
|
|
1612
1806
|
};
|
|
1613
1807
|
|
|
1614
|
-
export { BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposedRecord, DECISION_TYPE, DEFAULT_AGING_DAYS, DEFAULT_EXPIRING_DAYS, DEFAULT_LOAD_BUDGET, DEFAULT_PACK_HOPS, DEFAULT_PACK_MAX_NODES, DEFAULT_UNVERIFIED_DAYS, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_DOCTOR_CHECKS, KB_EDGE_KINDS, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, KbBaseFrozenError, type KbCatalogEntry, type KbCatalogResult, type KbCommand, type KbCommandContext, type KbContextBudgets, type KbContextOptions, type KbContextResult, type KbDoctorCheck, type KbDoctorFinding, type KbDoctorGroup, type KbDoctorOptions, type KbDoctorReport, type KbDoctorThresholds, type KbEdgeKind, KbInvalidConceptIdError, type KbLoadResult, type KbLogEntry, type KbLogReadResult, type KbLogger, type KbMergedPin, type KbMergedPins, KbMissingFlagValueError, type KbNeighbour, KbPackBudgetExceededError, type KbPackOptions, type KbPackResult, type KbPackedRecord, type KbPin, type KbPinLayer, type KbPinOptions, type KbPinResult, type KbPinStatus, KbPinsMalformedError, type KbPinsManifest, type KbRecord, KbRecordAlreadyExistsError, type KbRecordFrontmatter, KbRecordNotFoundError, type KbRecordStatus, type KbRecordType, type KbRecordTypeSpec, type KbSearchLogger, KbSelfVerificationError, type KbSource, type KbStanding, KbStore, type KbSupersededStub, type KbSyncResult, type KbTraceEdge, type KbTraceOptions, type KbTraceStep, type KbValidationProblem, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LOG_FILE, type MatchOptions, NO_DECISION_SLUG, PINS_FILE, PINS_LOCAL_FILE, PIN_LAYERS, type QmdModule, RECORD_TYPES, SEARCH_INDEX_FILE, type SearchHit, type SearchOptions, type SymbolRange, TRACE_EDGES, adjudicate, assertBaseNotFrozen, buildContext, catalog, composeDecisionRecord, composeInputSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, doctor, edgeNeighbours, indexIsStale, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLogEntrySchema, kbRecordFrontmatterSchema, kbSourceSchema, kbVerifiedEventSchema, listPins, loadQmd, matchToDiff, mergedContextBudgets, neighbours, pack, parseLog, parseMarkdownWithFrontmatter, pinBase, readMergedPins, readPinsLayer, renderCatalogLine, renderIndex, renderIndexLine, renderLogEntry, resolveHeads, resolveHits, resolvePinPath, runKbCli, runKbMcpServer, searchBase, selectDecisions, splitMarkdownFrontmatter, stringifyMarkdownWithFrontmatter, syncInstructions, toHookJson, trace, unpinBase, validateBundle };
|
|
1808
|
+
export { type AnchorResolver, BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposedRecord, DECISION_TYPE, DEFAULT_AGING_DAYS, DEFAULT_EXPIRING_DAYS, DEFAULT_LOAD_BUDGET, DEFAULT_PACK_HOPS, DEFAULT_PACK_MAX_NODES, DEFAULT_UNVERIFIED_DAYS, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_DOCTOR_CHECKS, KB_EDGE_KINDS, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, type KbAnchorDriftEntry, KbBaseFrozenError, type KbCatalogEntry, type KbCatalogResult, type KbCommand, type KbCommandContext, type KbContextBudgets, type KbContextOptions, type KbContextResult, type KbDoctorCheck, type KbDoctorFinding, type KbDoctorGroup, type KbDoctorOptions, type KbDoctorReport, type KbDoctorThresholds, type KbEdgeKind, KbInvalidConceptIdError, type KbLoadResult, type KbLogEntry, type KbLogReadResult, type KbLogger, type KbMergedPin, type KbMergedPins, KbMissingFlagValueError, type KbNeighbour, KbPackBudgetExceededError, type KbPackOptions, type KbPackResult, type KbPackedRecord, type KbPin, type KbPinLayer, type KbPinOptions, type KbPinResult, type KbPinStatus, KbPinsMalformedError, type KbPinsManifest, type KbRecord, KbRecordAlreadyExistsError, type KbRecordFrontmatter, KbRecordNotFoundError, type KbRecordStatus, type KbRecordType, type KbRecordTypeSpec, type KbSearchLogger, KbSelfVerificationError, type KbSource, type KbStanding, KbStore, type KbSupersededStub, type KbSyncResult, type KbTraceEdge, type KbTraceOptions, type KbTraceStep, type KbValidationProblem, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LOG_FILE, type MatchOptions, NO_DECISION_SLUG, PINS_FILE, PINS_LOCAL_FILE, PIN_LAYERS, type QmdModule, RECORD_TYPES, type ResolvedSymbol, SEARCH_INDEX_FILE, type SearchHit, type SearchOptions, type SymbolRange, TRACE_EDGES, adjudicate, anchorFilePath, assertBaseNotFrozen, buildContext, catalog, composeDecisionRecord, composeInputSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, detectAnchorDrift, doctor, edgeNeighbours, hashAnchorText, indexIsStale, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLogEntrySchema, kbRecordFrontmatterSchema, kbSourceSchema, kbVerifiedEventSchema, listPins, loadQmd, matchToDiff, mergedContextBudgets, neighbours, pack, parseLog, parseMarkdownWithFrontmatter, pinBase, readMergedPins, readPinsLayer, regexResolver, renderCatalogLine, renderIndex, renderIndexLine, renderLogEntry, resolveAnchor, resolveHeads, resolveHits, resolvePinPath, runKbCli, runKbMcpServer, searchBase, selectDecisions, splitMarkdownFrontmatter, stringifyMarkdownWithFrontmatter, syncInstructions, toHookJson, trace, unpinBase, validateBundle };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runKbCli
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-F2U2YLWV.js";
|
|
4
4
|
import {
|
|
5
5
|
createKbMcpServer,
|
|
6
6
|
runKbMcpServer
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EXKK2KUN.js";
|
|
8
8
|
import {
|
|
9
9
|
BaseError,
|
|
10
10
|
CONTEXT_BEGIN,
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
SEARCH_INDEX_FILE,
|
|
51
51
|
TRACE_EDGES,
|
|
52
52
|
adjudicate,
|
|
53
|
+
anchorFilePath,
|
|
53
54
|
assertBaseNotFrozen,
|
|
54
55
|
buildContext,
|
|
55
56
|
catalog,
|
|
@@ -59,8 +60,10 @@ import {
|
|
|
59
60
|
composeRecord,
|
|
60
61
|
contextProfileBudgets,
|
|
61
62
|
decisionInputSchema,
|
|
63
|
+
detectAnchorDrift,
|
|
62
64
|
doctor,
|
|
63
65
|
edgeNeighbours,
|
|
66
|
+
hashAnchorText,
|
|
64
67
|
indexIsStale,
|
|
65
68
|
isKbRecordType,
|
|
66
69
|
isNoDecisionRecord,
|
|
@@ -82,10 +85,12 @@ import {
|
|
|
82
85
|
pinBase,
|
|
83
86
|
readMergedPins,
|
|
84
87
|
readPinsLayer,
|
|
88
|
+
regexResolver,
|
|
85
89
|
renderCatalogLine,
|
|
86
90
|
renderIndex,
|
|
87
91
|
renderIndexLine,
|
|
88
92
|
renderLogEntry,
|
|
93
|
+
resolveAnchor,
|
|
89
94
|
resolveHeads,
|
|
90
95
|
resolveHits,
|
|
91
96
|
resolvePinPath,
|
|
@@ -98,7 +103,7 @@ import {
|
|
|
98
103
|
trace,
|
|
99
104
|
unpinBase,
|
|
100
105
|
validateBundle
|
|
101
|
-
} from "./chunk-
|
|
106
|
+
} from "./chunk-33ZCBEUV.js";
|
|
102
107
|
|
|
103
108
|
// src/match-diff.ts
|
|
104
109
|
function matchToDiff(files, records, options = {}) {
|
|
@@ -224,6 +229,7 @@ export {
|
|
|
224
229
|
SEARCH_INDEX_FILE,
|
|
225
230
|
TRACE_EDGES,
|
|
226
231
|
adjudicate,
|
|
232
|
+
anchorFilePath,
|
|
227
233
|
assertBaseNotFrozen,
|
|
228
234
|
buildContext,
|
|
229
235
|
catalog,
|
|
@@ -234,8 +240,10 @@ export {
|
|
|
234
240
|
contextProfileBudgets,
|
|
235
241
|
createKbMcpServer,
|
|
236
242
|
decisionInputSchema,
|
|
243
|
+
detectAnchorDrift,
|
|
237
244
|
doctor,
|
|
238
245
|
edgeNeighbours,
|
|
246
|
+
hashAnchorText,
|
|
239
247
|
indexIsStale,
|
|
240
248
|
isKbRecordType,
|
|
241
249
|
isNoDecisionRecord,
|
|
@@ -258,10 +266,12 @@ export {
|
|
|
258
266
|
pinBase,
|
|
259
267
|
readMergedPins,
|
|
260
268
|
readPinsLayer,
|
|
269
|
+
regexResolver,
|
|
261
270
|
renderCatalogLine,
|
|
262
271
|
renderIndex,
|
|
263
272
|
renderIndexLine,
|
|
264
273
|
renderLogEntry,
|
|
274
|
+
resolveAnchor,
|
|
265
275
|
resolveHeads,
|
|
266
276
|
resolveHits,
|
|
267
277
|
resolvePinPath,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/match-diff.ts"],"sourcesContent":["import { adjudicate, type KbAdjudicated } from \"./adjudicate.js\";\nimport type { KbAnchor, KbRecord } from \"./kb-record.schema.js\";\n\n/**\n * Which records apply to which part of a change.\n *\n * Takes a structural description of a diff rather than a patch, so this package\n * carries no diff parser: callers already have one, and a knowledge base has no\n * business preferring a particular flavour of unified diff.\n *\n * Deterministic on purpose. Every step here is mechanical — the one judgment,\n * whether a matched record is worth showing a reviewer, is deliberately absent.\n * A model placed here would sit between the reviewer and their diff on every\n * review, to answer a question nobody has yet shown needs asking.\n *\n * Distinct from `load()`, which hands a reader the whole base. That answers\n * \"does anything address this question\"; this answers \"what is attached to this\n * code\", and an anchor is the author's own statement rather than an inference\n * from one. A reader guessing which record relates to a hunk would be guessing\n * at something already written down — and a diff has dozens of hunks, which is\n * dozens of reader calls against microseconds of matching. Where they compose:\n * this narrows a hunk to a few records, and a reader asked to explain them gets\n * those, not the base.\n */\nexport type DiffHunk = {\n /** 1-based, inclusive, in the file's post-change line numbering. */\n startLine: number;\n endLine: number;\n};\n\nexport type DiffFile = {\n /** Repo-relative, matching how anchors are written. */\n filePath: string;\n hunks: DiffHunk[];\n};\n\n/**\n * A symbol resolved to lines. Supplied by whatever the caller uses to index\n * symbols; absence is tolerated — see `place()`.\n */\nexport type SymbolRange = {\n file: string;\n symbol: string;\n startLine: number;\n endLine: number;\n};\n\nexport type DiffMatch = {\n filePath: string;\n hunk: DiffHunk;\n /** Current records first — what still holds should be read before what does not. */\n records: KbAdjudicated[];\n /**\n * `symbol` when every record here was placed by a resolved symbol range,\n * `file` when at least one fell back to the whole file. Reported rather than\n * hidden: a caller showing a file-level match as though it were pinned to\n * these lines is claiming a precision it does not have.\n */\n precision: \"symbol\" | \"file\";\n};\n\nexport type MatchOptions = {\n /** Without these, symbol anchors degrade to file level rather than vanishing. */\n symbolRanges?: SymbolRange[];\n now?: Date;\n};\n\nexport function matchToDiff(\n files: DiffFile[],\n records: KbRecord[],\n options: MatchOptions = {},\n): DiffMatch[] {\n const ranges = indexRanges(options.symbolRanges ?? []);\n const anchored = records.filter(\n (record) => (record.frontmatter.strauss_anchors ?? []).length > 0,\n );\n const matches: DiffMatch[] = [];\n\n for (const file of files) {\n const candidates = anchored\n .map((record) => ({\n record,\n anchors: (record.frontmatter.strauss_anchors ?? []).filter(\n (anchor) => normalize(anchor.file) === normalize(file.filePath),\n ),\n }))\n .filter(({ anchors }) => anchors.length > 0);\n if (!candidates.length) continue;\n\n for (const hunk of file.hunks) {\n const hits: KbRecord[] = [];\n let precision: DiffMatch[\"precision\"] = \"symbol\";\n\n for (const { record, anchors } of candidates) {\n const placement = place(anchors, file.filePath, hunk, ranges);\n if (placement === \"miss\") continue;\n if (placement === \"file\") precision = \"file\";\n hits.push(record);\n }\n\n if (!hits.length) continue;\n matches.push({\n filePath: file.filePath,\n hunk,\n records: order(adjudicate(hits, records, options.now)),\n precision,\n });\n }\n }\n\n return matches;\n}\n\n/**\n * Whether any of a record's anchors puts it on this hunk.\n *\n * An anchor naming only a file is about the whole file, so it lands on every\n * hunk in it. One naming a symbol lands only where that symbol's lines overlap\n * — unless nothing resolved the symbol, in which case it falls back to the file\n * rather than disappearing. A record silently absent because a resolver was\n * unavailable is worse than one shown imprecisely and labelled as such.\n */\nfunction place(\n anchors: KbAnchor[],\n filePath: string,\n hunk: DiffHunk,\n ranges: Map<string, SymbolRange[]>,\n): \"symbol\" | \"file\" | \"miss\" {\n let fallback: \"file\" | \"miss\" = \"miss\";\n\n for (const anchor of anchors) {\n if (!anchor.symbol) return \"file\";\n\n const resolved = ranges.get(key(filePath, anchor.symbol));\n if (!resolved?.length) {\n fallback = \"file\";\n continue;\n }\n if (resolved.some((range) => overlaps(range, hunk))) return \"symbol\";\n }\n\n return fallback;\n}\n\nfunction overlaps(range: SymbolRange, hunk: DiffHunk): boolean {\n return range.startLine <= hunk.endLine && hunk.startLine <= range.endLine;\n}\n\n/** Current before superseded, then oldest first, so an arc reads in order. */\nfunction order(records: KbAdjudicated[]): KbAdjudicated[] {\n const rank: Record<string, number> = {\n current: 0,\n unsettled: 1,\n open: 2,\n superseded: 3,\n rejected: 4,\n };\n return [...records].sort(\n (left, right) =>\n (rank[left.standing] ?? 9) - (rank[right.standing] ?? 9) ||\n (left.record.frontmatter.generated?.at ?? \"\").localeCompare(\n right.record.frontmatter.generated?.at ?? \"\",\n ),\n );\n}\n\nfunction indexRanges(ranges: SymbolRange[]): Map<string, SymbolRange[]> {\n const byKey = new Map<string, SymbolRange[]>();\n for (const range of ranges) {\n const id = key(range.file, range.symbol);\n byKey.set(id, [...(byKey.get(id) ?? []), range]);\n }\n return byKey;\n}\n\nfunction key(file: string, symbol: string): string {\n return `${normalize(file)}#${symbol}`;\n}\n\n/** Anchors are written by hand often enough that `./` shows up. */\nfunction normalize(path: string): string {\n return path.replace(/^\\.\\//, \"\");\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/match-diff.ts"],"sourcesContent":["import { adjudicate, type KbAdjudicated } from \"./adjudicate.js\";\nimport type { KbAnchor, KbRecord } from \"./kb-record.schema.js\";\n\n/**\n * Which records apply to which part of a change.\n *\n * Takes a structural description of a diff rather than a patch, so this package\n * carries no diff parser: callers already have one, and a knowledge base has no\n * business preferring a particular flavour of unified diff.\n *\n * Deterministic on purpose. Every step here is mechanical — the one judgment,\n * whether a matched record is worth showing a reviewer, is deliberately absent.\n * A model placed here would sit between the reviewer and their diff on every\n * review, to answer a question nobody has yet shown needs asking.\n *\n * Distinct from `load()`, which hands a reader the whole base. That answers\n * \"does anything address this question\"; this answers \"what is attached to this\n * code\", and an anchor is the author's own statement rather than an inference\n * from one. A reader guessing which record relates to a hunk would be guessing\n * at something already written down — and a diff has dozens of hunks, which is\n * dozens of reader calls against microseconds of matching. Where they compose:\n * this narrows a hunk to a few records, and a reader asked to explain them gets\n * those, not the base.\n */\nexport type DiffHunk = {\n /** 1-based, inclusive, in the file's post-change line numbering. */\n startLine: number;\n endLine: number;\n};\n\nexport type DiffFile = {\n /** Repo-relative, matching how anchors are written. */\n filePath: string;\n hunks: DiffHunk[];\n};\n\n/**\n * A symbol resolved to lines. Supplied by whatever the caller uses to index\n * symbols; absence is tolerated — see `place()`.\n */\nexport type SymbolRange = {\n file: string;\n symbol: string;\n startLine: number;\n endLine: number;\n};\n\nexport type DiffMatch = {\n filePath: string;\n hunk: DiffHunk;\n /** Current records first — what still holds should be read before what does not. */\n records: KbAdjudicated[];\n /**\n * `symbol` when every record here was placed by a resolved symbol range,\n * `file` when at least one fell back to the whole file. Reported rather than\n * hidden: a caller showing a file-level match as though it were pinned to\n * these lines is claiming a precision it does not have.\n */\n precision: \"symbol\" | \"file\";\n};\n\nexport type MatchOptions = {\n /** Without these, symbol anchors degrade to file level rather than vanishing. */\n symbolRanges?: SymbolRange[];\n now?: Date;\n};\n\nexport function matchToDiff(\n files: DiffFile[],\n records: KbRecord[],\n options: MatchOptions = {},\n): DiffMatch[] {\n const ranges = indexRanges(options.symbolRanges ?? []);\n const anchored = records.filter(\n (record) => (record.frontmatter.strauss_anchors ?? []).length > 0,\n );\n const matches: DiffMatch[] = [];\n\n for (const file of files) {\n const candidates = anchored\n .map((record) => ({\n record,\n anchors: (record.frontmatter.strauss_anchors ?? []).filter(\n (anchor) => normalize(anchor.file) === normalize(file.filePath),\n ),\n }))\n .filter(({ anchors }) => anchors.length > 0);\n if (!candidates.length) continue;\n\n for (const hunk of file.hunks) {\n const hits: KbRecord[] = [];\n let precision: DiffMatch[\"precision\"] = \"symbol\";\n\n for (const { record, anchors } of candidates) {\n const placement = place(anchors, file.filePath, hunk, ranges);\n if (placement === \"miss\") continue;\n if (placement === \"file\") precision = \"file\";\n hits.push(record);\n }\n\n if (!hits.length) continue;\n matches.push({\n filePath: file.filePath,\n hunk,\n records: order(adjudicate(hits, records, options.now)),\n precision,\n });\n }\n }\n\n return matches;\n}\n\n/**\n * Whether any of a record's anchors puts it on this hunk.\n *\n * An anchor naming only a file is about the whole file, so it lands on every\n * hunk in it. One naming a symbol lands only where that symbol's lines overlap\n * — unless nothing resolved the symbol, in which case it falls back to the file\n * rather than disappearing. A record silently absent because a resolver was\n * unavailable is worse than one shown imprecisely and labelled as such.\n */\nfunction place(\n anchors: KbAnchor[],\n filePath: string,\n hunk: DiffHunk,\n ranges: Map<string, SymbolRange[]>,\n): \"symbol\" | \"file\" | \"miss\" {\n let fallback: \"file\" | \"miss\" = \"miss\";\n\n for (const anchor of anchors) {\n if (!anchor.symbol) return \"file\";\n\n const resolved = ranges.get(key(filePath, anchor.symbol));\n if (!resolved?.length) {\n fallback = \"file\";\n continue;\n }\n if (resolved.some((range) => overlaps(range, hunk))) return \"symbol\";\n }\n\n return fallback;\n}\n\nfunction overlaps(range: SymbolRange, hunk: DiffHunk): boolean {\n return range.startLine <= hunk.endLine && hunk.startLine <= range.endLine;\n}\n\n/** Current before superseded, then oldest first, so an arc reads in order. */\nfunction order(records: KbAdjudicated[]): KbAdjudicated[] {\n const rank: Record<string, number> = {\n current: 0,\n unsettled: 1,\n open: 2,\n superseded: 3,\n rejected: 4,\n };\n return [...records].sort(\n (left, right) =>\n (rank[left.standing] ?? 9) - (rank[right.standing] ?? 9) ||\n (left.record.frontmatter.generated?.at ?? \"\").localeCompare(\n right.record.frontmatter.generated?.at ?? \"\",\n ),\n );\n}\n\nfunction indexRanges(ranges: SymbolRange[]): Map<string, SymbolRange[]> {\n const byKey = new Map<string, SymbolRange[]>();\n for (const range of ranges) {\n const id = key(range.file, range.symbol);\n byKey.set(id, [...(byKey.get(id) ?? []), range]);\n }\n return byKey;\n}\n\nfunction key(file: string, symbol: string): string {\n return `${normalize(file)}#${symbol}`;\n}\n\n/** Anchors are written by hand often enough that `./` shows up. */\nfunction normalize(path: string): string {\n return path.replace(/^\\.\\//, \"\");\n}\n"],"mappings":"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","names":[]}
|