@nanonets/graft 0.13.0 → 0.15.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/README.md +16 -5
- package/dist/ai/crux.d.ts.map +1 -1
- package/dist/ai/crux.js +8 -50
- package/dist/ai/crux.js.map +1 -1
- package/dist/ai/llm/factory.d.ts +5 -1
- package/dist/ai/llm/factory.d.ts.map +1 -1
- package/dist/ai/llm/factory.js +8 -0
- package/dist/ai/llm/factory.js.map +1 -1
- package/dist/ai/llm/litellm.d.ts +34 -0
- package/dist/ai/llm/litellm.d.ts.map +1 -0
- package/dist/ai/llm/litellm.js +45 -0
- package/dist/ai/llm/litellm.js.map +1 -0
- package/dist/ai/llm/openai.d.ts.map +1 -1
- package/dist/ai/llm/openai.js +66 -7
- package/dist/ai/llm/openai.js.map +1 -1
- package/dist/ai/llm/recover-tool.d.ts +19 -0
- package/dist/ai/llm/recover-tool.d.ts.map +1 -0
- package/dist/ai/llm/recover-tool.js +109 -0
- package/dist/ai/llm/recover-tool.js.map +1 -0
- package/dist/ai/providers.d.ts.map +1 -1
- package/dist/ai/providers.js +2 -0
- package/dist/ai/providers.js.map +1 -1
- package/dist/ai/synthesize.d.ts.map +1 -1
- package/dist/ai/synthesize.js +19 -5
- package/dist/ai/synthesize.js.map +1 -1
- package/dist/app/checkout.d.ts +28 -0
- package/dist/app/checkout.d.ts.map +1 -0
- package/dist/app/checkout.js +100 -0
- package/dist/app/checkout.js.map +1 -0
- package/dist/app/events.d.ts +37 -0
- package/dist/app/events.d.ts.map +1 -0
- package/dist/app/events.js +59 -0
- package/dist/app/events.js.map +1 -0
- package/dist/app/identity.d.ts +54 -0
- package/dist/app/identity.d.ts.map +1 -0
- package/dist/app/identity.js +96 -0
- package/dist/app/identity.js.map +1 -0
- package/dist/app/main.d.ts +2 -0
- package/dist/app/main.d.ts.map +1 -0
- package/dist/app/main.js +56 -0
- package/dist/app/main.js.map +1 -0
- package/dist/app/pages.d.ts +39 -0
- package/dist/app/pages.d.ts.map +1 -0
- package/dist/app/pages.js +86 -0
- package/dist/app/pages.js.map +1 -0
- package/dist/app/queue.d.ts +40 -0
- package/dist/app/queue.d.ts.map +1 -0
- package/dist/app/queue.js +68 -0
- package/dist/app/queue.js.map +1 -0
- package/dist/app/review.d.ts +23 -0
- package/dist/app/review.d.ts.map +1 -0
- package/dist/app/review.js +129 -0
- package/dist/app/review.js.map +1 -0
- package/dist/app/server.d.ts +40 -0
- package/dist/app/server.d.ts.map +1 -0
- package/dist/app/server.js +117 -0
- package/dist/app/server.js.map +1 -0
- package/dist/ask/ask.d.ts +35 -0
- package/dist/ask/ask.d.ts.map +1 -1
- package/dist/ask/ask.js +427 -63
- package/dist/ask/ask.js.map +1 -1
- package/dist/ask/file-rank.d.ts +72 -0
- package/dist/ask/file-rank.d.ts.map +1 -0
- package/dist/ask/file-rank.js +160 -0
- package/dist/ask/file-rank.js.map +1 -0
- package/dist/ask/file-selection.d.ts +4 -0
- package/dist/ask/file-selection.d.ts.map +1 -1
- package/dist/ask/file-selection.js +25 -0
- package/dist/ask/file-selection.js.map +1 -1
- package/dist/ask/fuse.d.ts +16 -0
- package/dist/ask/fuse.d.ts.map +1 -1
- package/dist/ask/fuse.js +21 -4
- package/dist/ask/fuse.js.map +1 -1
- package/dist/ask/graphrank.d.ts +18 -0
- package/dist/ask/graphrank.d.ts.map +1 -1
- package/dist/ask/graphrank.js +65 -19
- package/dist/ask/graphrank.js.map +1 -1
- package/dist/blast/blast-cli.d.ts +5 -0
- package/dist/blast/blast-cli.d.ts.map +1 -1
- package/dist/blast/blast-cli.js +15 -0
- package/dist/blast/blast-cli.js.map +1 -1
- package/dist/blast/blast.d.ts +9 -0
- package/dist/blast/blast.d.ts.map +1 -1
- package/dist/blast/blast.js.map +1 -1
- package/dist/blast/owners.d.ts +85 -0
- package/dist/blast/owners.d.ts.map +1 -0
- package/dist/blast/owners.js +272 -0
- package/dist/blast/owners.js.map +1 -0
- package/dist/blast/render.d.ts.map +1 -1
- package/dist/blast/render.js +105 -0
- package/dist/blast/render.js.map +1 -1
- package/dist/blast/viz.d.ts.map +1 -1
- package/dist/blast/viz.js +10 -0
- package/dist/blast/viz.js.map +1 -1
- package/dist/claude/format.js +1 -1
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +43 -3
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/settings-merge.d.ts +11 -0
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +24 -10
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/state.d.ts +13 -0
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/statusline.d.ts +5 -3
- package/dist/claude/statusline.d.ts.map +1 -1
- package/dist/claude/statusline.js +6 -4
- package/dist/claude/statusline.js.map +1 -1
- package/dist/claude/tally.d.ts +23 -0
- package/dist/claude/tally.d.ts.map +1 -0
- package/dist/claude/tally.js +138 -0
- package/dist/claude/tally.js.map +1 -0
- package/dist/cli.js +117 -2
- package/dist/cli.js.map +1 -1
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +23 -4
- package/dist/context/build.js.map +1 -1
- package/dist/context/check.d.ts.map +1 -1
- package/dist/context/check.js +2 -1
- package/dist/context/check.js.map +1 -1
- package/dist/context/node-file.d.ts +2 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +37 -2
- package/dist/context/node-file.js.map +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/bindings.d.ts +6 -5
- package/dist/graph/bindings.d.ts.map +1 -1
- package/dist/graph/bindings.js +117 -5
- package/dist/graph/bindings.js.map +1 -1
- package/dist/graph/build.d.ts +4 -0
- package/dist/graph/build.d.ts.map +1 -1
- package/dist/graph/build.js +7 -4
- package/dist/graph/build.js.map +1 -1
- package/dist/graph/check.d.ts.map +1 -1
- package/dist/graph/check.js +36 -6
- package/dist/graph/check.js.map +1 -1
- package/dist/graph/extract.d.ts +9 -2
- package/dist/graph/extract.d.ts.map +1 -1
- package/dist/graph/extract.js +356 -12
- package/dist/graph/extract.js.map +1 -1
- package/dist/graph/fingerprint.d.ts +6 -1
- package/dist/graph/fingerprint.d.ts.map +1 -1
- package/dist/graph/fingerprint.js +7 -3
- package/dist/graph/fingerprint.js.map +1 -1
- package/dist/graph/generic.d.ts.map +1 -1
- package/dist/graph/generic.js +0 -1
- package/dist/graph/generic.js.map +1 -1
- package/dist/graph/invariants.d.ts +3 -2
- package/dist/graph/invariants.d.ts.map +1 -1
- package/dist/graph/invariants.js +6 -3
- package/dist/graph/invariants.js.map +1 -1
- package/dist/graph/refresh.d.ts.map +1 -1
- package/dist/graph/refresh.js +6 -2
- package/dist/graph/refresh.js.map +1 -1
- package/dist/graph/resolve.d.ts +1 -1
- package/dist/graph/resolve.d.ts.map +1 -1
- package/dist/graph/resolve.js +106 -5
- package/dist/graph/resolve.js.map +1 -1
- package/dist/graph/scopes.d.ts.map +1 -1
- package/dist/graph/scopes.js +2 -1
- package/dist/graph/scopes.js.map +1 -1
- package/dist/graph/source-files.d.ts +7 -2
- package/dist/graph/source-files.d.ts.map +1 -1
- package/dist/graph/source-files.js +19 -6
- package/dist/graph/source-files.js.map +1 -1
- package/dist/graph/workspace-cli.d.ts +2 -0
- package/dist/graph/workspace-cli.d.ts.map +1 -1
- package/dist/graph/workspace-cli.js +3 -0
- package/dist/graph/workspace-cli.js.map +1 -1
- package/dist/hosts/mcp-config.d.ts +12 -0
- package/dist/hosts/mcp-config.d.ts.map +1 -1
- package/dist/hosts/mcp-config.js +46 -5
- package/dist/hosts/mcp-config.js.map +1 -1
- package/dist/hosts/retract.d.ts +47 -0
- package/dist/hosts/retract.d.ts.map +1 -0
- package/dist/hosts/retract.js +473 -0
- package/dist/hosts/retract.js.map +1 -0
- package/dist/ingest/fs.d.ts +19 -5
- package/dist/ingest/fs.d.ts.map +1 -1
- package/dist/ingest/fs.js +29 -9
- package/dist/ingest/fs.js.map +1 -1
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +7 -2
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/sessions.d.ts.map +1 -1
- package/dist/telemetry/sessions.js +6 -0
- package/dist/telemetry/sessions.js.map +1 -1
- package/dist/util/state.d.ts +12 -2
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js +4 -0
- package/dist/util/state.js.map +1 -1
- package/dist/viewer/app.js +2 -1
- package/dist/viewer/style.css +14 -0
- package/dist/viz/assemble.d.ts +17 -0
- package/dist/viz/assemble.d.ts.map +1 -1
- package/dist/viz/assemble.js.map +1 -1
- package/package.json +11 -3
- package/scripts/graph-quality.mjs +3 -3
- package/scripts/tally-audit.mjs +140 -0
- package/dist/ai/llm/claude-cli.d.ts +0 -60
- package/dist/ai/llm/claude-cli.d.ts.map +0 -1
- package/dist/ai/llm/claude-cli.js +0 -417
- package/dist/ai/llm/claude-cli.js.map +0 -1
- package/dist/util/num.d.ts +0 -3
- package/dist/util/num.d.ts.map +0 -1
- package/dist/util/num.js +0 -20
- package/dist/util/num.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanonets/graft",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Build a repo's context graph as a folder of linked markdown files: a local, regenerable cache that every query keeps in sync with your code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -67,8 +67,9 @@
|
|
|
67
67
|
"tree-sitter-php": "^0.23.4",
|
|
68
68
|
"tree-sitter-python": "^0.21.0",
|
|
69
69
|
"tree-sitter-r": "npm:@davisvaughan/tree-sitter-r@^1.3.0",
|
|
70
|
+
"tree-sitter-swift": "^0.7.1",
|
|
70
71
|
"tree-sitter-typescript": "^0.23.2",
|
|
71
|
-
"tree-sitter-wasm": "^1.1.
|
|
72
|
+
"tree-sitter-wasm": "^1.1.6",
|
|
72
73
|
"vscode-jsonrpc": "^8.2.1",
|
|
73
74
|
"vscode-languageserver-protocol": "^3.18.2",
|
|
74
75
|
"web-tree-sitter": "^0.25.10"
|
|
@@ -78,6 +79,7 @@
|
|
|
78
79
|
"@types/node": "^26.1.0",
|
|
79
80
|
"d3-force": "^3.0.0",
|
|
80
81
|
"esbuild": "^0.28.1",
|
|
82
|
+
"node-gyp": "^12.4.0",
|
|
81
83
|
"tsx": "^4.22.4",
|
|
82
84
|
"typescript": "^6.0.3"
|
|
83
85
|
},
|
|
@@ -92,6 +94,12 @@
|
|
|
92
94
|
"fsevents@2.3.3": true,
|
|
93
95
|
"tree-sitter-kotlin@0.3.8": true,
|
|
94
96
|
"tree-sitter-php@0.23.12": true,
|
|
95
|
-
"tree-sitter-r@1.3.0": true
|
|
97
|
+
"@davisvaughan/tree-sitter-r@1.3.0": true,
|
|
98
|
+
"tree-sitter-swift@0.7.1": true
|
|
99
|
+
},
|
|
100
|
+
"overrides": {
|
|
101
|
+
"tree-sitter-swift": {
|
|
102
|
+
"tree-sitter": "$tree-sitter"
|
|
103
|
+
}
|
|
96
104
|
}
|
|
97
105
|
}
|
|
@@ -48,8 +48,8 @@ for (const n of nodes) {
|
|
|
48
48
|
else if (Number(m[1]) > Number(m[2])) problems.push(`inverted span ${n.span}: ${n.id}`);
|
|
49
49
|
}
|
|
50
50
|
// an edge target may be a node id OR a deliberately-unresolved external string
|
|
51
|
-
// (import specifier / bare heritage name
|
|
52
|
-
// point at real nodes.
|
|
51
|
+
// (import specifier / bare heritage name / unresolved Java annotation type);
|
|
52
|
+
// only calls/contains must point at real nodes.
|
|
53
53
|
let dangling = 0, selfLoops = 0, unresolvedExternal = 0;
|
|
54
54
|
for (const e of edges) {
|
|
55
55
|
if (!RELATIONS.has(e.relation)) problems.push(`bad relation '${e.relation}'`);
|
|
@@ -57,7 +57,7 @@ for (const e of edges) {
|
|
|
57
57
|
if (!ids.has(e.source)) { problems.push(`dangling source: ${e.source}`); dangling++; }
|
|
58
58
|
const targetIsNode = ids.has(e.target);
|
|
59
59
|
if (!targetIsNode) {
|
|
60
|
-
if (e.relation === "imports" || e.relation === "extends" || e.relation === "implements") unresolvedExternal++;
|
|
60
|
+
if (e.relation === "imports" || e.relation === "extends" || e.relation === "implements" || e.relation === "references") unresolvedExternal++;
|
|
61
61
|
else { problems.push(`dangling ${e.relation} target: ${e.source} → ${e.target}`); dangling++; }
|
|
62
62
|
}
|
|
63
63
|
if (e.relation === "calls" && e.source === e.target) selfLoops++;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Saved vs said": how often does the agent actually TELL the user what graft saved?
|
|
3
|
+
*
|
|
4
|
+
* node scripts/tally-audit.mjs [transcript-dir] [--json]
|
|
5
|
+
*
|
|
6
|
+
* `savedTokens` (src/claude/state.ts) counts what graft *computed* — every
|
|
7
|
+
* `[graft] tokens saved ≈ N` footer the PostToolUse accumulator swept up. This
|
|
8
|
+
* script measures the other half, offline and in full detail: of the turns that
|
|
9
|
+
* used graft, how many closed with the one-line tally SKILL.md asks for, was the
|
|
10
|
+
* number right, and did it name the call count.
|
|
11
|
+
*
|
|
12
|
+
* The shipped metric (`graft_turns_bucket` / `reported_turns_bucket` on
|
|
13
|
+
* `session_summary`) is the same ratio at bucket resolution, which is all that
|
|
14
|
+
* can cross the wire. Run this locally when the aggregate says something
|
|
15
|
+
* surprising and you need to see which turns and why.
|
|
16
|
+
*
|
|
17
|
+
* Reads Claude Code's own transcript JSONL, which defaults to
|
|
18
|
+
* ~/.claude/projects/<slugified-cwd>/. Nothing leaves the machine.
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
|
|
24
|
+
const SAVED_FOOTER = /\[graft\] tokens saved ≈ ([\d,]+)/g;
|
|
25
|
+
const TALLY = /graft\s+saved\s*[~≈]?\s*([\d,.]+)\s*(k|m)?\s*(?:tok|tokens)/i;
|
|
26
|
+
const CALLS = /(\d+)\s*calls?/i;
|
|
27
|
+
/** How far past the tally to look for its call count. The tally is one line
|
|
28
|
+
* ("🌱 graft saved ~12,400 tokens this turn (3 calls)"); scanning the rest of
|
|
29
|
+
* the reply instead would match any unrelated "3 calls" further down and report
|
|
30
|
+
* near-perfect compliance for a field the agent mostly omits. */
|
|
31
|
+
const TALLY_WINDOW = 60;
|
|
32
|
+
/** Above this, a per-turn "saved" figure is an estimator artifact rather than a
|
|
33
|
+
* saving — a `--depth all` closure or a vendored tree summing whole-file
|
|
34
|
+
* baselines nobody would have read. Reported separately, never averaged in. */
|
|
35
|
+
const IMPLAUSIBLE_TOKENS = 1_000_000;
|
|
36
|
+
|
|
37
|
+
const args = process.argv.slice(2).filter((a) => a !== "--json");
|
|
38
|
+
const asJson = process.argv.includes("--json");
|
|
39
|
+
const dir = args[0] ?? defaultTranscriptDir();
|
|
40
|
+
|
|
41
|
+
function defaultTranscriptDir() {
|
|
42
|
+
const slug = process.cwd().replace(/[/.]/g, "-");
|
|
43
|
+
return join(homedir(), ".claude", "projects", slug);
|
|
44
|
+
}
|
|
45
|
+
function textOf(content) {
|
|
46
|
+
if (typeof content === "string") return content;
|
|
47
|
+
if (!Array.isArray(content)) return "";
|
|
48
|
+
return content.map((p) => (p?.type === "text" ? p.text ?? "" : "")).join("\n");
|
|
49
|
+
}
|
|
50
|
+
function isUserPrompt(o) {
|
|
51
|
+
if (o.type !== "user" || o.isSidechain || o.isMeta) return false;
|
|
52
|
+
const c = o.message?.content;
|
|
53
|
+
if (typeof c === "string") return true;
|
|
54
|
+
return Array.isArray(c) && !c.some((p) => p?.type === "tool_result");
|
|
55
|
+
}
|
|
56
|
+
function median(ns) {
|
|
57
|
+
if (!ns.length) return 0;
|
|
58
|
+
const s = [...ns].sort((a, b) => a - b);
|
|
59
|
+
const mid = s.length >> 1;
|
|
60
|
+
return s.length % 2 ? s[mid] : Math.round((s[mid - 1] + s[mid]) / 2);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!existsSync(dir)) {
|
|
64
|
+
console.error(`no transcripts at ${dir}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const turns = [];
|
|
69
|
+
for (const file of readdirSync(dir).filter((f) => f.endsWith(".jsonl"))) {
|
|
70
|
+
let turn = null;
|
|
71
|
+
const flush = () => { if (turn?.calls > 0) turns.push(turn); };
|
|
72
|
+
for (const line of readFileSync(join(dir, file), "utf8").split("\n")) {
|
|
73
|
+
if (!line.trim()) continue;
|
|
74
|
+
let o;
|
|
75
|
+
try { o = JSON.parse(line); } catch { continue; }
|
|
76
|
+
if (o.isSidechain) continue; // a subagent's prose is not what the user read
|
|
77
|
+
if (isUserPrompt(o)) { flush(); turn = { file, calls: 0, saved: 0, said: null, statedCalls: null }; continue; }
|
|
78
|
+
if (!turn) continue;
|
|
79
|
+
if (o.type === "user") { // a tool_result wearing the user role
|
|
80
|
+
for (const m of JSON.stringify(o.message?.content ?? "").matchAll(SAVED_FOOTER)) {
|
|
81
|
+
turn.calls++;
|
|
82
|
+
turn.saved += Number(m[1].replace(/,/g, "")) || 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (o.type === "assistant") {
|
|
86
|
+
const m = TALLY.exec(textOf(o.message?.content));
|
|
87
|
+
if (m) {
|
|
88
|
+
let n = Number(m[1].replace(/,/g, ""));
|
|
89
|
+
if (m[2]?.toLowerCase() === "k") n *= 1e3;
|
|
90
|
+
if (m[2]?.toLowerCase() === "m") n *= 1e6;
|
|
91
|
+
turn.said = n;
|
|
92
|
+
const c = CALLS.exec(textOf(o.message?.content).slice(m.index, m.index + TALLY_WINDOW));
|
|
93
|
+
turn.statedCalls = c ? Number(c[1]) : null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
flush();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const said = turns.filter((t) => t.said !== null);
|
|
101
|
+
const plausible = turns.filter((t) => t.saved < IMPLAUSIBLE_TOKENS);
|
|
102
|
+
const outliers = turns.filter((t) => t.saved >= IMPLAUSIBLE_TOKENS);
|
|
103
|
+
const fidelity = { exact: 0, over: 0, under: 0 };
|
|
104
|
+
for (const t of said.filter((t) => t.saved > 0)) {
|
|
105
|
+
const r = t.said / t.saved;
|
|
106
|
+
if (r >= 0.9 && r <= 1.1) fidelity.exact++;
|
|
107
|
+
else if (r > 1.1) fidelity.over++;
|
|
108
|
+
else fidelity.under++;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const report = {
|
|
112
|
+
transcripts: readdirSync(dir).filter((f) => f.endsWith(".jsonl")).length,
|
|
113
|
+
graft_turns: turns.length,
|
|
114
|
+
reported: said.length,
|
|
115
|
+
silent: turns.length - said.length,
|
|
116
|
+
reported_pct: turns.length ? Number(((said.length / turns.length) * 100).toFixed(1)) : 0,
|
|
117
|
+
stated_call_count: said.filter((t) => t.statedCalls !== null).length,
|
|
118
|
+
median_saved_per_turn: median(plausible.map((t) => t.saved)),
|
|
119
|
+
fidelity,
|
|
120
|
+
implausible_turns: outliers.length,
|
|
121
|
+
implausible_max: outliers.length ? Math.max(...outliers.map((t) => t.saved)) : 0,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (asJson) {
|
|
125
|
+
console.log(JSON.stringify(report, null, 2));
|
|
126
|
+
} else {
|
|
127
|
+
const { graft_turns: n } = report;
|
|
128
|
+
console.log(`transcripts scanned: ${report.transcripts} (${dir})`);
|
|
129
|
+
console.log(`turns that used graft: ${n}`);
|
|
130
|
+
console.log(` ...reported a tally: ${report.reported} (${report.reported_pct}%)`);
|
|
131
|
+
console.log(` ...silent: ${report.silent}`);
|
|
132
|
+
console.log(` ...named the call count ${report.stated_call_count}/${report.reported}`);
|
|
133
|
+
console.log(`median saved per turn: ${report.median_saved_per_turn.toLocaleString()} tok`);
|
|
134
|
+
console.log(`reported number vs footer sum (±10%): exact ${fidelity.exact} · over ${fidelity.over} · under ${fidelity.under}`);
|
|
135
|
+
if (outliers.length) {
|
|
136
|
+
console.log(`\n⚠ ${outliers.length} turn(s) claim ≥${IMPLAUSIBLE_TOKENS.toLocaleString()} tokens saved ` +
|
|
137
|
+
`(max ${report.implausible_max.toLocaleString()}) — an estimator artifact, excluded from the median. ` +
|
|
138
|
+
`See savingsFor() in src/context/savings.ts.`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { ChatModel, ChatRequest, ChatResponse } from "./types.js";
|
|
2
|
-
export interface ClaudeCliResult {
|
|
3
|
-
code: number | null;
|
|
4
|
-
stdout: string;
|
|
5
|
-
stderr: string;
|
|
6
|
-
}
|
|
7
|
-
/** Injectable process runner — tests pass a stub so nothing is ever spawned. */
|
|
8
|
-
export type ClaudeCliRunner = (args: string[], stdin: string, timeoutMs: number) => Promise<ClaudeCliResult>;
|
|
9
|
-
export interface ClaudeCliChatModelOptions {
|
|
10
|
-
/** Model alias or full id (`sonnet`, `opus`, `claude-sonnet-5`, …). */
|
|
11
|
-
model: string;
|
|
12
|
-
/** Path to the CLI. Default: resolved from PATH (env GRAFT_CLAUDE_BIN wins). */
|
|
13
|
-
bin?: string;
|
|
14
|
-
label?: string;
|
|
15
|
-
/** Per-call wall clock. Default 300s — a crux pass over a big file is slow. */
|
|
16
|
-
timeoutMs?: number;
|
|
17
|
-
/** Optional spend ceiling passed through as `--max-budget-usd`. */
|
|
18
|
-
maxBudgetUsd?: number;
|
|
19
|
-
/** Inject a runner (tests stub it; production omits it). */
|
|
20
|
-
run?: ClaudeCliRunner;
|
|
21
|
-
}
|
|
22
|
-
export declare class ClaudeCliChatModel implements ChatModel {
|
|
23
|
-
readonly label: string;
|
|
24
|
-
private model;
|
|
25
|
-
private runner;
|
|
26
|
-
private timeoutMs;
|
|
27
|
-
private maxBudgetUsd?;
|
|
28
|
-
constructor(opts: ClaudeCliChatModelOptions);
|
|
29
|
-
create(req: ChatRequest): Promise<ChatResponse>;
|
|
30
|
-
/**
|
|
31
|
-
* Transport-level retry. A `--deep` build is thousands of sequential calls over
|
|
32
|
-
* hours, so a single blip — an overloaded upstream, a dropped connection, one
|
|
33
|
-
* slow file hitting the wall clock — would otherwise silently cost that file its
|
|
34
|
-
* summary. Only failures classified transient are retried; a bad login or an
|
|
35
|
-
* exhausted quota fails immediately, because sleeping on those just turns a
|
|
36
|
-
* clear error into a slow one.
|
|
37
|
-
*/
|
|
38
|
-
private invoke;
|
|
39
|
-
private invokeOnce;
|
|
40
|
-
}
|
|
41
|
-
export declare function isTransient(message: string): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Pull one JSON value out of a model reply. Handles the two ways a prompt-enforced
|
|
44
|
-
* schema leaks: a markdown fence around the object, and prose either side of it.
|
|
45
|
-
* Brace scanning is string- and escape-aware so a `}` inside a summary can't end
|
|
46
|
-
* the object early. Returns undefined when nothing parses.
|
|
47
|
-
*/
|
|
48
|
-
export declare function extractJson(text: string): unknown;
|
|
49
|
-
/**
|
|
50
|
-
* Resolve the CLI without a shell. Node's spawn only appends `.exe` on Windows,
|
|
51
|
-
* so PATHEXT is walked by hand — the native installer ships `claude.exe` but an
|
|
52
|
-
* `npm i -g` install leaves only the `claude.cmd` shim, and missing it would make
|
|
53
|
-
* this provider look uninstalled on a machine where it works fine.
|
|
54
|
-
*/
|
|
55
|
-
export declare function resolveClaudeBin(explicit?: string): string | undefined;
|
|
56
|
-
/** True when the `claude` CLI is installed. Cached — this runs per build, not per file. */
|
|
57
|
-
export declare function claudeCliAvailable(explicit?: string): boolean;
|
|
58
|
-
/** Test seam: drop the memoized lookup so a test can flip PATH. */
|
|
59
|
-
export declare function resetClaudeCliCache(): void;
|
|
60
|
-
//# sourceMappingURL=claude-cli.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"claude-cli.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/claude-cli.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAA4B,MAAM,YAAY,CAAC;AAgBjG,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAE7G,MAAM,WAAW,yBAAyB;IACxC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,GAAG,CAAC,EAAE,eAAe,CAAC;CACvB;AAiBD,qBAAa,kBAAmB,YAAW,SAAS;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAC,CAAS;gBAElB,IAAI,EAAE,yBAAyB;IAQrC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAgDrD;;;;;;;OAOG;YACW,MAAM;YAeN,UAAU;CAmCzB;AAeD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAGpD;AAkFD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA0CjD;AAcD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgBtE;AAID,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAI7D;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
|