@saasontools/strauss-kb 0.1.14 → 0.1.16
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 +20 -2
- package/README.md +44 -28
- package/dist/{chunk-43KALLFU.js → chunk-H5W53NVU.js} +1090 -388
- package/dist/chunk-H5W53NVU.js.map +1 -0
- package/dist/{chunk-PYA5E7FL.js → chunk-MEZCF646.js} +2 -2
- package/dist/{chunk-MBXNCZ4V.js → chunk-RINBOAQZ.js} +15 -6
- package/dist/chunk-RINBOAQZ.js.map +1 -0
- package/dist/cli-main.cjs +1111 -404
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +2 -2
- package/dist/index.cjs +1152 -434
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -69
- package/dist/index.d.ts +174 -69
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-main.cjs +1100 -402
- package/dist/mcp-main.cjs.map +1 -1
- package/dist/mcp-main.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-43KALLFU.js.map +0 -1
- package/dist/chunk-MBXNCZ4V.js.map +0 -1
- /package/dist/{chunk-PYA5E7FL.js.map → chunk-MEZCF646.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -187,12 +187,14 @@ type KbRecord = {
|
|
|
187
187
|
body: string;
|
|
188
188
|
};
|
|
189
189
|
|
|
190
|
+
/** Where bare mirrors live. Overridable so a test never writes to `$HOME`. */
|
|
191
|
+
declare function repoCacheDir(override?: string): string;
|
|
192
|
+
|
|
190
193
|
/**
|
|
191
|
-
*
|
|
194
|
+
* The vocabulary anchor resolution is reported in.
|
|
192
195
|
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* reported `unresolved` rather than guessed at.
|
|
196
|
+
* Every failure is a finding with a reason, never a throw: a record pointing
|
|
197
|
+
* at code that moved is information, not a broken run.
|
|
196
198
|
*/
|
|
197
199
|
type ResolvedSymbol = {
|
|
198
200
|
text: string;
|
|
@@ -206,36 +208,22 @@ interface AnchorResolver {
|
|
|
206
208
|
}
|
|
207
209
|
/** Why an anchor could not be compared. Never an error — always a finding. */
|
|
208
210
|
type AnchorUnresolvedReason = "file-missing" | "symbol-not-found" | "outside-repo" | "file-too-large" | "file-unreadable"
|
|
209
|
-
/**
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
*/
|
|
226
|
-
declare const regexResolver: AnchorResolver;
|
|
227
|
-
/** CRLF normalized to LF before hashing, so checkout style cannot read as drift. */
|
|
228
|
-
declare function hashAnchorText(text: string): string;
|
|
229
|
-
/**
|
|
230
|
-
* An anchor without a symbol is about the whole file; with one, the resolver
|
|
231
|
-
* decides. Source newlines are normalized first so line counts and hashes
|
|
232
|
-
* agree with `hashAnchorText`.
|
|
233
|
-
*
|
|
234
|
-
* A file's last line is the last line with content: a trailing newline is a
|
|
235
|
-
* terminator, not an empty line, and counting it would have made every
|
|
236
|
-
* whole-file anchor's `lines` one larger than the file.
|
|
237
|
-
*/
|
|
238
|
-
declare function resolveAnchor(source: string, anchor: KbAnchor, resolver?: AnchorResolver): ResolvedSymbol | null;
|
|
211
|
+
/** The remote could not be fetched, or `--offline` found nothing cached. */
|
|
212
|
+
| "remote-unreachable"
|
|
213
|
+
/** The anchor's `ref` is not on the remote any more. */
|
|
214
|
+
| "ref-not-found" | "repo-unauthorized"
|
|
215
|
+
/** No default branch, so there is no "current" to compare against. */
|
|
216
|
+
| "default-branch-unknown"
|
|
217
|
+
/** The anchor's `ref` is not a name git may safely be handed. */
|
|
218
|
+
| "ref-invalid"
|
|
219
|
+
/** The anchor's `repo` is not a remote we will fetch from. */
|
|
220
|
+
| "repo-invalid";
|
|
221
|
+
/**
|
|
222
|
+
* How a ref-pinned foreign anchor stands. `drifted-on-default` is the one a
|
|
223
|
+
* working-tree anchor has no equivalent of: the evidence is still true at the
|
|
224
|
+
* commit it was taken from, and the code has moved since.
|
|
225
|
+
*/
|
|
226
|
+
type RemoteAnchorState = "matches-ref" | "drifted-from-ref" | "drifted-on-default";
|
|
239
227
|
type KbAnchorDriftEntry = {
|
|
240
228
|
file: string;
|
|
241
229
|
symbol?: string;
|
|
@@ -245,17 +233,10 @@ type KbAnchorDriftEntry = {
|
|
|
245
233
|
/** `null` when the anchor recorded no `lines` — size unknown, not zero. */
|
|
246
234
|
diffSize: number | null;
|
|
247
235
|
reason?: AnchorUnresolvedReason;
|
|
236
|
+
/** Set only when the anchor was resolved against another repository. */
|
|
237
|
+
repo?: string;
|
|
238
|
+
remoteState?: RemoteAnchorState;
|
|
248
239
|
};
|
|
249
|
-
/**
|
|
250
|
-
* An anchor's `file` must stay inside the repository root — a record points
|
|
251
|
-
* at code, not at arbitrary files on the machine reading it. Bundles are
|
|
252
|
-
* data, so a traversal or absolute path here is untrusted input, not a bug
|
|
253
|
-
* in the caller. Returns the resolved path, or `null` when it escapes.
|
|
254
|
-
*
|
|
255
|
-
* Lexical only, and therefore not the whole containment check: see
|
|
256
|
-
* `readAnchorFile`, which re-tests the real path after following symlinks.
|
|
257
|
-
*/
|
|
258
|
-
declare function anchorFilePath(repoRoot: string, file: string): string | null;
|
|
259
240
|
type AnchorRead = {
|
|
260
241
|
ok: true;
|
|
261
242
|
source: string;
|
|
@@ -264,26 +245,109 @@ type AnchorRead = {
|
|
|
264
245
|
reason: AnchorUnresolvedReason;
|
|
265
246
|
};
|
|
266
247
|
type AnchorFileReader = (file: string) => Promise<AnchorRead>;
|
|
248
|
+
|
|
267
249
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
250
|
+
* A foreign anchor's file at one rev. `ref` absent means the remote's default
|
|
251
|
+
* branch — the "current" side of a ref-pinned comparison.
|
|
252
|
+
*/
|
|
253
|
+
type RemoteWant = {
|
|
254
|
+
repo: string;
|
|
255
|
+
ref?: string;
|
|
256
|
+
file: string;
|
|
257
|
+
};
|
|
258
|
+
type RemoteRead = {
|
|
259
|
+
ok: true;
|
|
260
|
+
source: string;
|
|
261
|
+
} | {
|
|
262
|
+
ok: false;
|
|
263
|
+
reason: AnchorUnresolvedReason;
|
|
264
|
+
};
|
|
265
|
+
type RemoteOptions = {
|
|
266
|
+
/** Cache only: no `fetch`, no `ls-remote`. */
|
|
267
|
+
offline?: boolean;
|
|
268
|
+
cacheDir?: string;
|
|
269
|
+
fetchTimeoutMs?: number;
|
|
270
|
+
/** Repositories worked on at once; fetches within one repo stay serial. */
|
|
271
|
+
concurrency?: number;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Reads each wanted (repo, rev, file) out of a bare cache under
|
|
276
|
+
* `~/.strauss/repo-cache`, fetching once per (repo, rev) and never per anchor.
|
|
275
277
|
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* not depend on which read finished first.
|
|
278
|
+
* Every failure lands as an `unresolved` reason on the wants it affects; no
|
|
279
|
+
* path is read from disk, so containment does not apply here.
|
|
279
280
|
*/
|
|
280
|
-
declare function
|
|
281
|
+
declare function readRemoteAnchors(wants: readonly RemoteWant[], options?: RemoteOptions): Promise<Map<string, RemoteRead>>;
|
|
282
|
+
|
|
283
|
+
type AnchorDriftOptions = {
|
|
281
284
|
repoRoot?: string;
|
|
282
285
|
resolver?: AnchorResolver;
|
|
283
286
|
concurrency?: number;
|
|
284
287
|
/** Test seam: replaces the disk reader. */
|
|
285
288
|
reader?: AnchorFileReader;
|
|
286
|
-
|
|
289
|
+
/** Remote resolution of foreign anchors; `offline` keeps a run off the wire. */
|
|
290
|
+
remote?: RemoteOptions;
|
|
291
|
+
/** Test seam: replaces the remote blob reader. */
|
|
292
|
+
readRemote?: typeof readRemoteAnchors;
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Re-resolves every hash-carrying anchor and compares against the stored hash.
|
|
296
|
+
*
|
|
297
|
+
* An anchor naming another repository is read from that repository's remote
|
|
298
|
+
* through a bare cache; everything else is read from the working tree. A
|
|
299
|
+
* missing file, an unreachable remote, or an unresolvable symbol is a finding
|
|
300
|
+
* (`unresolved`), never a throw.
|
|
301
|
+
*
|
|
302
|
+
* Four phases: collect the checkable anchors, read the working tree's distinct
|
|
303
|
+
* files and the remotes' distinct (repo, rev, file) blobs, then resolve and
|
|
304
|
+
* hash in record order — so the output never depends on which read finished
|
|
305
|
+
* first.
|
|
306
|
+
*/
|
|
307
|
+
declare function detectAnchorDrift(records: KbRecord[], options?: AnchorDriftOptions): Promise<Map<string, KbAnchorDriftEntry[]>>;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* An anchor's `file` must stay inside the repository root — a record points
|
|
311
|
+
* at code, not at arbitrary files on the machine reading it. Bundles are
|
|
312
|
+
* data, so a traversal or absolute path here is untrusted input, not a bug
|
|
313
|
+
* in the caller. Returns the resolved path, or `null` when it escapes.
|
|
314
|
+
*
|
|
315
|
+
* Lexical only, and therefore not the whole containment check: see
|
|
316
|
+
* `readAnchorFile`, which re-tests the real path after following symlinks.
|
|
317
|
+
*/
|
|
318
|
+
declare function anchorFilePath(repoRoot: string, file: string): string | null;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Does this spelling say where the repository lives?
|
|
322
|
+
*
|
|
323
|
+
* Only a full URL can be fetched from. A short form still matches this root's
|
|
324
|
+
* own origin, which is why `validate` warns rather than rejects.
|
|
325
|
+
*/
|
|
326
|
+
declare function isCanonicalRepoUrl(value: string): boolean;
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* v1 heuristic resolver. A dotted symbol like `OrderService.cancel` matches on
|
|
330
|
+
* its last segment, with the parent used to scope the search: a candidate
|
|
331
|
+
* counts only if the parent name appears in the fifty lines above it, when any
|
|
332
|
+
* candidate satisfies that at all.
|
|
333
|
+
*
|
|
334
|
+
* Deterministic, and ambiguity is not resolved by guessing — two lines of
|
|
335
|
+
* equally good shape mean the resolver cannot tell which one the record meant,
|
|
336
|
+
* and it says so by returning `null`.
|
|
337
|
+
*/
|
|
338
|
+
declare const regexResolver: AnchorResolver;
|
|
339
|
+
/** CRLF normalized to LF before hashing, so checkout style cannot read as drift. */
|
|
340
|
+
declare function hashAnchorText(text: string): string;
|
|
341
|
+
/**
|
|
342
|
+
* An anchor without a symbol is about the whole file; with one, the resolver
|
|
343
|
+
* decides. Source newlines are normalized first so line counts and hashes
|
|
344
|
+
* agree with `hashAnchorText`.
|
|
345
|
+
*
|
|
346
|
+
* A file's last line is the last line with content: a trailing newline is a
|
|
347
|
+
* terminator, not an empty line, and counting it would have made every
|
|
348
|
+
* whole-file anchor's `lines` one larger than the file.
|
|
349
|
+
*/
|
|
350
|
+
declare function resolveAnchor(source: string, anchor: KbAnchor, resolver?: AnchorResolver): ResolvedSymbol | null;
|
|
287
351
|
|
|
288
352
|
/**
|
|
289
353
|
* Why a matched record must not be read as a plain answer.
|
|
@@ -334,13 +398,23 @@ type KbWarning =
|
|
|
334
398
|
* the record may describe code that no longer exists in that form. */
|
|
335
399
|
| {
|
|
336
400
|
kind: "drifted";
|
|
337
|
-
anchors:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
401
|
+
anchors: KbWarningAnchor[];
|
|
402
|
+
}
|
|
403
|
+
/** A foreign anchor nothing could check: the remote was unreachable, refused,
|
|
404
|
+
* or the run was offline. Neither drift nor a clean match. */
|
|
405
|
+
| {
|
|
406
|
+
kind: "unchecked";
|
|
407
|
+
anchors: KbWarningAnchor[];
|
|
408
|
+
};
|
|
409
|
+
type KbWarningAnchor = {
|
|
410
|
+
file: string;
|
|
411
|
+
symbol?: string;
|
|
412
|
+
/** `null` when the anchor recorded no line count — size unknown. */
|
|
413
|
+
diffSize: number | null;
|
|
414
|
+
reason?: string;
|
|
415
|
+
/** Set only for an anchor resolved against another repository. */
|
|
416
|
+
repo?: string;
|
|
417
|
+
remoteState?: string;
|
|
344
418
|
};
|
|
345
419
|
type KbStanding = "current" | "superseded" | "rejected" | "unsettled" | "open";
|
|
346
420
|
type KbAdjudicated = {
|
|
@@ -376,6 +450,11 @@ declare function resolveHeads(from: KbRecord, byId: Map<string, KbRecord>): {
|
|
|
376
450
|
warnings: KbWarning[];
|
|
377
451
|
};
|
|
378
452
|
|
|
453
|
+
type KbRecordStamp = {
|
|
454
|
+
conceptId: string;
|
|
455
|
+
digest: string;
|
|
456
|
+
};
|
|
457
|
+
|
|
379
458
|
/**
|
|
380
459
|
* Edges a trace may follow — the shared kb-edges.ts definitions, minus
|
|
381
460
|
* `body-link`: body links can reach most of a bundle from anywhere, which
|
|
@@ -742,6 +821,16 @@ type KbLoadResult = {
|
|
|
742
821
|
*/
|
|
743
822
|
digest: string;
|
|
744
823
|
};
|
|
824
|
+
/** What `stamp` reports for one base. See `kb-stamp.ts`. */
|
|
825
|
+
type KbStampResult = {
|
|
826
|
+
path: string;
|
|
827
|
+
digest: string;
|
|
828
|
+
recordCount: number;
|
|
829
|
+
superseded: number;
|
|
830
|
+
/** Newest `generated.at` across the base, or null when none carries one. */
|
|
831
|
+
newestAt: string | null;
|
|
832
|
+
records: KbRecordStamp[];
|
|
833
|
+
};
|
|
745
834
|
type KbWriteInput = {
|
|
746
835
|
type: string;
|
|
747
836
|
slug: string;
|
|
@@ -858,7 +947,8 @@ declare class KbStore {
|
|
|
858
947
|
* at the repo root, and the MCP server's cwd is the workspace.
|
|
859
948
|
*
|
|
860
949
|
* Public because `doctor` needs the same map with the same degradation: a
|
|
861
|
-
* sweep that failed to read the tree should report no drift, not fail
|
|
950
|
+
* sweep that failed to read the tree should report no drift, not fail — and
|
|
951
|
+
* `offline: false` there, because a sweep is worth a fetch.
|
|
862
952
|
*
|
|
863
953
|
* When no root was given and not one anchored file was found, the finding is
|
|
864
954
|
* discarded. A base read from somewhere other than the tree it describes
|
|
@@ -870,7 +960,9 @@ declare class KbStore {
|
|
|
870
960
|
* plausible, and the misses become findings again; an explicit `repoRoot` is
|
|
871
961
|
* taken at its word either way.
|
|
872
962
|
*/
|
|
873
|
-
detectDrift(records: KbRecord[], repoRoot?: string
|
|
963
|
+
detectDrift(records: KbRecord[], repoRoot?: string, options?: {
|
|
964
|
+
offline?: boolean;
|
|
965
|
+
}): Promise<Map<string, KbAnchorDriftEntry[]> | undefined>;
|
|
874
966
|
/**
|
|
875
967
|
* The whole base, adjudicated, when it is small enough to hand over.
|
|
876
968
|
*
|
|
@@ -907,6 +999,13 @@ declare class KbStore {
|
|
|
907
999
|
all?: boolean;
|
|
908
1000
|
repoRoot?: string;
|
|
909
1001
|
}): Promise<KbLoadResult>;
|
|
1002
|
+
/**
|
|
1003
|
+
* `load`'s digest without `load`'s bodies — the same records, adjudicated
|
|
1004
|
+
* the same way, handed back as a stamp. Skips the anchor drift pass, which
|
|
1005
|
+
* reads source files and only ever adds warnings: no warning reaches the
|
|
1006
|
+
* digest, so the value is identical to the one `load` returns.
|
|
1007
|
+
*/
|
|
1008
|
+
stamp(bundlePath: string): Promise<KbStampResult>;
|
|
910
1009
|
/** How a position was arrived at, as a timeline. See `trace.ts`. */
|
|
911
1010
|
trace(bundlePath: string, seedId: string, options?: KbTraceOptions): Promise<KbTraceStep[]>;
|
|
912
1011
|
/** Every record named in one line each. See `catalog.ts`. */
|
|
@@ -1043,6 +1142,8 @@ declare enum ErrorTypes {
|
|
|
1043
1142
|
KbPackBudgetExceeded = "KbPackBudgetExceeded",
|
|
1044
1143
|
KbRecordNotFound = "KbRecordNotFound",
|
|
1045
1144
|
KbSelfVerification = "KbSelfVerification",
|
|
1145
|
+
KbStampBaselineUnreadable = "KbStampBaselineUnreadable",
|
|
1146
|
+
KbStampDigestBaselineAmbiguous = "KbStampDigestBaselineAmbiguous",
|
|
1046
1147
|
KbUnknownLinkRel = "KbUnknownLinkRel",
|
|
1047
1148
|
KbWriteConflict = "KbWriteConflict"
|
|
1048
1149
|
}
|
|
@@ -1727,7 +1828,7 @@ declare function validateBundle(records: KbRecord[]): KbValidationProblem[];
|
|
|
1727
1828
|
declare const DEFAULT_EXPIRING_DAYS = 30;
|
|
1728
1829
|
declare const DEFAULT_UNVERIFIED_DAYS = 90;
|
|
1729
1830
|
declare const DEFAULT_AGING_DAYS = 90;
|
|
1730
|
-
declare const KB_DOCTOR_CHECKS: readonly ["expired", "expiring", "unverified", "aging", "orphaned", "broken-supersession", "superseded-but-cited", "drifted"];
|
|
1831
|
+
declare const KB_DOCTOR_CHECKS: readonly ["expired", "expiring", "unverified", "aging", "orphaned", "broken-supersession", "superseded-but-cited", "drifted", "unchecked"];
|
|
1731
1832
|
type KbDoctorCheck = (typeof KB_DOCTOR_CHECKS)[number];
|
|
1732
1833
|
type KbDoctorFinding = {
|
|
1733
1834
|
conceptId: string;
|
|
@@ -1904,8 +2005,12 @@ type KbCommand<Shape extends z.ZodRawShape = z.ZodRawShape> = {
|
|
|
1904
2005
|
/** Shown to an agent choosing a tool, so it carries the judgment too. */
|
|
1905
2006
|
description: string;
|
|
1906
2007
|
input: z.ZodObject<Shape>;
|
|
1907
|
-
/**
|
|
1908
|
-
|
|
2008
|
+
/**
|
|
2009
|
+
* Positional argv → the same object MCP receives. `bundleExplicit` says
|
|
2010
|
+
* whether `--bundle` was actually passed, for the one command whose meaning
|
|
2011
|
+
* turns on it: `stamp` with no bundle stamps every pinned base.
|
|
2012
|
+
*/
|
|
2013
|
+
fromArgv(argv: string[], bundlePath: string, stdin: () => Promise<string>, bundleExplicit?: boolean): Promise<unknown> | unknown;
|
|
1909
2014
|
run(ctx: KbCommandContext, input: z.infer<z.ZodObject<Shape>>): Promise<unknown>;
|
|
1910
2015
|
/**
|
|
1911
2016
|
* A human-readable form of the result, for the CLI. Where it exists the CLI
|
|
@@ -2071,4 +2176,4 @@ declare function parseMarkdownWithFrontmatter<S extends z.ZodType>(text: string,
|
|
|
2071
2176
|
frontmatter: ReturnType<S["safeParse"]>;
|
|
2072
2177
|
};
|
|
2073
2178
|
|
|
2074
|
-
export { type AnchorResolver, BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposeLink, type ComposedRecord, DECISION_TYPE, DEFAULT_AGING_DAYS, DEFAULT_EXPIRING_DAYS, DEFAULT_LOAD_BUDGET, DEFAULT_PACK_HOPS, DEFAULT_PACK_MAX_NODES, DEFAULT_TYPED_LINK_RELS, DEFAULT_UNVERIFIED_DAYS, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_CAUSAL_LINK_RELS, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_DOCTOR_CHECKS, KB_EDGE_KINDS, KB_LINK_RELS, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, type KbAnchorDriftEntry, type KbBacklink, type KbBacklinksResult, 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, type KbImpactOptions, type KbImpactResult, type KbImpactedRecord, type KbInboundEdge, KbInvalidConceptIdError, type KbLink, type KbLinkEdge, type KbLinkRel, type KbLinkRelSpec, 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, KbUnknownLinkRelError, type KbValidationProblem, type KbValidationSeverity, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LINK_RELS, 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, backlinks, buildContext, catalog, composeDecisionRecord, composeInputSchema, composeLinkSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, detectAnchorDrift, doctor, edgeNeighbours, hashAnchorText, impact, inboundIndex, indexIsStale, isKbLinkRel, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLinkSchema, 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 };
|
|
2179
|
+
export { type AnchorResolver, BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposeLink, type ComposedRecord, DECISION_TYPE, DEFAULT_AGING_DAYS, DEFAULT_EXPIRING_DAYS, DEFAULT_LOAD_BUDGET, DEFAULT_PACK_HOPS, DEFAULT_PACK_MAX_NODES, DEFAULT_TYPED_LINK_RELS, DEFAULT_UNVERIFIED_DAYS, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_CAUSAL_LINK_RELS, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_DOCTOR_CHECKS, KB_EDGE_KINDS, KB_LINK_RELS, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, type KbAnchorDriftEntry, type KbBacklink, type KbBacklinksResult, 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, type KbImpactOptions, type KbImpactResult, type KbImpactedRecord, type KbInboundEdge, KbInvalidConceptIdError, type KbLink, type KbLinkEdge, type KbLinkRel, type KbLinkRelSpec, 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, KbUnknownLinkRelError, type KbValidationProblem, type KbValidationSeverity, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LINK_RELS, LOG_FILE, type MatchOptions, NO_DECISION_SLUG, PINS_FILE, PINS_LOCAL_FILE, PIN_LAYERS, type QmdModule, RECORD_TYPES, type RemoteAnchorState, type RemoteOptions, type RemoteRead, type ResolvedSymbol, SEARCH_INDEX_FILE, type SearchHit, type SearchOptions, type SymbolRange, TRACE_EDGES, adjudicate, anchorFilePath, assertBaseNotFrozen, backlinks, buildContext, catalog, composeDecisionRecord, composeInputSchema, composeLinkSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, detectAnchorDrift, doctor, edgeNeighbours, hashAnchorText, impact, inboundIndex, indexIsStale, isCanonicalRepoUrl, isKbLinkRel, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLinkSchema, kbLogEntrySchema, kbRecordFrontmatterSchema, kbSourceSchema, kbVerifiedEventSchema, listPins, loadQmd, matchToDiff, mergedContextBudgets, neighbours, pack, parseLog, parseMarkdownWithFrontmatter, pinBase, readMergedPins, readPinsLayer, readRemoteAnchors, regexResolver, renderCatalogLine, renderIndex, renderIndexLine, renderLogEntry, repoCacheDir, 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-RINBOAQZ.js";
|
|
4
4
|
import {
|
|
5
5
|
createKbMcpServer,
|
|
6
6
|
runKbMcpServer
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MEZCF646.js";
|
|
8
8
|
import {
|
|
9
9
|
BaseError,
|
|
10
10
|
CONTEXT_BEGIN,
|
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
impact,
|
|
75
75
|
inboundIndex,
|
|
76
76
|
indexIsStale,
|
|
77
|
+
isCanonicalRepoUrl,
|
|
77
78
|
isKbLinkRel,
|
|
78
79
|
isKbRecordType,
|
|
79
80
|
isNoDecisionRecord,
|
|
@@ -96,11 +97,13 @@ import {
|
|
|
96
97
|
pinBase,
|
|
97
98
|
readMergedPins,
|
|
98
99
|
readPinsLayer,
|
|
100
|
+
readRemoteAnchors,
|
|
99
101
|
regexResolver,
|
|
100
102
|
renderCatalogLine,
|
|
101
103
|
renderIndex,
|
|
102
104
|
renderIndexLine,
|
|
103
105
|
renderLogEntry,
|
|
106
|
+
repoCacheDir,
|
|
104
107
|
resolveAnchor,
|
|
105
108
|
resolveHeads,
|
|
106
109
|
resolveHits,
|
|
@@ -114,7 +117,7 @@ import {
|
|
|
114
117
|
trace,
|
|
115
118
|
unpinBase,
|
|
116
119
|
validateBundle
|
|
117
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-H5W53NVU.js";
|
|
118
121
|
|
|
119
122
|
// src/match-diff.ts
|
|
120
123
|
function matchToDiff(files, records, options = {}) {
|
|
@@ -265,6 +268,7 @@ export {
|
|
|
265
268
|
impact,
|
|
266
269
|
inboundIndex,
|
|
267
270
|
indexIsStale,
|
|
271
|
+
isCanonicalRepoUrl,
|
|
268
272
|
isKbLinkRel,
|
|
269
273
|
isKbRecordType,
|
|
270
274
|
isNoDecisionRecord,
|
|
@@ -288,11 +292,13 @@ export {
|
|
|
288
292
|
pinBase,
|
|
289
293
|
readMergedPins,
|
|
290
294
|
readPinsLayer,
|
|
295
|
+
readRemoteAnchors,
|
|
291
296
|
regexResolver,
|
|
292
297
|
renderCatalogLine,
|
|
293
298
|
renderIndex,
|
|
294
299
|
renderIndexLine,
|
|
295
300
|
renderLogEntry,
|
|
301
|
+
repoCacheDir,
|
|
296
302
|
resolveAnchor,
|
|
297
303
|
resolveHeads,
|
|
298
304
|
resolveHits,
|
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":[]}
|