@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/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
- * Resolves symbolic anchors to text and detects drift against stored hashes.
194
+ * The vocabulary anchor resolution is reported in.
192
195
  *
193
- * Resolvers are pure source string in, range out; only the file readers touch
194
- * disk. Any shape the lexer cannot handle confidently returns `null` and is
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
- * The anchor names a repository this root is not. Expected rather than
211
- * wrong a base describing several repositories resolves against one tree
212
- * at a time — so it is never a drift finding and never reaches a reader as
213
- * a warning. Multi-root resolution is SAA-709.
214
- */
215
- | "foreign-repo";
216
- /**
217
- * v1 heuristic resolver. A dotted symbol like `OrderService.cancel` matches on
218
- * its last segment, with the parent used to scope the search: a candidate
219
- * counts only if the parent name appears in the fifty lines above it, when any
220
- * candidate satisfies that at all.
221
- *
222
- * Deterministic, and ambiguity is not resolved by guessing two lines of
223
- * equally good shape mean the resolver cannot tell which one the record meant,
224
- * and it says so by returning `null`.
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
- * Re-resolves every hash-carrying anchor and compares against the stored hash.
269
- *
270
- * Anchors without a `hash` are skipped; one naming another repository is
271
- * reported `foreign-repo` and never read, and `origin` is asked for once per
272
- * run, only when some anchor declares a `repo`. A missing file or unresolvable
273
- * symbol is a finding (`unresolved`), never a throw. Each distinct file is read
274
- * once per run; all checked entries are returned per record, callers filter.
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
- * Three phases: collect the checkable anchors, read their distinct files with
277
- * a bounded pool, then resolve and hash in record order — so the output does
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 detectAnchorDrift(records: KbRecord[], options?: {
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
- }): Promise<Map<string, KbAnchorDriftEntry[]>>;
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
- file: string;
339
- symbol?: string;
340
- /** `null` when the anchor recorded no line count size unknown. */
341
- diffSize: number | null;
342
- reason?: string;
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): Promise<Map<string, KbAnchorDriftEntry[]> | undefined>;
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
- /** Positional argv → the same object MCP receives. */
1908
- fromArgv(argv: string[], bundlePath: string, stdin: () => Promise<string>): Promise<unknown> | unknown;
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-MBXNCZ4V.js";
3
+ } from "./chunk-RINBOAQZ.js";
4
4
  import {
5
5
  createKbMcpServer,
6
6
  runKbMcpServer
7
- } from "./chunk-PYA5E7FL.js";
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-43KALLFU.js";
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":"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","names":[]}
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":[]}