@saasontools/strauss-kb 0.1.18 → 0.1.20

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-IOIUS26S.js";
3
+ } from "./chunk-XDH6J6CH.js";
4
4
  import {
5
5
  createKbMcpServer,
6
6
  runKbMcpServer
7
- } from "./chunk-ROGVYSMV.js";
7
+ } from "./chunk-EXZQZJU2.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,8 @@ import {
97
111
  languageForFile,
98
112
  listPins,
99
113
  loadQmd,
114
+ matchToDiff,
115
+ matchesTags,
100
116
  mergedContextBudgets,
101
117
  neighbours,
102
118
  pack,
@@ -104,6 +120,8 @@ import {
104
120
  parseMarkdownWithFrontmatter,
105
121
  pinBase,
106
122
  prepareResolvers,
123
+ promoteCandidates,
124
+ promoteInputSchema,
107
125
  readMergedPins,
108
126
  readPinsLayer,
109
127
  readRemoteAnchors,
@@ -123,6 +141,7 @@ import {
123
141
  selectDecisions,
124
142
  splitMarkdownFrontmatter,
125
143
  stringifyMarkdownWithFrontmatter,
144
+ symbolRangeIndex,
126
145
  syncInstructions,
127
146
  toHookJson,
128
147
  trace,
@@ -130,87 +149,7 @@ import {
130
149
  unifiedDiff,
131
150
  unpinBase,
132
151
  validateBundle
133
- } from "./chunk-GSOTMWZZ.js";
134
-
135
- // src/match-diff.ts
136
- function matchToDiff(files, records, options = {}) {
137
- const ranges = indexRanges(options.symbolRanges ?? []);
138
- const anchored = records.filter(
139
- (record) => (record.frontmatter.strauss_anchors ?? []).length > 0
140
- );
141
- const matches = [];
142
- for (const file of files) {
143
- const candidates = anchored.map((record) => ({
144
- record,
145
- anchors: (record.frontmatter.strauss_anchors ?? []).filter(
146
- (anchor) => normalize(anchor.file) === normalize(file.filePath)
147
- )
148
- })).filter(({ anchors }) => anchors.length > 0);
149
- if (!candidates.length) continue;
150
- for (const hunk of file.hunks) {
151
- const hits = [];
152
- let precision = "symbol";
153
- for (const { record, anchors } of candidates) {
154
- const placement = place(anchors, file.filePath, hunk, ranges);
155
- if (placement === "miss") continue;
156
- if (placement === "file") precision = "file";
157
- hits.push(record);
158
- }
159
- if (!hits.length) continue;
160
- matches.push({
161
- filePath: file.filePath,
162
- hunk,
163
- records: order(adjudicate(hits, records, options.now)),
164
- precision
165
- });
166
- }
167
- }
168
- return matches;
169
- }
170
- function place(anchors, filePath, hunk, ranges) {
171
- let fallback = "miss";
172
- for (const anchor of anchors) {
173
- if (!anchor.symbol) return "file";
174
- const resolved = ranges.get(key(filePath, anchor.symbol));
175
- if (!resolved?.length) {
176
- fallback = "file";
177
- continue;
178
- }
179
- if (resolved.some((range) => overlaps(range, hunk))) return "symbol";
180
- }
181
- return fallback;
182
- }
183
- function overlaps(range, hunk) {
184
- return range.startLine <= hunk.endLine && hunk.startLine <= range.endLine;
185
- }
186
- function order(records) {
187
- const rank = {
188
- current: 0,
189
- unsettled: 1,
190
- open: 2,
191
- superseded: 3,
192
- rejected: 4
193
- };
194
- return [...records].sort(
195
- (left, right) => (rank[left.standing] ?? 9) - (rank[right.standing] ?? 9) || (left.record.frontmatter.generated?.at ?? "").localeCompare(
196
- right.record.frontmatter.generated?.at ?? ""
197
- )
198
- );
199
- }
200
- function indexRanges(ranges) {
201
- const byKey = /* @__PURE__ */ new Map();
202
- for (const range of ranges) {
203
- const id = key(range.file, range.symbol);
204
- byKey.set(id, [...byKey.get(id) ?? [], range]);
205
- }
206
- return byKey;
207
- }
208
- function key(file, symbol) {
209
- return `${normalize(file)}#${symbol}`;
210
- }
211
- function normalize(path) {
212
- return path.replace(/^\.\//, "");
213
- }
152
+ } from "./chunk-QQLPJO4R.js";
214
153
  export {
215
154
  BaseError,
216
155
  CONTEXT_BEGIN,
@@ -222,12 +161,14 @@ export {
222
161
  DEFAULT_LOAD_BUDGET,
223
162
  DEFAULT_PACK_HOPS,
224
163
  DEFAULT_PACK_MAX_NODES,
164
+ DEFAULT_THRESHOLDS,
225
165
  DEFAULT_TYPED_LINK_RELS,
226
166
  DEFAULT_UNVERIFIED_DAYS,
227
167
  ErrorTypes,
228
168
  Fault,
229
169
  INDEX_FILE,
230
170
  KB_CAUSAL_LINK_RELS,
171
+ KB_CLASSES,
231
172
  KB_COMMANDS,
232
173
  KB_COMMANDS_BY_NAME,
233
174
  KB_CONCEPT_ID_PATTERN,
@@ -241,10 +182,15 @@ export {
241
182
  KB_RECORD_TYPES,
242
183
  KB_SLUG_PATTERN,
243
184
  KbBaseFrozenError,
185
+ KbClassifyInputError,
244
186
  KbInvalidConceptIdError,
245
187
  KbMissingFlagValueError,
246
188
  KbPackBudgetExceededError,
247
189
  KbPinsMalformedError,
190
+ KbPromoteCollisionError,
191
+ KbPromoteSelfError,
192
+ KbPromoteStandingError,
193
+ KbPromoteStoppedError,
248
194
  KbRecordAlreadyExistsError,
249
195
  KbRecordNotFoundError,
250
196
  KbSelfVerificationError,
@@ -257,16 +203,20 @@ export {
257
203
  PINS_FILE,
258
204
  PINS_LOCAL_FILE,
259
205
  PIN_LAYERS,
206
+ PROMOTION_SOURCE_ID,
260
207
  RECORD_TYPES,
261
208
  SEARCH_INDEX_FILE,
262
209
  TRACE_EDGES,
263
210
  TreeSitterResolver,
264
211
  adjudicate,
265
212
  anchorFilePath,
213
+ anchorOnHunk,
266
214
  assertBaseNotFrozen,
267
215
  backlinks,
268
216
  buildContext,
217
+ carry,
269
218
  catalog,
219
+ classifyDiff,
270
220
  classifyDrift,
271
221
  composeDecisionRecord,
272
222
  composeInputSchema,
@@ -292,8 +242,11 @@ export {
292
242
  isKbLinkRel,
293
243
  isKbRecordType,
294
244
  isNoDecisionRecord,
245
+ isReviewTag,
295
246
  kbActorStampSchema,
296
247
  kbAnchorSchema,
248
+ kbAnchorSpanSchema,
249
+ kbAnchorWriteSchema,
297
250
  kbConceptIdSchema,
298
251
  kbJsonSchemas,
299
252
  kbLinkSchema,
@@ -305,6 +258,7 @@ export {
305
258
  listPins,
306
259
  loadQmd,
307
260
  matchToDiff,
261
+ matchesTags,
308
262
  mergedContextBudgets,
309
263
  neighbours,
310
264
  pack,
@@ -312,6 +266,8 @@ export {
312
266
  parseMarkdownWithFrontmatter,
313
267
  pinBase,
314
268
  prepareResolvers,
269
+ promoteCandidates,
270
+ promoteInputSchema,
315
271
  readMergedPins,
316
272
  readPinsLayer,
317
273
  readRemoteAnchors,
@@ -333,6 +289,7 @@ export {
333
289
  selectDecisions,
334
290
  splitMarkdownFrontmatter,
335
291
  stringifyMarkdownWithFrontmatter,
292
+ symbolRangeIndex,
336
293
  syncInstructions,
337
294
  toHookJson,
338
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":[]}