@saasontools/strauss-kb 0.1.19 → 0.1.21

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.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  runKbCli
3
- } from "./chunk-CQBLH7CE.js";
3
+ } from "./chunk-VXWN62HT.js";
4
4
  import {
5
5
  createKbMcpServer,
6
6
  runKbMcpServer
7
- } from "./chunk-ZWSCLHG6.js";
7
+ } from "./chunk-WVJULVGY.js";
8
8
  import {
9
9
  BaseError,
10
10
  CONTEXT_BEGIN,
@@ -16,12 +16,14 @@ import {
16
16
  DEFAULT_LOAD_BUDGET,
17
17
  DEFAULT_PACK_HOPS,
18
18
  DEFAULT_PACK_MAX_NODES,
19
+ DEFAULT_THRESHOLDS,
19
20
  DEFAULT_TYPED_LINK_RELS,
20
21
  DEFAULT_UNVERIFIED_DAYS,
21
22
  ErrorTypes,
22
23
  Fault,
23
24
  INDEX_FILE,
24
25
  KB_CAUSAL_LINK_RELS,
26
+ KB_CLASSES,
25
27
  KB_COMMANDS,
26
28
  KB_COMMANDS_BY_NAME,
27
29
  KB_CONCEPT_ID_PATTERN,
@@ -35,10 +37,15 @@ import {
35
37
  KB_RECORD_TYPES,
36
38
  KB_SLUG_PATTERN,
37
39
  KbBaseFrozenError,
40
+ KbClassifyInputError,
38
41
  KbInvalidConceptIdError,
39
42
  KbMissingFlagValueError,
40
43
  KbPackBudgetExceededError,
41
44
  KbPinsMalformedError,
45
+ KbPromoteCollisionError,
46
+ KbPromoteSelfError,
47
+ KbPromoteStandingError,
48
+ KbPromoteStoppedError,
42
49
  KbRecordAlreadyExistsError,
43
50
  KbRecordNotFoundError,
44
51
  KbSelfVerificationError,
@@ -51,16 +58,20 @@ import {
51
58
  PINS_FILE,
52
59
  PINS_LOCAL_FILE,
53
60
  PIN_LAYERS,
61
+ PROMOTION_SOURCE_ID,
54
62
  RECORD_TYPES,
55
63
  SEARCH_INDEX_FILE,
56
64
  TRACE_EDGES,
57
65
  TreeSitterResolver,
58
66
  adjudicate,
59
67
  anchorFilePath,
68
+ anchorOnHunk,
60
69
  assertBaseNotFrozen,
61
70
  backlinks,
62
71
  buildContext,
72
+ carry,
63
73
  catalog,
74
+ classifyDiff,
64
75
  classifyDrift,
65
76
  composeDecisionRecord,
66
77
  composeInputSchema,
@@ -85,8 +96,11 @@ import {
85
96
  isKbLinkRel,
86
97
  isKbRecordType,
87
98
  isNoDecisionRecord,
99
+ isReviewTag,
88
100
  kbActorStampSchema,
89
101
  kbAnchorSchema,
102
+ kbAnchorSpanSchema,
103
+ kbAnchorWriteSchema,
90
104
  kbConceptIdSchema,
91
105
  kbJsonSchemas,
92
106
  kbLinkSchema,
@@ -97,6 +111,7 @@ import {
97
111
  languageForFile,
98
112
  listPins,
99
113
  loadQmd,
114
+ matchToDiff,
100
115
  matchesTags,
101
116
  mergedContextBudgets,
102
117
  neighbours,
@@ -105,6 +120,8 @@ import {
105
120
  parseMarkdownWithFrontmatter,
106
121
  pinBase,
107
122
  prepareResolvers,
123
+ promoteCandidates,
124
+ promoteInputSchema,
108
125
  readMergedPins,
109
126
  readPinsLayer,
110
127
  readRemoteAnchors,
@@ -124,6 +141,7 @@ import {
124
141
  selectDecisions,
125
142
  splitMarkdownFrontmatter,
126
143
  stringifyMarkdownWithFrontmatter,
144
+ symbolRangeIndex,
127
145
  syncInstructions,
128
146
  toHookJson,
129
147
  trace,
@@ -131,87 +149,7 @@ import {
131
149
  unifiedDiff,
132
150
  unpinBase,
133
151
  validateBundle
134
- } from "./chunk-MNQNHYWL.js";
135
-
136
- // src/match-diff.ts
137
- function matchToDiff(files, records, options = {}) {
138
- const ranges = indexRanges(options.symbolRanges ?? []);
139
- const anchored = records.filter(
140
- (record) => (record.frontmatter.strauss_anchors ?? []).length > 0
141
- );
142
- const matches = [];
143
- for (const file of files) {
144
- const candidates = anchored.map((record) => ({
145
- record,
146
- anchors: (record.frontmatter.strauss_anchors ?? []).filter(
147
- (anchor) => normalize(anchor.file) === normalize(file.filePath)
148
- )
149
- })).filter(({ anchors }) => anchors.length > 0);
150
- if (!candidates.length) continue;
151
- for (const hunk of file.hunks) {
152
- const hits = [];
153
- let precision = "symbol";
154
- for (const { record, anchors } of candidates) {
155
- const placement = place(anchors, file.filePath, hunk, ranges);
156
- if (placement === "miss") continue;
157
- if (placement === "file") precision = "file";
158
- hits.push(record);
159
- }
160
- if (!hits.length) continue;
161
- matches.push({
162
- filePath: file.filePath,
163
- hunk,
164
- records: order(adjudicate(hits, records, options.now)),
165
- precision
166
- });
167
- }
168
- }
169
- return matches;
170
- }
171
- function place(anchors, filePath, hunk, ranges) {
172
- let fallback = "miss";
173
- for (const anchor of anchors) {
174
- if (!anchor.symbol) return "file";
175
- const resolved = ranges.get(key(filePath, anchor.symbol));
176
- if (!resolved?.length) {
177
- fallback = "file";
178
- continue;
179
- }
180
- if (resolved.some((range) => overlaps(range, hunk))) return "symbol";
181
- }
182
- return fallback;
183
- }
184
- function overlaps(range, hunk) {
185
- return range.startLine <= hunk.endLine && hunk.startLine <= range.endLine;
186
- }
187
- function order(records) {
188
- const rank = {
189
- current: 0,
190
- unsettled: 1,
191
- open: 2,
192
- superseded: 3,
193
- rejected: 4
194
- };
195
- return [...records].sort(
196
- (left, right) => (rank[left.standing] ?? 9) - (rank[right.standing] ?? 9) || (left.record.frontmatter.generated?.at ?? "").localeCompare(
197
- right.record.frontmatter.generated?.at ?? ""
198
- )
199
- );
200
- }
201
- function indexRanges(ranges) {
202
- const byKey = /* @__PURE__ */ new Map();
203
- for (const range of ranges) {
204
- const id = key(range.file, range.symbol);
205
- byKey.set(id, [...byKey.get(id) ?? [], range]);
206
- }
207
- return byKey;
208
- }
209
- function key(file, symbol) {
210
- return `${normalize(file)}#${symbol}`;
211
- }
212
- function normalize(path) {
213
- return path.replace(/^\.\//, "");
214
- }
152
+ } from "./chunk-XBZ7G5R5.js";
215
153
  export {
216
154
  BaseError,
217
155
  CONTEXT_BEGIN,
@@ -223,12 +161,14 @@ export {
223
161
  DEFAULT_LOAD_BUDGET,
224
162
  DEFAULT_PACK_HOPS,
225
163
  DEFAULT_PACK_MAX_NODES,
164
+ DEFAULT_THRESHOLDS,
226
165
  DEFAULT_TYPED_LINK_RELS,
227
166
  DEFAULT_UNVERIFIED_DAYS,
228
167
  ErrorTypes,
229
168
  Fault,
230
169
  INDEX_FILE,
231
170
  KB_CAUSAL_LINK_RELS,
171
+ KB_CLASSES,
232
172
  KB_COMMANDS,
233
173
  KB_COMMANDS_BY_NAME,
234
174
  KB_CONCEPT_ID_PATTERN,
@@ -242,10 +182,15 @@ export {
242
182
  KB_RECORD_TYPES,
243
183
  KB_SLUG_PATTERN,
244
184
  KbBaseFrozenError,
185
+ KbClassifyInputError,
245
186
  KbInvalidConceptIdError,
246
187
  KbMissingFlagValueError,
247
188
  KbPackBudgetExceededError,
248
189
  KbPinsMalformedError,
190
+ KbPromoteCollisionError,
191
+ KbPromoteSelfError,
192
+ KbPromoteStandingError,
193
+ KbPromoteStoppedError,
249
194
  KbRecordAlreadyExistsError,
250
195
  KbRecordNotFoundError,
251
196
  KbSelfVerificationError,
@@ -258,16 +203,20 @@ export {
258
203
  PINS_FILE,
259
204
  PINS_LOCAL_FILE,
260
205
  PIN_LAYERS,
206
+ PROMOTION_SOURCE_ID,
261
207
  RECORD_TYPES,
262
208
  SEARCH_INDEX_FILE,
263
209
  TRACE_EDGES,
264
210
  TreeSitterResolver,
265
211
  adjudicate,
266
212
  anchorFilePath,
213
+ anchorOnHunk,
267
214
  assertBaseNotFrozen,
268
215
  backlinks,
269
216
  buildContext,
217
+ carry,
270
218
  catalog,
219
+ classifyDiff,
271
220
  classifyDrift,
272
221
  composeDecisionRecord,
273
222
  composeInputSchema,
@@ -293,8 +242,11 @@ export {
293
242
  isKbLinkRel,
294
243
  isKbRecordType,
295
244
  isNoDecisionRecord,
245
+ isReviewTag,
296
246
  kbActorStampSchema,
297
247
  kbAnchorSchema,
248
+ kbAnchorSpanSchema,
249
+ kbAnchorWriteSchema,
298
250
  kbConceptIdSchema,
299
251
  kbJsonSchemas,
300
252
  kbLinkSchema,
@@ -314,6 +266,8 @@ export {
314
266
  parseMarkdownWithFrontmatter,
315
267
  pinBase,
316
268
  prepareResolvers,
269
+ promoteCandidates,
270
+ promoteInputSchema,
317
271
  readMergedPins,
318
272
  readPinsLayer,
319
273
  readRemoteAnchors,
@@ -335,6 +289,7 @@ export {
335
289
  selectDecisions,
336
290
  splitMarkdownFrontmatter,
337
291
  stringifyMarkdownWithFrontmatter,
292
+ symbolRangeIndex,
338
293
  syncInstructions,
339
294
  toHookJson,
340
295
  trace,
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":[],"sourcesContent":[],"mappings":"","names":[]}