@getfoyer/review-core 0.1.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/plan-context.d.ts +14 -0
- package/dist/plan-context.d.ts.map +1 -0
- package/dist/plan-context.js +133 -0
- package/dist/plan-context.js.map +1 -0
- 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 +20 -4
|
@@ -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,14 @@
|
|
|
1
|
+
import type { PlanRepoContext } from '@getfoyer/client';
|
|
2
|
+
export declare const PATH_BACKTICK_RE: RegExp;
|
|
3
|
+
export declare const SYMBOL_BACKTICK_RE: RegExp;
|
|
4
|
+
export declare const NOISE_PATH_PREFIXES: string[];
|
|
5
|
+
export declare const EXTENSIONLESS_TOP_LEVEL: Set<string>;
|
|
6
|
+
export declare const STOP_WORDS: Set<string>;
|
|
7
|
+
export declare function buildRepoContext(planText: string, cwd: string): Promise<PlanRepoContext>;
|
|
8
|
+
export declare function extractReferencedPaths(text: string): string[];
|
|
9
|
+
export declare function extractReferencedSymbols(text: string): string[];
|
|
10
|
+
export declare function fileExists(absPath: string): Promise<boolean>;
|
|
11
|
+
export declare function gitGrepExists(needle: string, cwd: string): boolean;
|
|
12
|
+
export declare function collectNearbyTests(paths: string[], cwd: string): string[];
|
|
13
|
+
export declare function detectLanguages(cwd: string): string[];
|
|
14
|
+
//# sourceMappingURL=plan-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-context.d.ts","sourceRoot":"","sources":["../src/plan-context.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,eAAO,MAAM,gBAAgB,QAAkO,CAAC;AAChQ,eAAO,MAAM,kBAAkB,QAAkD,CAAC;AAElF,eAAO,MAAM,mBAAmB,UAA4C,CAAC;AAE7E,eAAO,MAAM,uBAAuB,aAGlC,CAAC;AAEH,eAAO,MAAM,UAAU,aAGrB,CAAC;AAEH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAsB9F;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAe7D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAa/D;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOlE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAUlE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBzE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBrD"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { access } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
+
export const PATH_BACKTICK_RE = /`((?:[./A-Za-z0-9_\-]+\.[A-Za-z0-9]{1,8})|(?:[./A-Za-z0-9_\-]*\/[A-Za-z0-9_.\-]+)|(?:Dockerfile|Makefile|Rakefile|Gemfile|Procfile|LICENSE|README|CHANGELOG|NOTICE|AUTHORS|CONTRIBUTORS|VERSION|\.[A-Za-z][A-Za-z0-9_\-]*))`/g;
|
|
6
|
+
export const SYMBOL_BACKTICK_RE = /`([A-Za-z_][A-Za-z0-9_]{2,})(?:\([^`)]*\))?`/g;
|
|
7
|
+
export const NOISE_PATH_PREFIXES = ['http://', 'https://', '/', './', '../'];
|
|
8
|
+
export const EXTENSIONLESS_TOP_LEVEL = new Set([
|
|
9
|
+
'Dockerfile', 'Makefile', 'Rakefile', 'Gemfile', 'Procfile',
|
|
10
|
+
'LICENSE', 'README', 'CHANGELOG', 'NOTICE', 'AUTHORS', 'CONTRIBUTORS', 'VERSION',
|
|
11
|
+
]);
|
|
12
|
+
export const STOP_WORDS = new Set([
|
|
13
|
+
'and', 'the', 'for', 'with', 'this', 'that', 'from', 'into', 'onto',
|
|
14
|
+
'json', 'null', 'true', 'false', 'TODO', 'FIXME', 'NOTE',
|
|
15
|
+
]);
|
|
16
|
+
export async function buildRepoContext(planText, cwd) {
|
|
17
|
+
const referencedPaths = extractReferencedPaths(planText);
|
|
18
|
+
const referencedSymbols = extractReferencedSymbols(planText);
|
|
19
|
+
const files = await Promise.all(referencedPaths.map(async (path) => ({
|
|
20
|
+
path,
|
|
21
|
+
exists: await fileExists(resolve(cwd, path)),
|
|
22
|
+
})));
|
|
23
|
+
const symbols = await Promise.all(referencedSymbols.map(async (name) => ({
|
|
24
|
+
name,
|
|
25
|
+
foundInRepo: gitGrepExists(name, cwd),
|
|
26
|
+
})));
|
|
27
|
+
const nearbyTests = collectNearbyTests(referencedPaths, cwd);
|
|
28
|
+
const languages = detectLanguages(cwd);
|
|
29
|
+
return { files, symbols, nearbyTests, languages };
|
|
30
|
+
}
|
|
31
|
+
export function extractReferencedPaths(text) {
|
|
32
|
+
const seen = new Set();
|
|
33
|
+
PATH_BACKTICK_RE.lastIndex = 0;
|
|
34
|
+
let m;
|
|
35
|
+
while ((m = PATH_BACKTICK_RE.exec(text)) !== null) {
|
|
36
|
+
const raw = m[1];
|
|
37
|
+
if (NOISE_PATH_PREFIXES.some((p) => raw.startsWith(p)))
|
|
38
|
+
continue;
|
|
39
|
+
const hasSlash = raw.includes('/');
|
|
40
|
+
const looksLikeVersion = /^\d+\.\d+(\.\d+)?$/.test(raw);
|
|
41
|
+
const isKnownTopLevel = EXTENSIONLESS_TOP_LEVEL.has(raw) || raw.startsWith('.');
|
|
42
|
+
if ((!hasSlash && !isKnownTopLevel) || looksLikeVersion)
|
|
43
|
+
continue;
|
|
44
|
+
seen.add(raw);
|
|
45
|
+
if (seen.size >= 50)
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
return Array.from(seen);
|
|
49
|
+
}
|
|
50
|
+
export function extractReferencedSymbols(text) {
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
SYMBOL_BACKTICK_RE.lastIndex = 0;
|
|
53
|
+
let m;
|
|
54
|
+
while ((m = SYMBOL_BACKTICK_RE.exec(text)) !== null) {
|
|
55
|
+
const raw = m[1];
|
|
56
|
+
if (raw.includes('/') || raw.includes('.'))
|
|
57
|
+
continue;
|
|
58
|
+
if (raw.length < 3 || raw.length > 64)
|
|
59
|
+
continue;
|
|
60
|
+
if (STOP_WORDS.has(raw))
|
|
61
|
+
continue;
|
|
62
|
+
seen.add(raw);
|
|
63
|
+
if (seen.size >= 50)
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
return Array.from(seen);
|
|
67
|
+
}
|
|
68
|
+
export async function fileExists(absPath) {
|
|
69
|
+
try {
|
|
70
|
+
await access(absPath);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function gitGrepExists(needle, cwd) {
|
|
78
|
+
try {
|
|
79
|
+
execSync(`git grep -l --fixed-strings --word-regexp -- ${JSON.stringify(needle)}`, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function collectNearbyTests(paths, cwd) {
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
for (const path of paths) {
|
|
89
|
+
const dir = dirname(resolve(cwd, path));
|
|
90
|
+
for (const candidate of [join(dir, '__tests__'), dir]) {
|
|
91
|
+
let entries;
|
|
92
|
+
try {
|
|
93
|
+
entries = readdirSync(candidate, { withFileTypes: true, encoding: 'utf8' });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
if (!entry.isFile())
|
|
100
|
+
continue;
|
|
101
|
+
if (!/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(entry.name))
|
|
102
|
+
continue;
|
|
103
|
+
const rel = relative(cwd, join(candidate, entry.name));
|
|
104
|
+
seen.add(rel);
|
|
105
|
+
if (seen.size >= 30)
|
|
106
|
+
return Array.from(seen);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return Array.from(seen);
|
|
111
|
+
}
|
|
112
|
+
export function detectLanguages(cwd) {
|
|
113
|
+
const langs = [];
|
|
114
|
+
for (const [file, lang] of [
|
|
115
|
+
['package.json', 'JavaScript/TypeScript'],
|
|
116
|
+
['tsconfig.json', 'TypeScript'],
|
|
117
|
+
['pyproject.toml', 'Python'],
|
|
118
|
+
['requirements.txt', 'Python'],
|
|
119
|
+
['go.mod', 'Go'],
|
|
120
|
+
['Cargo.toml', 'Rust'],
|
|
121
|
+
['Gemfile', 'Ruby'],
|
|
122
|
+
['composer.json', 'PHP'],
|
|
123
|
+
]) {
|
|
124
|
+
if (existsSync(join(cwd, file)))
|
|
125
|
+
langs.push(lang);
|
|
126
|
+
}
|
|
127
|
+
const unique = [];
|
|
128
|
+
for (const l of langs)
|
|
129
|
+
if (!unique.includes(l))
|
|
130
|
+
unique.push(l);
|
|
131
|
+
return unique;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=plan-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-context.js","sourceRoot":"","sources":["../src/plan-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -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"}
|