@nanonets/graft 0.1.0
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/LICENSE +21 -0
- package/README.md +228 -0
- package/dist/ai/crux.d.ts +31 -0
- package/dist/ai/crux.d.ts.map +1 -0
- package/dist/ai/crux.js +91 -0
- package/dist/ai/crux.js.map +1 -0
- package/dist/ai/providers.d.ts +37 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +17 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/summarize.d.ts +25 -0
- package/dist/ai/summarize.d.ts.map +1 -0
- package/dist/ai/summarize.js +41 -0
- package/dist/ai/summarize.js.map +1 -0
- package/dist/ai/synthesize.d.ts +32 -0
- package/dist/ai/synthesize.d.ts.map +1 -0
- package/dist/ai/synthesize.js +132 -0
- package/dist/ai/synthesize.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +131 -0
- package/dist/cli.js.map +1 -0
- package/dist/context/build.d.ts +33 -0
- package/dist/context/build.d.ts.map +1 -0
- package/dist/context/build.js +265 -0
- package/dist/context/build.js.map +1 -0
- package/dist/context/check.d.ts +21 -0
- package/dist/context/check.d.ts.map +1 -0
- package/dist/context/check.js +127 -0
- package/dist/context/check.js.map +1 -0
- package/dist/context/node-file.d.ts +80 -0
- package/dist/context/node-file.d.ts.map +1 -0
- package/dist/context/node-file.js +165 -0
- package/dist/context/node-file.js.map +1 -0
- package/dist/engine.d.ts +58 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +87 -0
- package/dist/engine.js.map +1 -0
- package/dist/graph/build.d.ts +33 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +95 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/check.d.ts +18 -0
- package/dist/graph/check.d.ts.map +1 -0
- package/dist/graph/check.js +126 -0
- package/dist/graph/check.js.map +1 -0
- package/dist/graph/enrich.d.ts +42 -0
- package/dist/graph/enrich.d.ts.map +1 -0
- package/dist/graph/enrich.js +141 -0
- package/dist/graph/enrich.js.map +1 -0
- package/dist/graph/extract.d.ts +23 -0
- package/dist/graph/extract.d.ts.map +1 -0
- package/dist/graph/extract.js +249 -0
- package/dist/graph/extract.js.map +1 -0
- package/dist/graph/resolve.d.ts +4 -0
- package/dist/graph/resolve.d.ts.map +1 -0
- package/dist/graph/resolve.js +0 -0
- package/dist/graph/resolve.js.map +1 -0
- package/dist/graph/types.d.ts +56 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +14 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/graph/write.d.ts +9 -0
- package/dist/graph/write.d.ts.map +1 -0
- package/dist/graph/write.js +39 -0
- package/dist/graph/write.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/fs.d.ts +10 -0
- package/dist/ingest/fs.d.ts.map +1 -0
- package/dist/ingest/fs.js +48 -0
- package/dist/ingest/fs.js.map +1 -0
- package/dist/util/id.d.ts +7 -0
- package/dist/util/id.d.ts.map +1 -0
- package/dist/util/id.js +14 -0
- package/dist/util/id.js.map +1 -0
- package/dist/viewer/app.js +1 -0
- package/dist/viewer/index.html +48 -0
- package/dist/viewer/style.css +119 -0
- package/dist/viz/assemble.d.ts +33 -0
- package/dist/viz/assemble.d.ts.map +1 -0
- package/dist/viz/assemble.js +101 -0
- package/dist/viz/assemble.js.map +1 -0
- package/dist/viz/serve.d.ts +12 -0
- package/dist/viz/serve.d.ts.map +1 -0
- package/dist/viz/serve.js +128 -0
- package/dist/viz/serve.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `graph` — build `.context/graph.json` from a code repository.
|
|
3
|
+
*
|
|
4
|
+
* M1 pipeline (Tier-1 only, deterministic, no LLM):
|
|
5
|
+
* 1. Walk the repo for TS/Python source files.
|
|
6
|
+
* 2. Parse each with tree-sitter and emit one NodeV1 per definition.
|
|
7
|
+
* 3. Write a sorted graph.json.
|
|
8
|
+
* Edges (M2) and LLM summary/crux (M3) layer onto this without changing it.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { join, relative, resolve } from "node:path";
|
|
12
|
+
import { walkDir } from "../ingest/fs.js";
|
|
13
|
+
import { contextDirFor } from "../context/node-file.js";
|
|
14
|
+
import { extractFile, languageOf } from "./extract.js";
|
|
15
|
+
import { resolveEdges } from "./resolve.js";
|
|
16
|
+
import { enrichGraph } from "./enrich.js";
|
|
17
|
+
import { GRAPH_FILE, readGraph, writeGraph } from "./write.js";
|
|
18
|
+
/** The source files a graph build parses: supported languages, minus the output dir. */
|
|
19
|
+
export function listSourceFiles(root, outDir) {
|
|
20
|
+
return walkDir(root).filter((f) => !f.startsWith(outDir) && languageOf(f) !== null);
|
|
21
|
+
}
|
|
22
|
+
export async function buildGraph(dir, opts = {}) {
|
|
23
|
+
const root = resolve(dir);
|
|
24
|
+
const outDir = contextDirFor(root, opts.contextDir);
|
|
25
|
+
const files = listSourceFiles(root, outDir);
|
|
26
|
+
const nodes = [];
|
|
27
|
+
const rawEdges = [];
|
|
28
|
+
const sources = new Map();
|
|
29
|
+
const langs = new Set();
|
|
30
|
+
const errors = [];
|
|
31
|
+
files.forEach((file, i) => {
|
|
32
|
+
const rel = relative(root, file);
|
|
33
|
+
opts.onProgress?.({ phase: "parse", index: i, total: files.length, file: rel });
|
|
34
|
+
const lang = languageOf(file);
|
|
35
|
+
let source;
|
|
36
|
+
try {
|
|
37
|
+
source = readFileSync(file, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
errors.push(`${rel}: ${err instanceof Error ? err.message : String(err)}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const { nodes: fileNodes, rawEdges: fileEdges } = extractFile(rel, source, lang);
|
|
45
|
+
nodes.push(...fileNodes);
|
|
46
|
+
rawEdges.push(...fileEdges);
|
|
47
|
+
sources.set(rel, source);
|
|
48
|
+
langs.add(lang);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
errors.push(`${rel}: parse failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const edges = resolveEdges(nodes, rawEdges);
|
|
55
|
+
// graph.json is its own Tier-2 cache: fold in the prior meaning layer so an
|
|
56
|
+
// unchanged body is never re-summarized (and a Tier-1-only run never wipes it).
|
|
57
|
+
const prior = readGraph(join(outDir, GRAPH_FILE));
|
|
58
|
+
const priorById = new Map((prior?.nodes ?? []).map((n) => [n.id, n]));
|
|
59
|
+
const meaning = await enrichGraph(nodes, priorById, sources, {
|
|
60
|
+
summarizer: opts.summarizer,
|
|
61
|
+
onProgress: ({ index, total, node }) => opts.onProgress?.({ phase: "enrich", index, total, file: node }),
|
|
62
|
+
});
|
|
63
|
+
errors.push(...meaning.errors);
|
|
64
|
+
const graph = {
|
|
65
|
+
meta: {
|
|
66
|
+
version: 1,
|
|
67
|
+
nodeCount: nodes.length,
|
|
68
|
+
edgeCount: edges.length,
|
|
69
|
+
languages: [...langs].sort(),
|
|
70
|
+
},
|
|
71
|
+
nodes,
|
|
72
|
+
edges,
|
|
73
|
+
};
|
|
74
|
+
const graphPath = writeGraph(graph, outDir);
|
|
75
|
+
const byKind = {};
|
|
76
|
+
for (const n of nodes)
|
|
77
|
+
byKind[n.kind] = (byKind[n.kind] ?? 0) + 1;
|
|
78
|
+
const byRelation = {};
|
|
79
|
+
for (const e of edges)
|
|
80
|
+
byRelation[e.relation] = (byRelation[e.relation] ?? 0) + 1;
|
|
81
|
+
return {
|
|
82
|
+
contextDir: outDir,
|
|
83
|
+
graphPath,
|
|
84
|
+
files: files.length,
|
|
85
|
+
nodes: nodes.length,
|
|
86
|
+
edges: edges.length,
|
|
87
|
+
byKind,
|
|
88
|
+
byRelation,
|
|
89
|
+
languages: [...langs].sort(),
|
|
90
|
+
meaning,
|
|
91
|
+
errors,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export { GRAPH_FILE };
|
|
95
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/graph/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface GraphCheckResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
/** True when there is no graph.json (a graph has never been built). */
|
|
4
|
+
missing: boolean;
|
|
5
|
+
added: string[];
|
|
6
|
+
removed: string[];
|
|
7
|
+
changed: string[];
|
|
8
|
+
stale: string[];
|
|
9
|
+
/** Nodes never summarized (reported for context; not counted as drift). */
|
|
10
|
+
pending: number;
|
|
11
|
+
}
|
|
12
|
+
export interface GraphCheckOptions {
|
|
13
|
+
contextDir?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function checkGraph(dir: string, opts?: GraphCheckOptions): GraphCheckResult;
|
|
16
|
+
/** Render a graph-check result as a human-readable report. */
|
|
17
|
+
export declare function formatGraphCheckReport(r: GraphCheckResult): string;
|
|
18
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/graph/check.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CA0DtF;AAED,8DAA8D;AAC9D,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,CA+BlE"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `checkGraph` — is the committed `graph.json` still in sync with the code?
|
|
3
|
+
*
|
|
4
|
+
* Deterministic and fast (tree-sitter only, no LLM, no network): it re-runs
|
|
5
|
+
* Tier-1 extraction and diffs the fresh node set against the committed graph by
|
|
6
|
+
* `id` and `body_hash`. Meant for CI — exit non-zero when a PR changed code but
|
|
7
|
+
* didn't rebuild the graph.
|
|
8
|
+
*
|
|
9
|
+
* Drift categories:
|
|
10
|
+
* added a definition exists in code but not in graph.json (run `graph`)
|
|
11
|
+
* removed a node in graph.json no longer exists in code (run `graph`)
|
|
12
|
+
* changed a node's body_hash differs from the committed one (run `graph`)
|
|
13
|
+
* stale a committed node's summary is flagged stale — its body changed
|
|
14
|
+
* since it was last summarized (run `graph --llm`)
|
|
15
|
+
*
|
|
16
|
+
* `added`/`removed`/`changed` are structural: the graph no longer describes the
|
|
17
|
+
* code. `stale` is a meaning-layer signal the last build already recorded.
|
|
18
|
+
* `pending` (never summarized) is not drift — it's a deliberate Tier-1-only build.
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import { join, relative, resolve } from "node:path";
|
|
22
|
+
import { contextDirFor } from "../context/node-file.js";
|
|
23
|
+
import { extractFile, languageOf } from "./extract.js";
|
|
24
|
+
import { listSourceFiles } from "./build.js";
|
|
25
|
+
import { GRAPH_FILE, readGraph } from "./write.js";
|
|
26
|
+
export function checkGraph(dir, opts = {}) {
|
|
27
|
+
const root = resolve(dir);
|
|
28
|
+
const outDir = contextDirFor(root, opts.contextDir);
|
|
29
|
+
const result = {
|
|
30
|
+
ok: false,
|
|
31
|
+
missing: false,
|
|
32
|
+
added: [],
|
|
33
|
+
removed: [],
|
|
34
|
+
changed: [],
|
|
35
|
+
stale: [],
|
|
36
|
+
pending: 0,
|
|
37
|
+
};
|
|
38
|
+
const committed = readGraph(join(outDir, GRAPH_FILE));
|
|
39
|
+
if (!committed) {
|
|
40
|
+
result.missing = true;
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
// Freshly extract Tier-1 nodes from the code on disk (same file set as build).
|
|
44
|
+
const current = new Map(); // id → body_hash
|
|
45
|
+
for (const file of listSourceFiles(root, outDir)) {
|
|
46
|
+
const lang = languageOf(file);
|
|
47
|
+
let source;
|
|
48
|
+
try {
|
|
49
|
+
source = readFileSync(file, "utf8");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue; // unreadable now → its nodes show up as `removed` below
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const { nodes } = extractFile(relative(root, file), source, lang);
|
|
56
|
+
for (const n of nodes)
|
|
57
|
+
current.set(n.id, n.body_hash);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// parse failure → skip; the committed nodes for this file become `removed`.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const committedById = new Map(committed.nodes.map((n) => [n.id, n]));
|
|
64
|
+
for (const [id, node] of committedById) {
|
|
65
|
+
const now = current.get(id);
|
|
66
|
+
if (now === undefined)
|
|
67
|
+
result.removed.push(id);
|
|
68
|
+
else if (now !== node.body_hash)
|
|
69
|
+
result.changed.push(id);
|
|
70
|
+
if (node.summary_state === "stale")
|
|
71
|
+
result.stale.push(id);
|
|
72
|
+
if (node.summary_state === "pending")
|
|
73
|
+
result.pending++;
|
|
74
|
+
}
|
|
75
|
+
for (const id of current.keys()) {
|
|
76
|
+
if (!committedById.has(id))
|
|
77
|
+
result.added.push(id);
|
|
78
|
+
}
|
|
79
|
+
for (const arr of [result.added, result.removed, result.changed, result.stale])
|
|
80
|
+
arr.sort();
|
|
81
|
+
result.ok =
|
|
82
|
+
result.added.length === 0 &&
|
|
83
|
+
result.removed.length === 0 &&
|
|
84
|
+
result.changed.length === 0 &&
|
|
85
|
+
result.stale.length === 0;
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/** Render a graph-check result as a human-readable report. */
|
|
89
|
+
export function formatGraphCheckReport(r) {
|
|
90
|
+
if (r.missing) {
|
|
91
|
+
return "graph check: NO GRAPH\n\nNo .context/graph.json found. Run `graft graph` first.";
|
|
92
|
+
}
|
|
93
|
+
if (r.ok) {
|
|
94
|
+
const note = r.pending ? ` (${r.pending} node(s) not yet summarized — run \`graph --llm\`)` : "";
|
|
95
|
+
return `graph check: OK — graph.json is in sync with the code.${note}`;
|
|
96
|
+
}
|
|
97
|
+
const lines = ["graph check: STALE", ""];
|
|
98
|
+
const structural = r.added.length + r.removed.length + r.changed.length;
|
|
99
|
+
if (r.changed.length) {
|
|
100
|
+
lines.push(`changed (${r.changed.length}):`);
|
|
101
|
+
for (const id of r.changed)
|
|
102
|
+
lines.push(` ~ ${id}`);
|
|
103
|
+
}
|
|
104
|
+
if (r.added.length) {
|
|
105
|
+
lines.push(`added (${r.added.length}):`);
|
|
106
|
+
for (const id of r.added)
|
|
107
|
+
lines.push(` + ${id}`);
|
|
108
|
+
}
|
|
109
|
+
if (r.removed.length) {
|
|
110
|
+
lines.push(`removed (${r.removed.length}):`);
|
|
111
|
+
for (const id of r.removed)
|
|
112
|
+
lines.push(` - ${id}`);
|
|
113
|
+
}
|
|
114
|
+
if (r.stale.length) {
|
|
115
|
+
lines.push(`stale summaries (${r.stale.length}):`);
|
|
116
|
+
for (const id of r.stale)
|
|
117
|
+
lines.push(` ! ${id}`);
|
|
118
|
+
}
|
|
119
|
+
lines.push("");
|
|
120
|
+
if (structural)
|
|
121
|
+
lines.push("Run `graft graph` to rebuild the structure, then commit .context/.");
|
|
122
|
+
if (r.stale.length)
|
|
123
|
+
lines.push("Run `graft graph --llm` to refresh stale summaries.");
|
|
124
|
+
return lines.join("\n");
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/graph/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-2 pass: attach the LLM meaning layer (`summary` + `crux`) to nodes.
|
|
3
|
+
*
|
|
4
|
+
* graph.json is its own cache — the committed file already holds every node's
|
|
5
|
+
* summary/crux from a prior run. So this pass is diff-driven:
|
|
6
|
+
*
|
|
7
|
+
* - cache hit — a prior node with the same id, same `body_hash`, and
|
|
8
|
+
* `summary_state:"ready"` → carry its summary/crux over, no LLM call.
|
|
9
|
+
* - stale — a prior ready summary whose body has since changed → keep the
|
|
10
|
+
* old text as a hint but mark it "stale". Recomputed only if an LLM is given.
|
|
11
|
+
* - pending — new or never-summarized node → one LLM call when an LLM is
|
|
12
|
+
* given, otherwise left "pending".
|
|
13
|
+
*
|
|
14
|
+
* Passing no summarizer runs the cache/stale bookkeeping alone (no calls, no
|
|
15
|
+
* cost) — which is what a plain `graph` build does, so it never wipes the
|
|
16
|
+
* meaning layer a previous `--llm` run produced.
|
|
17
|
+
*
|
|
18
|
+
* The LLM returns line numbers into the slice it was shown; we consume them here,
|
|
19
|
+
* once, to cut `crux.code` verbatim from the source. `crux.span` is a pointer
|
|
20
|
+
* only and is never used to re-slice.
|
|
21
|
+
*/
|
|
22
|
+
import type { CruxSummarizer } from "../ai/crux.js";
|
|
23
|
+
import type { NodeV1 } from "./types.js";
|
|
24
|
+
export interface EnrichOptions {
|
|
25
|
+
/** When present, (re)compute meaning for stale/pending nodes. Absent → cache only. */
|
|
26
|
+
summarizer?: CruxSummarizer;
|
|
27
|
+
/** Progress is reported per file (one LLM call each), not per node. */
|
|
28
|
+
onProgress?: (info: {
|
|
29
|
+
index: number;
|
|
30
|
+
total: number;
|
|
31
|
+
node: string;
|
|
32
|
+
}) => void;
|
|
33
|
+
}
|
|
34
|
+
export interface EnrichStats {
|
|
35
|
+
cached: number;
|
|
36
|
+
computed: number;
|
|
37
|
+
stale: number;
|
|
38
|
+
pending: number;
|
|
39
|
+
errors: string[];
|
|
40
|
+
}
|
|
41
|
+
export declare function enrichGraph(nodes: NodeV1[], prior: Map<string, NodeV1>, sources: Map<string, string>, opts?: EnrichOptions): Promise<EnrichStats>;
|
|
42
|
+
//# sourceMappingURL=enrich.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich.d.ts","sourceRoot":"","sources":["../../src/graph/enrich.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAQ,MAAM,EAAE,MAAM,YAAY,CAAC;AAK/C,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC7E;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,WAAW,CAAC,CAgFtB"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/** Cap on the stored crux: an over-long pick is trimmed to its leading slice. */
|
|
2
|
+
const MAX_CRUX_LINES = 12;
|
|
3
|
+
export async function enrichGraph(nodes, prior, sources, opts = {}) {
|
|
4
|
+
const stats = { cached: 0, computed: 0, stale: 0, pending: 0, errors: [] };
|
|
5
|
+
// Which nodes actually need an LLM call this run (after cache carry-over).
|
|
6
|
+
const dirty = [];
|
|
7
|
+
for (const node of nodes) {
|
|
8
|
+
const was = prior.get(node.id);
|
|
9
|
+
if (was?.summary_state === "ready" && was.body_hash === node.body_hash) {
|
|
10
|
+
node.summary = was.summary;
|
|
11
|
+
node.crux = was.crux;
|
|
12
|
+
node.summary_state = "ready";
|
|
13
|
+
stats.cached++;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
// needs computing; seed with the old summary as a stale hint when one exists.
|
|
17
|
+
if (was && was.summary) {
|
|
18
|
+
node.summary = was.summary;
|
|
19
|
+
node.crux = was.crux;
|
|
20
|
+
node.summary_state = "stale";
|
|
21
|
+
}
|
|
22
|
+
dirty.push(node);
|
|
23
|
+
}
|
|
24
|
+
if (!opts.summarizer) {
|
|
25
|
+
for (const node of dirty) {
|
|
26
|
+
if (node.summary_state === "stale")
|
|
27
|
+
stats.stale++;
|
|
28
|
+
else
|
|
29
|
+
stats.pending++;
|
|
30
|
+
}
|
|
31
|
+
return stats;
|
|
32
|
+
}
|
|
33
|
+
// One LLM call per file: group the dirty nodes by the file they live in.
|
|
34
|
+
const byFile = new Map();
|
|
35
|
+
for (const node of dirty) {
|
|
36
|
+
if (!sources.has(node.path))
|
|
37
|
+
continue;
|
|
38
|
+
const arr = byFile.get(node.path);
|
|
39
|
+
if (arr)
|
|
40
|
+
arr.push(node);
|
|
41
|
+
else
|
|
42
|
+
byFile.set(node.path, [node]);
|
|
43
|
+
}
|
|
44
|
+
const files = [...byFile.keys()];
|
|
45
|
+
for (let i = 0; i < files.length; i++) {
|
|
46
|
+
const path = files[i];
|
|
47
|
+
const fileNodes = byFile.get(path);
|
|
48
|
+
const source = sources.get(path);
|
|
49
|
+
const lineCount = source.split("\n").length;
|
|
50
|
+
opts.onProgress?.({ index: i, total: files.length, node: path });
|
|
51
|
+
const refs = fileNodes.map((n) => {
|
|
52
|
+
const [startLine, endLine] = spanLines(n.span, lineCount);
|
|
53
|
+
return { id: n.id, kind: n.kind, signature: n.signature, startLine, endLine };
|
|
54
|
+
});
|
|
55
|
+
const { results, error } = await collectFileCrux(opts.summarizer, path, source, refs);
|
|
56
|
+
if (error)
|
|
57
|
+
stats.errors.push(`${path}: ${error}`);
|
|
58
|
+
if (results.size === 0) {
|
|
59
|
+
// whole-file call failed: leave every node as-is (stale hint or pending).
|
|
60
|
+
for (const node of fileNodes) {
|
|
61
|
+
if (node.summary_state === "stale")
|
|
62
|
+
stats.stale++;
|
|
63
|
+
else
|
|
64
|
+
stats.pending++;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
for (const node of fileNodes) {
|
|
69
|
+
const r = results.get(node.id);
|
|
70
|
+
if (!r) {
|
|
71
|
+
// model skipped this symbol; keep whatever it had.
|
|
72
|
+
if (node.summary_state === "stale")
|
|
73
|
+
stats.stale++;
|
|
74
|
+
else
|
|
75
|
+
stats.pending++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
node.summary = r.summary || null;
|
|
79
|
+
node.crux = buildCrux(r, node, source, lineCount);
|
|
80
|
+
node.summary_state = "ready";
|
|
81
|
+
stats.computed++;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return stats;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Describe every requested definition in a file, re-asking for any the model
|
|
88
|
+
* omits (it sometimes drops entries from a batch). Returns whatever it collected
|
|
89
|
+
* plus the last error, if any — partial results are kept, not discarded.
|
|
90
|
+
*/
|
|
91
|
+
async function collectFileCrux(summarizer, path, source, refs) {
|
|
92
|
+
const results = new Map();
|
|
93
|
+
let missing = refs;
|
|
94
|
+
let error;
|
|
95
|
+
for (let attempt = 0; attempt < 2 && missing.length > 0; attempt++) {
|
|
96
|
+
try {
|
|
97
|
+
const list = await summarizer.describeFile({ path, source, nodes: missing });
|
|
98
|
+
for (const r of list)
|
|
99
|
+
if (!results.has(r.id))
|
|
100
|
+
results.set(r.id, r);
|
|
101
|
+
missing = refs.filter((r) => !results.has(r.id));
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
error = err instanceof Error ? err.message : String(err);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return { results, error };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Cut the crux text verbatim from the file using the model's file-absolute line
|
|
112
|
+
* range, clamped to the node's own span. Returns null when the model reported no
|
|
113
|
+
* distinct crux (0/0) or gave an unusable range.
|
|
114
|
+
*/
|
|
115
|
+
function buildCrux(r, node, source, lineCount) {
|
|
116
|
+
// 0/0 (or an invalid range) is the model saying "nothing to highlight here".
|
|
117
|
+
if (r.crux_start < 1 || r.crux_end < r.crux_start)
|
|
118
|
+
return null;
|
|
119
|
+
const [nodeStart, nodeEnd] = spanLines(node.span, lineCount);
|
|
120
|
+
const start = Math.max(nodeStart, Math.min(r.crux_start, nodeEnd));
|
|
121
|
+
let end = Math.max(start, Math.min(r.crux_end, nodeEnd));
|
|
122
|
+
// Keep the crux readable: if the model pointed at a big region, store its
|
|
123
|
+
// leading slice (the anchor it chose) rather than the whole blob — still the
|
|
124
|
+
// most important part, and the full definition is reachable via node.span.
|
|
125
|
+
if (end - start + 1 > MAX_CRUX_LINES)
|
|
126
|
+
end = start + MAX_CRUX_LINES - 1;
|
|
127
|
+
const code = source.split("\n").slice(start - 1, end).join("\n");
|
|
128
|
+
if (!code.trim())
|
|
129
|
+
return null;
|
|
130
|
+
return { code, span: `L${start}-L${end}` };
|
|
131
|
+
}
|
|
132
|
+
/** Parse a `"L12-L30"` span into a clamped [start, end] line pair (1-based). */
|
|
133
|
+
function spanLines(span, fileLines) {
|
|
134
|
+
const m = /^L(\d+)-L(\d+)$/.exec(span);
|
|
135
|
+
if (!m)
|
|
136
|
+
return [1, fileLines];
|
|
137
|
+
const start = Math.max(1, Math.min(Number(m[1]), fileLines));
|
|
138
|
+
const end = Math.max(start, Math.min(Number(m[2]), fileLines));
|
|
139
|
+
return [start, end];
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=enrich.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich.js","sourceRoot":"","sources":["../../src/graph/enrich.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { NodeV1, Relation } from "./types.js";
|
|
2
|
+
export type Language = "typescript" | "tsx" | "python";
|
|
3
|
+
/** Map a file path to a supported language, or null if unsupported. */
|
|
4
|
+
export declare function languageOf(path: string): Language | null;
|
|
5
|
+
/**
|
|
6
|
+
* An edge whose target isn't resolved yet. build.ts turns these into EdgeV1 by
|
|
7
|
+
* matching `name`/`specifier` against the repo-wide node index.
|
|
8
|
+
*/
|
|
9
|
+
export interface RawEdge {
|
|
10
|
+
source: string;
|
|
11
|
+
relation: Relation;
|
|
12
|
+
file: string;
|
|
13
|
+
targetId?: string;
|
|
14
|
+
specifier?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
viaMember?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface ExtractResult {
|
|
19
|
+
nodes: NodeV1[];
|
|
20
|
+
rawEdges: RawEdge[];
|
|
21
|
+
}
|
|
22
|
+
export declare function extractFile(rel: string, source: string, lang: Language): ExtractResult;
|
|
23
|
+
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/graph/extract.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAQ,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEvD,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAMxD;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAkDD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,aAAa,CAiCtF"}
|