@getfoyer/review-core 0.2.0 → 0.2.1
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/fileReader.d.ts +17 -0
- package/dist/fileReader.d.ts.map +1 -0
- package/dist/fileReader.js +2 -0
- package/dist/fileReader.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/semantic/cache.d.ts +31 -0
- package/dist/semantic/cache.d.ts.map +1 -0
- package/dist/semantic/cache.js +50 -0
- package/dist/semantic/cache.js.map +1 -0
- package/dist/semantic/contextBuilder.d.ts +17 -0
- package/dist/semantic/contextBuilder.d.ts.map +1 -0
- package/dist/semantic/contextBuilder.js +92 -0
- package/dist/semantic/contextBuilder.js.map +1 -0
- package/dist/semantic/diffParse.d.ts +30 -0
- package/dist/semantic/diffParse.d.ts.map +1 -0
- package/dist/semantic/diffParse.js +48 -0
- package/dist/semantic/diffParse.js.map +1 -0
- package/dist/semantic/imports.d.ts +16 -0
- package/dist/semantic/imports.d.ts.map +1 -0
- package/dist/semantic/imports.js +102 -0
- package/dist/semantic/imports.js.map +1 -0
- package/dist/semantic/index.d.ts +45 -0
- package/dist/semantic/index.d.ts.map +1 -0
- package/dist/semantic/index.js +78 -0
- package/dist/semantic/index.js.map +1 -0
- package/dist/semantic/languages.d.ts +14 -0
- package/dist/semantic/languages.d.ts.map +1 -0
- package/dist/semantic/languages.js +40 -0
- package/dist/semantic/languages.js.map +1 -0
- package/dist/semantic/parser.d.ts +15 -0
- package/dist/semantic/parser.d.ts.map +1 -0
- package/dist/semantic/parser.js +83 -0
- package/dist/semantic/parser.js.map +1 -0
- package/dist/semantic/symbols.d.ts +54 -0
- package/dist/semantic/symbols.d.ts.map +1 -0
- package/dist/semantic/symbols.js +287 -0
- package/dist/semantic/symbols.js.map +1 -0
- package/dist/semantic/types.d.ts +43 -0
- package/dist/semantic/types.d.ts.map +1 -0
- package/dist/semantic/types.js +2 -0
- package/dist/semantic/types.js.map +1 -0
- package/package.json +13 -5
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileReader port — the seam between "where do source files come from?"
|
|
3
|
+
* and the semantic-context builder / repair-instruction renderer.
|
|
4
|
+
*
|
|
5
|
+
* Two real implementations live outside this package:
|
|
6
|
+
* - api/ ships an Octokit-backed reader that fetches by SHA from GitHub
|
|
7
|
+
* (used during PR verification, where no workspace exists yet).
|
|
8
|
+
* - packages/cli ships a disk-backed reader that reads from the working
|
|
9
|
+
* tree (used during pre-PR `foyer review`).
|
|
10
|
+
*
|
|
11
|
+
* Both share the same contract: returns the file's UTF-8 contents or
|
|
12
|
+
* null. Never throws — callers degrade silently.
|
|
13
|
+
*/
|
|
14
|
+
export interface FileReader {
|
|
15
|
+
read(path: string): Promise<string | null>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=fileReader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileReader.d.ts","sourceRoot":"","sources":["../src/fileReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileReader.js","sourceRoot":"","sources":["../src/fileReader.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,7 @@
|
|
|
6
6
|
* without specifying the subpath.
|
|
7
7
|
*/
|
|
8
8
|
export * from './types.js';
|
|
9
|
+
export type { FileReader } from './fileReader.js';
|
|
10
|
+
export { buildSemanticContext, buildSemanticContextCached, getSharedSemanticBlockCache, _resetSharedCacheForTests, } from './semantic/index.js';
|
|
11
|
+
export type { BuildSemanticContextOptions, SemanticBlock, TouchedSymbol, ImportedSymbol, SemanticBlockCache, } from './semantic/index.js';
|
|
9
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,2BAA2B,EAC3B,aAAa,EACb,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,YAAY,CAAC;AAE3B,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-local LRU cache for rendered semantic blocks.
|
|
3
|
+
*
|
|
4
|
+
* Same PR head SHA → same touched files → same `<semantic_context>` block.
|
|
5
|
+
* `code_review` and `intent_fidelity` both want it; cache lets us build once.
|
|
6
|
+
*
|
|
7
|
+
* Out of scope: cross-process / cross-deploy persistence. Railway redeploys
|
|
8
|
+
* flush the map and the next request rebuilds on demand. The cache is a hot-
|
|
9
|
+
* path optimization, not a correctness primitive — every miss is harmless.
|
|
10
|
+
*/
|
|
11
|
+
import type { SemanticBlock } from './types.js';
|
|
12
|
+
export interface SemanticBlockCache {
|
|
13
|
+
get(key: string): SemanticBlock | undefined;
|
|
14
|
+
set(key: string, block: SemanticBlock): void;
|
|
15
|
+
/** For tests only — drops every entry. */
|
|
16
|
+
clear(): void;
|
|
17
|
+
/** For metrics. */
|
|
18
|
+
stats(): {
|
|
19
|
+
hits: number;
|
|
20
|
+
misses: number;
|
|
21
|
+
size: number;
|
|
22
|
+
capacity: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare function createSemanticBlockCache(capacity?: number): SemanticBlockCache;
|
|
26
|
+
/** Canonical key. `repoFullName` is `owner/name`; head SHA is the post-image. */
|
|
27
|
+
export declare function cacheKey(repoFullName: string, headSha: string): string;
|
|
28
|
+
export declare function getSharedSemanticBlockCache(): SemanticBlockCache;
|
|
29
|
+
/** Test seam. */
|
|
30
|
+
export declare function _resetSharedCacheForTests(): void;
|
|
31
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/semantic/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAC5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7C,0CAA0C;IAC1C,KAAK,IAAI,IAAI,CAAC;IACd,mBAAmB;IACnB,KAAK,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,GAAE,MAAyB,GAAG,kBAAkB,CA4BhG;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtE;AAID,wBAAgB,2BAA2B,IAAI,kBAAkB,CAGhE;AAED,iBAAiB;AACjB,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const DEFAULT_CAPACITY = 64;
|
|
2
|
+
export function createSemanticBlockCache(capacity = DEFAULT_CAPACITY) {
|
|
3
|
+
// Map preserves insertion order; deleting and re-setting on read pushes a
|
|
4
|
+
// key to the most-recently-used end. Evict from the iterator's first entry
|
|
5
|
+
// when over capacity. Standard LRU dance, no extra deps.
|
|
6
|
+
const map = new Map();
|
|
7
|
+
let hits = 0;
|
|
8
|
+
let misses = 0;
|
|
9
|
+
return {
|
|
10
|
+
get(key) {
|
|
11
|
+
const v = map.get(key);
|
|
12
|
+
if (v === undefined) {
|
|
13
|
+
misses += 1;
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
map.delete(key);
|
|
17
|
+
map.set(key, v);
|
|
18
|
+
hits += 1;
|
|
19
|
+
return v;
|
|
20
|
+
},
|
|
21
|
+
set(key, block) {
|
|
22
|
+
if (map.has(key))
|
|
23
|
+
map.delete(key);
|
|
24
|
+
map.set(key, block);
|
|
25
|
+
if (map.size > capacity) {
|
|
26
|
+
const oldest = map.keys().next().value;
|
|
27
|
+
if (oldest !== undefined)
|
|
28
|
+
map.delete(oldest);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
clear() { map.clear(); hits = 0; misses = 0; },
|
|
32
|
+
stats() { return { hits, misses, size: map.size, capacity }; },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Canonical key. `repoFullName` is `owner/name`; head SHA is the post-image. */
|
|
36
|
+
export function cacheKey(repoFullName, headSha) {
|
|
37
|
+
return `${repoFullName}@${headSha}`;
|
|
38
|
+
}
|
|
39
|
+
/** Process singleton — one cache for the whole verification worker. */
|
|
40
|
+
let _singleton = null;
|
|
41
|
+
export function getSharedSemanticBlockCache() {
|
|
42
|
+
if (!_singleton)
|
|
43
|
+
_singleton = createSemanticBlockCache();
|
|
44
|
+
return _singleton;
|
|
45
|
+
}
|
|
46
|
+
/** Test seam. */
|
|
47
|
+
export function _resetSharedCacheForTests() {
|
|
48
|
+
_singleton = null;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/semantic/cache.ts"],"names":[],"mappings":"AAYA,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAW5B,MAAM,UAAU,wBAAwB,CAAC,WAAmB,gBAAgB;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,yDAAyD;IACzD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC7C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,OAAO;QACL,GAAG,CAAC,GAAG;YACL,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;gBAAC,MAAM,IAAI,CAAC,CAAC;gBAAC,OAAO,SAAS,CAAC;YAAC,CAAC;YACvD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAChB,IAAI,IAAI,CAAC,CAAC;YACV,OAAO,CAAC,CAAC;QACX,CAAC;QACD,GAAG,CAAC,GAAG,EAAE,KAAK;YACZ,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACpB,IAAI,GAAG,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAC;gBACxB,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;gBACvC,IAAI,MAAM,KAAK,SAAS;oBAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QACD,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9C,KAAK,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;KAC/D,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,QAAQ,CAAC,YAAoB,EAAE,OAAe;IAC5D,OAAO,GAAG,YAAY,IAAI,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,uEAAuE;AACvE,IAAI,UAAU,GAA8B,IAAI,CAAC;AACjD,MAAM,UAAU,2BAA2B;IACzC,IAAI,CAAC,UAAU;QAAE,UAAU,GAAG,wBAAwB,EAAE,CAAC;IACzD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,yBAAyB;IACvC,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the touched-symbol + imported-symbol data into the markdown block
|
|
3
|
+
* that ships inside the LLM user message.
|
|
4
|
+
*
|
|
5
|
+
* Hard cap: `maxBytes` (default 30KB) on the final rendered string. We drop
|
|
6
|
+
* the lowest-priority items first (imported symbols before touched symbols;
|
|
7
|
+
* trailing items before earlier ones) and emit a truncation footer when we
|
|
8
|
+
* had to drop anything.
|
|
9
|
+
*/
|
|
10
|
+
import type { ImportedSymbol, SemanticBlock, TouchedSymbol } from './types.js';
|
|
11
|
+
export interface BuildContextInput {
|
|
12
|
+
touched: TouchedSymbol[];
|
|
13
|
+
imported: ImportedSymbol[];
|
|
14
|
+
maxBytes: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function renderSemanticBlock(input: BuildContextInput): SemanticBlock;
|
|
17
|
+
//# sourceMappingURL=contextBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextBuilder.d.ts","sourceRoot":"","sources":["../../src/semantic/contextBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,CAyD3E"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export function renderSemanticBlock(input) {
|
|
2
|
+
const { touched, imported, maxBytes } = input;
|
|
3
|
+
if (touched.length === 0 && imported.length === 0) {
|
|
4
|
+
return { rendered: '', stats: emptyStats() };
|
|
5
|
+
}
|
|
6
|
+
// Priority order: touched symbols first (most useful), then imports.
|
|
7
|
+
// Inside touched, preserve the order we collected (file order).
|
|
8
|
+
const renderedTouched = touched.map(renderTouchedSymbol);
|
|
9
|
+
const renderedImported = imported.map(renderImportedSymbol);
|
|
10
|
+
let droppedTouched = 0;
|
|
11
|
+
let droppedImported = 0;
|
|
12
|
+
let body = composeBody(renderedTouched, renderedImported);
|
|
13
|
+
let block = wrapBlock(body, droppedTouched, droppedImported);
|
|
14
|
+
// Trim imports first, then touched-symbol entries from the tail.
|
|
15
|
+
while (block.length > maxBytes && renderedImported.length > 0) {
|
|
16
|
+
renderedImported.pop();
|
|
17
|
+
droppedImported += 1;
|
|
18
|
+
body = composeBody(renderedTouched, renderedImported);
|
|
19
|
+
block = wrapBlock(body, droppedTouched, droppedImported);
|
|
20
|
+
}
|
|
21
|
+
while (block.length > maxBytes && renderedTouched.length > 1) {
|
|
22
|
+
renderedTouched.pop();
|
|
23
|
+
droppedTouched += 1;
|
|
24
|
+
body = composeBody(renderedTouched, renderedImported);
|
|
25
|
+
block = wrapBlock(body, droppedTouched, droppedImported);
|
|
26
|
+
}
|
|
27
|
+
// Last-resort hard slice if a single symbol body still exceeds the cap.
|
|
28
|
+
// The block remains well-formed (tag stays closed) so the LLM doesn't see
|
|
29
|
+
// a dangling fence.
|
|
30
|
+
if (block.length > maxBytes) {
|
|
31
|
+
const sliced = block.slice(0, Math.max(0, maxBytes - CLOSE_TAG.length)) + CLOSE_TAG;
|
|
32
|
+
return {
|
|
33
|
+
rendered: sliced,
|
|
34
|
+
stats: {
|
|
35
|
+
touchedFiles: countFiles(touched),
|
|
36
|
+
touchedSymbols: touched.length - droppedTouched,
|
|
37
|
+
importedSymbols: imported.length - droppedImported,
|
|
38
|
+
bytes: sliced.length,
|
|
39
|
+
truncated: true,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
rendered: block,
|
|
45
|
+
stats: {
|
|
46
|
+
touchedFiles: countFiles(touched),
|
|
47
|
+
touchedSymbols: touched.length - droppedTouched,
|
|
48
|
+
importedSymbols: imported.length - droppedImported,
|
|
49
|
+
bytes: block.length,
|
|
50
|
+
truncated: droppedTouched > 0 || droppedImported > 0,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Internal renderers
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
const OPEN_TAG = '<semantic_context>';
|
|
58
|
+
const CLOSE_TAG = '</semantic_context>';
|
|
59
|
+
function emptyStats() {
|
|
60
|
+
return { touchedFiles: 0, touchedSymbols: 0, importedSymbols: 0, bytes: 0, truncated: false };
|
|
61
|
+
}
|
|
62
|
+
function countFiles(touched) {
|
|
63
|
+
return new Set(touched.map((s) => s.file)).size;
|
|
64
|
+
}
|
|
65
|
+
function renderTouchedSymbol(sym) {
|
|
66
|
+
return `### ${sym.kind} \`${sym.name}\` — ${sym.file}:${sym.line}\n\`\`\`\n${sym.body}\n\`\`\``;
|
|
67
|
+
}
|
|
68
|
+
function renderImportedSymbol(sym) {
|
|
69
|
+
return `- \`${sym.name}\` from \`${sym.fromFile}\` — \`${sym.signature}\``;
|
|
70
|
+
}
|
|
71
|
+
function composeBody(touched, imported) {
|
|
72
|
+
const parts = [];
|
|
73
|
+
if (touched.length > 0) {
|
|
74
|
+
parts.push('## Touched symbols (full definitions, not just diff hunks)');
|
|
75
|
+
parts.push(touched.join('\n\n'));
|
|
76
|
+
}
|
|
77
|
+
if (imported.length > 0) {
|
|
78
|
+
parts.push('## Imported symbols referenced by touched files (1-hop)');
|
|
79
|
+
parts.push(imported.join('\n'));
|
|
80
|
+
}
|
|
81
|
+
return parts.join('\n\n');
|
|
82
|
+
}
|
|
83
|
+
function wrapBlock(body, droppedTouched, droppedImported) {
|
|
84
|
+
const footerParts = [];
|
|
85
|
+
if (droppedTouched > 0)
|
|
86
|
+
footerParts.push(`${droppedTouched} touched symbol(s) omitted`);
|
|
87
|
+
if (droppedImported > 0)
|
|
88
|
+
footerParts.push(`${droppedImported} imported signature(s) omitted`);
|
|
89
|
+
const footer = footerParts.length > 0 ? `\n\n[truncated: ${footerParts.join(', ')}]` : '';
|
|
90
|
+
return `${OPEN_TAG}\n${body}${footer}\n${CLOSE_TAG}`;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=contextBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextBuilder.js","sourceRoot":"","sources":["../../src/semantic/contextBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal unified-diff parser.
|
|
3
|
+
*
|
|
4
|
+
* Extracts, per touched file, the line ranges in the post-image that the diff
|
|
5
|
+
* actually changes (added or context-surrounded). Pure data — no AST work
|
|
6
|
+
* here. The downstream module maps those line ranges onto symbols.
|
|
7
|
+
*
|
|
8
|
+
* Why not a library: unified diff is small enough that a focused parser
|
|
9
|
+
* (5KB, no deps) beats pulling in `parse-diff` for one consumer.
|
|
10
|
+
*/
|
|
11
|
+
export interface DiffHunk {
|
|
12
|
+
/** 1-based starting line in the post-image (the `+` side). */
|
|
13
|
+
newStart: number;
|
|
14
|
+
/** Number of post-image lines covered by this hunk. */
|
|
15
|
+
newLines: number;
|
|
16
|
+
}
|
|
17
|
+
export interface DiffFile {
|
|
18
|
+
/** Repo-relative path from the `+++ b/<path>` header. `null` for /dev/null
|
|
19
|
+
* (file deleted) — callers should skip these. */
|
|
20
|
+
newPath: string | null;
|
|
21
|
+
hunks: DiffHunk[];
|
|
22
|
+
}
|
|
23
|
+
export declare function parseUnifiedDiff(diff: string): DiffFile[];
|
|
24
|
+
/** Touched files in the post-image. Drops deletions (no `+++ b/` path) and
|
|
25
|
+
* files with zero hunks (rename headers, mode changes). */
|
|
26
|
+
export declare function touchedFiles(diff: string): {
|
|
27
|
+
path: string;
|
|
28
|
+
hunks: DiffHunk[];
|
|
29
|
+
}[];
|
|
30
|
+
//# sourceMappingURL=diffParse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diffParse.d.ts","sourceRoot":"","sources":["../../src/semantic/diffParse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,QAAQ;IACvB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB;sDACkD;IAClD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CA6BzD;AAED;4DAC4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,EAAE,CAIhF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal unified-diff parser.
|
|
3
|
+
*
|
|
4
|
+
* Extracts, per touched file, the line ranges in the post-image that the diff
|
|
5
|
+
* actually changes (added or context-surrounded). Pure data — no AST work
|
|
6
|
+
* here. The downstream module maps those line ranges onto symbols.
|
|
7
|
+
*
|
|
8
|
+
* Why not a library: unified diff is small enough that a focused parser
|
|
9
|
+
* (5KB, no deps) beats pulling in `parse-diff` for one consumer.
|
|
10
|
+
*/
|
|
11
|
+
export function parseUnifiedDiff(diff) {
|
|
12
|
+
const lines = diff.split('\n');
|
|
13
|
+
const files = [];
|
|
14
|
+
let current = null;
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
if (line.startsWith('+++ ')) {
|
|
17
|
+
// `+++ b/path` or `+++ /dev/null`. Strip the `b/` prefix when present.
|
|
18
|
+
const raw = line.slice(4).trim();
|
|
19
|
+
const newPath = raw === '/dev/null' ? null : raw.replace(/^b\//, '');
|
|
20
|
+
current = { newPath, hunks: [] };
|
|
21
|
+
files.push(current);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (!current)
|
|
25
|
+
continue;
|
|
26
|
+
if (line.startsWith('@@')) {
|
|
27
|
+
// Form: `@@ -<oldStart>,<oldLines> +<newStart>,<newLines> @@ ...`.
|
|
28
|
+
// Defensive: line counts default to 1 when omitted, per spec.
|
|
29
|
+
const m = /\+(\d+)(?:,(\d+))?/.exec(line);
|
|
30
|
+
if (!m)
|
|
31
|
+
continue;
|
|
32
|
+
const newStart = parseInt(m[1], 10);
|
|
33
|
+
const newLines = m[2] ? parseInt(m[2], 10) : 1;
|
|
34
|
+
if (Number.isFinite(newStart) && Number.isFinite(newLines)) {
|
|
35
|
+
current.hunks.push({ newStart, newLines });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return files;
|
|
40
|
+
}
|
|
41
|
+
/** Touched files in the post-image. Drops deletions (no `+++ b/` path) and
|
|
42
|
+
* files with zero hunks (rename headers, mode changes). */
|
|
43
|
+
export function touchedFiles(diff) {
|
|
44
|
+
return parseUnifiedDiff(diff)
|
|
45
|
+
.filter((f) => f.newPath !== null && f.hunks.length > 0)
|
|
46
|
+
.map((f) => ({ path: f.newPath, hunks: f.hunks }));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=diffParse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diffParse.js","sourceRoot":"","sources":["../../src/semantic/diffParse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgBH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,IAAI,OAAO,GAAoB,IAAI,CAAC;IAEpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,uEAAuE;YACvE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACrE,OAAO,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,mEAAmE;YACnE,8DAA8D;YAC9D,MAAM,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpC,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3D,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;4DAC4D;AAC5D,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,gBAAgB,CAAC,IAAI,CAAC;SAC1B,MAAM,CAAC,CAAC,CAAC,EAAuC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;SAC5F,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FileReader } from '../fileReader.js';
|
|
2
|
+
import type { ImportedSymbol } from './types.js';
|
|
3
|
+
export interface ImportResolutionInput {
|
|
4
|
+
/** The importing file (a touched file). */
|
|
5
|
+
fromFile: string;
|
|
6
|
+
/** Edges produced by `findImports` for that file. */
|
|
7
|
+
edges: {
|
|
8
|
+
source: string;
|
|
9
|
+
names: string[];
|
|
10
|
+
}[];
|
|
11
|
+
reader: FileReader;
|
|
12
|
+
/** Soft cap on total imported-symbol entries collected across all edges. */
|
|
13
|
+
cap: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function resolveImports(input: ImportResolutionInput): Promise<ImportedSymbol[]>;
|
|
16
|
+
//# sourceMappingURL=imports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../src/semantic/imports.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA4CjD,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAC7C,MAAM,EAAE,UAAU,CAAC;IACnB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA6B5F"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import-edge resolution.
|
|
3
|
+
*
|
|
4
|
+
* Given an import like `import { foo, bar } from './utils'` in
|
|
5
|
+
* `src/services/billing.ts`, resolve `./utils` to a real repo path, fetch
|
|
6
|
+
* that file via the FileReader, parse it, and extract the signatures of
|
|
7
|
+
* `foo` and `bar`.
|
|
8
|
+
*
|
|
9
|
+
* Resolution rules (intentionally cheap):
|
|
10
|
+
* - Relative paths only (`.` or `..`). Bare-module specifiers (`react`,
|
|
11
|
+
* `lodash`) are skipped — we'd need package.json + node_modules walking
|
|
12
|
+
* to handle those, and signatures of third-party deps rarely catch bugs
|
|
13
|
+
* the diff alone misses.
|
|
14
|
+
* - Try the literal path, then `<path>.ts`, `.tsx`, `.js`, `.jsx`,
|
|
15
|
+
* `.py`, then `<path>/index.ts` / `.js`. Skip the rest. The plan calls
|
|
16
|
+
* this "1-hop"; we explicitly do NOT recurse.
|
|
17
|
+
*
|
|
18
|
+
* Returns at most `cap` imported symbols total across the call to keep the
|
|
19
|
+
* rendered block bounded.
|
|
20
|
+
*/
|
|
21
|
+
import { dirname, posix } from 'node:path';
|
|
22
|
+
import { languageForPath } from './languages.js';
|
|
23
|
+
import { parse } from './parser.js';
|
|
24
|
+
import { topLevelDefinitions, symbolName } from './symbols.js';
|
|
25
|
+
const RELATIVE_CANDIDATE_SUFFIXES = ['', '.ts', '.tsx', '.js', '.jsx', '.py', '.rb'];
|
|
26
|
+
const INDEX_SUFFIXES = ['/index.ts', '/index.tsx', '/index.js', '/index.jsx', '/__init__.py'];
|
|
27
|
+
function isRelative(spec) {
|
|
28
|
+
return spec.startsWith('./') || spec.startsWith('../') || spec === '.' || spec === '..';
|
|
29
|
+
}
|
|
30
|
+
function candidatePaths(fromFile, spec) {
|
|
31
|
+
const baseDir = dirname(fromFile);
|
|
32
|
+
const joined = posix.normalize(posix.join(baseDir, spec));
|
|
33
|
+
const out = [];
|
|
34
|
+
// TS/ESM convention: source imports `./foo.js` but the actual file on disk
|
|
35
|
+
// is `./foo.ts`. Try the TS-equivalent before the literal so common cases
|
|
36
|
+
// resolve on the first hit. Also handles `.jsx → .tsx`, `.mjs → .mts`, etc.
|
|
37
|
+
const jsToTs = {
|
|
38
|
+
'.js': '.ts',
|
|
39
|
+
'.jsx': '.tsx',
|
|
40
|
+
'.mjs': '.mts',
|
|
41
|
+
'.cjs': '.cts',
|
|
42
|
+
};
|
|
43
|
+
const extMatch = /(\.[a-z]+)$/i.exec(joined);
|
|
44
|
+
if (extMatch && jsToTs[extMatch[1]]) {
|
|
45
|
+
out.push(joined.slice(0, -extMatch[1].length) + jsToTs[extMatch[1]]);
|
|
46
|
+
}
|
|
47
|
+
for (const sfx of RELATIVE_CANDIDATE_SUFFIXES) {
|
|
48
|
+
out.push(joined + sfx);
|
|
49
|
+
}
|
|
50
|
+
for (const sfx of INDEX_SUFFIXES) {
|
|
51
|
+
out.push(joined + sfx);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
async function resolveImport(reader, fromFile, spec) {
|
|
56
|
+
if (!isRelative(spec))
|
|
57
|
+
return null;
|
|
58
|
+
for (const candidate of candidatePaths(fromFile, spec)) {
|
|
59
|
+
const content = await reader.read(candidate);
|
|
60
|
+
if (content !== null)
|
|
61
|
+
return { path: candidate, content };
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
export async function resolveImports(input) {
|
|
66
|
+
const collected = [];
|
|
67
|
+
for (const edge of input.edges) {
|
|
68
|
+
if (collected.length >= input.cap)
|
|
69
|
+
break;
|
|
70
|
+
if (!isRelative(edge.source))
|
|
71
|
+
continue;
|
|
72
|
+
const resolved = await resolveImport(input.reader, input.fromFile, edge.source);
|
|
73
|
+
if (!resolved)
|
|
74
|
+
continue;
|
|
75
|
+
const lang = languageForPath(resolved.path);
|
|
76
|
+
if (!lang)
|
|
77
|
+
continue;
|
|
78
|
+
const parsed = await parse(resolved.content, lang);
|
|
79
|
+
if (!parsed)
|
|
80
|
+
continue;
|
|
81
|
+
const defs = topLevelDefinitions(parsed, lang);
|
|
82
|
+
const wanted = new Set(edge.names);
|
|
83
|
+
const wantsAll = wanted.has('*');
|
|
84
|
+
for (const def of defs) {
|
|
85
|
+
if (collected.length >= input.cap)
|
|
86
|
+
break;
|
|
87
|
+
const nm = symbolName(def);
|
|
88
|
+
if (!wantsAll && !wanted.has(nm))
|
|
89
|
+
continue;
|
|
90
|
+
// Signature = first line of the body. For TS interfaces / type aliases
|
|
91
|
+
// the "first line" is the only meaningful part anyway.
|
|
92
|
+
const firstLine = def.text.split('\n', 1)[0].trim();
|
|
93
|
+
collected.push({
|
|
94
|
+
name: nm,
|
|
95
|
+
fromFile: resolved.path,
|
|
96
|
+
signature: firstLine,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return collected;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=imports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imports.js","sourceRoot":"","sources":["../../src/semantic/imports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entry point: build a `<semantic_context>` block to inline next to
|
|
3
|
+
* the diff in the code-review LLM user message.
|
|
4
|
+
*
|
|
5
|
+
* Never throws. Any failure (grammar load, file fetch, parse) returns an
|
|
6
|
+
* empty `SemanticBlock` and the caller proceeds with diff-only — same as
|
|
7
|
+
* Foyer behaves today.
|
|
8
|
+
*
|
|
9
|
+
* Timing: this runs in the pre-sandbox static pass alongside `code_review`.
|
|
10
|
+
* It uses `FileReader` (Octokit-backed) rather than a `SandboxHandle`,
|
|
11
|
+
* because at this point in the orchestrator no workspace exists yet.
|
|
12
|
+
* Cross-file caller search would require a workspace and is deferred —
|
|
13
|
+
* see `~/.claude/plans/implement-ast-parsing-for-scalable-charm.md`.
|
|
14
|
+
*/
|
|
15
|
+
import type { FileReader } from '../fileReader.js';
|
|
16
|
+
import { type SemanticBlockCache } from './cache.js';
|
|
17
|
+
import type { SemanticBlock } from './types.js';
|
|
18
|
+
export interface BuildSemanticContextOptions {
|
|
19
|
+
/** Hard cap on the rendered block (bytes). Default: 30_000. */
|
|
20
|
+
maxBytes?: number;
|
|
21
|
+
/** Cap on bytes per individual symbol body before slicing. Default: 4_000. */
|
|
22
|
+
bodyCapBytes?: number;
|
|
23
|
+
/** Cap on imported-symbol entries across the whole block. Default: 30. */
|
|
24
|
+
importedCap?: number;
|
|
25
|
+
/** Cap on files we'll parse in one call. Default: 20. Protects against
|
|
26
|
+
* mega-PRs that touch hundreds of files. */
|
|
27
|
+
fileCap?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function buildSemanticContext(diff: string, reader: FileReader, options?: BuildSemanticContextOptions): Promise<SemanticBlock>;
|
|
30
|
+
/**
|
|
31
|
+
* Cached variant: same `(repoFullName, headSha)` resolves through a shared
|
|
32
|
+
* LRU. The two consumers (`code_review`, `intent_fidelity`) hit the same key
|
|
33
|
+
* and the second one returns instantly. Falls through to a fresh build on
|
|
34
|
+
* miss, then stores. Pass `cache` to override the shared singleton in tests.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildSemanticContextCached(input: {
|
|
37
|
+
diff: string;
|
|
38
|
+
reader: FileReader;
|
|
39
|
+
repoFullName: string;
|
|
40
|
+
headSha: string;
|
|
41
|
+
}, options?: BuildSemanticContextOptions, cache?: SemanticBlockCache): Promise<SemanticBlock>;
|
|
42
|
+
export type { SemanticBlock, TouchedSymbol, ImportedSymbol } from './types.js';
|
|
43
|
+
export { getSharedSemanticBlockCache, _resetSharedCacheForTests } from './cache.js';
|
|
44
|
+
export type { SemanticBlockCache } from './cache.js';
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/semantic/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAOnD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,YAAY,CAAC;AAE/E,MAAM,WAAW,2BAA2B;IAC1C,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;iDAC6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AASD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,aAAa,CAAC,CA8CxB;AAED;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,EACD,OAAO,GAAE,2BAAgC,EACzC,KAAK,GAAE,kBAAkD,GACxD,OAAO,CAAC,aAAa,CAAC,CASxB;AAED,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACpF,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { touchedFiles } from './diffParse.js';
|
|
2
|
+
import { languageForPath } from './languages.js';
|
|
3
|
+
import { parse } from './parser.js';
|
|
4
|
+
import { findTouchedSymbols, findImports } from './symbols.js';
|
|
5
|
+
import { resolveImports } from './imports.js';
|
|
6
|
+
import { renderSemanticBlock } from './contextBuilder.js';
|
|
7
|
+
import { cacheKey, getSharedSemanticBlockCache, } from './cache.js';
|
|
8
|
+
const DEFAULT_OPTS = {
|
|
9
|
+
maxBytes: 30_000,
|
|
10
|
+
bodyCapBytes: 4_000,
|
|
11
|
+
importedCap: 30,
|
|
12
|
+
fileCap: 20,
|
|
13
|
+
};
|
|
14
|
+
export async function buildSemanticContext(diff, reader, options = {}) {
|
|
15
|
+
const opts = { ...DEFAULT_OPTS, ...options };
|
|
16
|
+
if (!diff || diff.trim().length === 0) {
|
|
17
|
+
return { rendered: '', stats: { touchedFiles: 0, touchedSymbols: 0, importedSymbols: 0, bytes: 0, truncated: false } };
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const files = touchedFiles(diff).slice(0, opts.fileCap);
|
|
21
|
+
if (files.length === 0) {
|
|
22
|
+
return { rendered: '', stats: { touchedFiles: 0, touchedSymbols: 0, importedSymbols: 0, bytes: 0, truncated: false } };
|
|
23
|
+
}
|
|
24
|
+
const touched = [];
|
|
25
|
+
const imported = [];
|
|
26
|
+
const remainingImportBudget = () => opts.importedCap - imported.length;
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const lang = languageForPath(file.path);
|
|
29
|
+
if (!lang)
|
|
30
|
+
continue;
|
|
31
|
+
const source = await reader.read(file.path);
|
|
32
|
+
if (source === null)
|
|
33
|
+
continue;
|
|
34
|
+
const parsed = await parse(source, lang);
|
|
35
|
+
if (!parsed)
|
|
36
|
+
continue;
|
|
37
|
+
const fileSymbols = findTouchedSymbols(parsed, lang, file.path, file.hunks, opts.bodyCapBytes);
|
|
38
|
+
touched.push(...fileSymbols);
|
|
39
|
+
// Only bother resolving imports for files that actually have touched
|
|
40
|
+
// symbols — if the diff is a config edit, the imports aren't useful.
|
|
41
|
+
if (fileSymbols.length > 0 && remainingImportBudget() > 0) {
|
|
42
|
+
const edges = findImports(parsed, lang);
|
|
43
|
+
const resolved = await resolveImports({
|
|
44
|
+
fromFile: file.path,
|
|
45
|
+
edges,
|
|
46
|
+
reader,
|
|
47
|
+
cap: remainingImportBudget(),
|
|
48
|
+
});
|
|
49
|
+
imported.push(...resolved);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return renderSemanticBlock({ touched, imported, maxBytes: opts.maxBytes });
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Never block a review on enrichment failure.
|
|
56
|
+
return { rendered: '', stats: { touchedFiles: 0, touchedSymbols: 0, importedSymbols: 0, bytes: 0, truncated: false } };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Cached variant: same `(repoFullName, headSha)` resolves through a shared
|
|
61
|
+
* LRU. The two consumers (`code_review`, `intent_fidelity`) hit the same key
|
|
62
|
+
* and the second one returns instantly. Falls through to a fresh build on
|
|
63
|
+
* miss, then stores. Pass `cache` to override the shared singleton in tests.
|
|
64
|
+
*/
|
|
65
|
+
export async function buildSemanticContextCached(input, options = {}, cache = getSharedSemanticBlockCache()) {
|
|
66
|
+
const key = cacheKey(input.repoFullName, input.headSha);
|
|
67
|
+
const cached = cache.get(key);
|
|
68
|
+
if (cached)
|
|
69
|
+
return cached;
|
|
70
|
+
const fresh = await buildSemanticContext(input.diff, input.reader, options);
|
|
71
|
+
// Only cache non-empty blocks. Empty results from a transient failure
|
|
72
|
+
// (e.g. parser cold-start race) should be retryable on the next consumer.
|
|
73
|
+
if (fresh.rendered.length > 0)
|
|
74
|
+
cache.set(key, fresh);
|
|
75
|
+
return fresh;
|
|
76
|
+
}
|
|
77
|
+
export { getSharedSemanticBlockCache, _resetSharedCacheForTests } from './cache.js';
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/semantic/index.ts"],"names":[],"mappings":"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"}
|