@nanonets/graft 0.13.0 → 0.16.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 +26 -10
- package/dist/ai/crux.d.ts +14 -1
- package/dist/ai/crux.d.ts.map +1 -1
- package/dist/ai/crux.js +39 -51
- package/dist/ai/crux.js.map +1 -1
- package/dist/ai/failure.d.ts +13 -2
- package/dist/ai/failure.d.ts.map +1 -1
- package/dist/ai/failure.js +16 -4
- package/dist/ai/failure.js.map +1 -1
- package/dist/ai/llm/factory.d.ts +10 -1
- package/dist/ai/llm/factory.d.ts.map +1 -1
- package/dist/ai/llm/factory.js +16 -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/orcarouter.d.ts +34 -0
- package/dist/ai/llm/orcarouter.d.ts.map +1 -0
- package/dist/ai/llm/orcarouter.js +45 -0
- package/dist/ai/llm/orcarouter.js.map +1 -0
- 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 +15 -3
- 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 +139 -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 +21 -30
- 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/name.d.ts +13 -0
- package/dist/blast/name.d.ts.map +1 -1
- package/dist/blast/name.js +36 -0
- package/dist/blast/name.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 +132 -22
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.d.ts +1 -0
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +1 -1
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/session-metrics.d.ts +63 -0
- package/dist/claude/session-metrics.d.ts.map +1 -0
- package/dist/claude/session-metrics.js +181 -0
- package/dist/claude/session-metrics.js.map +1 -0
- package/dist/claude/settings-merge.d.ts +18 -1
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +70 -27
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/state.d.ts +25 -0
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js +14 -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 +145 -4
- package/dist/cli.js.map +1 -1
- package/dist/context/build.d.ts +11 -0
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +49 -9
- package/dist/context/build.js.map +1 -1
- package/dist/context/check.d.ts.map +1 -1
- package/dist/context/check.js +5 -12
- 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 +70 -3
- package/dist/context/node-file.js.map +1 -1
- package/dist/context/savings.d.ts +8 -0
- package/dist/context/savings.d.ts.map +1 -1
- package/dist/context/savings.js +14 -0
- package/dist/context/savings.js.map +1 -1
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +2 -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/cards.d.ts +4 -3
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +34 -6
- package/dist/graph/cards.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/enrich.d.ts +4 -4
- package/dist/graph/enrich.d.ts.map +1 -1
- package/dist/graph/enrich.js +32 -6
- package/dist/graph/enrich.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 +10 -0
- package/dist/graph/generic.d.ts.map +1 -1
- package/dist/graph/generic.js +20 -3
- 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/graph/workspace.d.ts +2 -0
- package/dist/graph/workspace.d.ts.map +1 -1
- package/dist/graph/workspace.js +173 -6
- package/dist/graph/workspace.js.map +1 -1
- package/dist/hosts/antigravity.d.ts +2 -2
- package/dist/hosts/antigravity.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.d.ts +2 -6
- package/dist/hosts/codex-hooks.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.js +7 -30
- package/dist/hosts/codex-hooks.js.map +1 -1
- package/dist/hosts/config-write.d.ts +36 -0
- package/dist/hosts/config-write.d.ts.map +1 -0
- package/dist/hosts/config-write.js +63 -0
- package/dist/hosts/config-write.js.map +1 -0
- package/dist/hosts/cursor-hooks.d.ts +18 -0
- package/dist/hosts/cursor-hooks.d.ts.map +1 -0
- package/dist/hosts/cursor-hooks.js +100 -0
- package/dist/hosts/cursor-hooks.js.map +1 -0
- package/dist/hosts/init.d.ts +2 -2
- package/dist/hosts/init.d.ts.map +1 -1
- package/dist/hosts/init.js +8 -2
- package/dist/hosts/init.js.map +1 -1
- package/dist/hosts/mcp-config.d.ts +15 -5
- package/dist/hosts/mcp-config.d.ts.map +1 -1
- package/dist/hosts/mcp-config.js +51 -15
- package/dist/hosts/mcp-config.js.map +1 -1
- package/dist/hosts/plan.d.ts.map +1 -1
- package/dist/hosts/plan.js +2 -0
- package/dist/hosts/plan.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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/fs.d.ts +39 -5
- package/dist/ingest/fs.d.ts.map +1 -1
- package/dist/ingest/fs.js +104 -11
- package/dist/ingest/fs.js.map +1 -1
- package/dist/mcp/tool-names.d.ts +20 -0
- package/dist/mcp/tool-names.d.ts.map +1 -0
- package/dist/mcp/tool-names.js +36 -0
- package/dist/mcp/tool-names.js.map +1 -0
- package/dist/mcp/tools.d.ts +11 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +9 -21
- package/dist/mcp/tools.js.map +1 -1
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +17 -3
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/flush.d.ts.map +1 -1
- package/dist/telemetry/flush.js +1 -0
- package/dist/telemetry/flush.js.map +1 -1
- package/dist/telemetry/identity.d.ts +7 -0
- package/dist/telemetry/identity.d.ts.map +1 -1
- package/dist/telemetry/identity.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/sessions.d.ts +19 -2
- package/dist/telemetry/sessions.d.ts.map +1 -1
- package/dist/telemetry/sessions.js +57 -34
- package/dist/telemetry/sessions.js.map +1 -1
- package/dist/telemetry/track.d.ts +22 -3
- package/dist/telemetry/track.d.ts.map +1 -1
- package/dist/telemetry/track.js +34 -5
- package/dist/telemetry/track.js.map +1 -1
- package/dist/upkeep-run.js +1 -1
- package/dist/upkeep-run.js.map +1 -1
- package/dist/upkeep.d.ts +7 -4
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +2 -1
- package/dist/upkeep.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 +12 -4
- package/scripts/graph-quality.mjs +3 -3
- package/scripts/postinstall.mjs +42 -3
- package/scripts/run-tests.mjs +14 -0
- 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/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
### Turbocharge Claude Code, Cursor, Codex, Gemini & every coding agent: faster, cheaper, with contextual understanding specific to your codebase.
|
|
6
6
|
|
|
7
|
+
<a href="https://trendshift.io/repositories/92209?utm_source=trendshift-badge&utm_medium=badge&utm_campaign=badge-trendshift-92209" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/trendshift/repositories/92209/daily?language=TypeScript" alt="trailhq/Graft | Trendshift" width="250" height="55"/></a>
|
|
8
|
+
|
|
7
9
|
<p>
|
|
8
10
|
<a href="https://github.com/NanoNets/Graft"><img src="https://img.shields.io/github/stars/NanoNets/Graft?style=for-the-badge&logo=github&logoColor=white&label=Star%20on%20GitHub&color=FFC83D" /></a>
|
|
9
11
|
<a href="https://graft.nanonets.ai"><img src="https://img.shields.io/badge/website-graft.nanonets.ai-546FFF?style=for-the-badge" /></a>
|
|
@@ -15,6 +17,7 @@
|
|
|
15
17
|
<img src="https://img.shields.io/badge/License-MIT-20C997?style=for-the-badge" />
|
|
16
18
|
<a href="TELEMETRY.md"><img src="https://img.shields.io/badge/telemetry-anonymous%2C%20opt--out-546FFF?style=for-the-badge" /></a>
|
|
17
19
|
<a href="https://scorecard.dev/viewer/?uri=github.com/NanoNets/Graft"><img src="https://img.shields.io/ossf-scorecard/github.com/NanoNets/Graft?style=for-the-badge&label=openssf%20scorecard" /></a>
|
|
20
|
+
<a href="https://github.com/apps/trailhq-graft"><img src="https://img.shields.io/badge/PR%20reviews-install%20GitHub%20App-546FFF?style=for-the-badge&logo=github&logoColor=white" /></a>
|
|
18
21
|
</p>
|
|
19
22
|
|
|
20
23
|
### Up to **4× cheaper** and **3× faster**, with better or no loss of correctness.
|
|
@@ -26,10 +29,10 @@
|
|
|
26
29
|
| Time savings | Baseline | **+60%** |
|
|
27
30
|
| Correctness | 54% | **66% (+12 pts)** |
|
|
28
31
|
|
|
29
|
-
<sub>Efficiency is a 162-run controlled benchmark (same agent, same file tools, only the context differs). Correctness is **SWE-bench Verified**, graded by the official harness — graft resolved 66% of instances tested against Cold Claude Code's 54%. [Efficiency method ↓](#benchmark) · [SWE-bench ↓](#swe-bench-verified) · [Per-repo numbers ↓](#tested-on-your-popular-repos)</sub>
|
|
30
|
-
|
|
31
32
|
</div>
|
|
32
33
|
|
|
34
|
+
<p align="center"><b>Reviewing pull requests?</b> <a href="https://github.com/apps/trailhq-graft">Add the graft GitHub App</a> to get an automatic blast-radius review on every PR.</p>
|
|
35
|
+
|
|
33
36
|
<p align="center">
|
|
34
37
|
<img src="assets/graft-comparison-demo.gif" alt="Side-by-side comparison of a coding agent working with and without graft" width="820"/>
|
|
35
38
|
</p>
|
|
@@ -107,7 +110,7 @@ Graft builds that understanding **once** and writes it into your repo as a folde
|
|
|
107
110
|
- **A real graph you can read.** No embeddings, no similarity search, no index to keep warm. The graph is a set of linked files your agent opens, greps, and follows, exactly the way it reads any other file in the repo.
|
|
108
111
|
- **A local cache, not a committed artifact.** `graft build` writes `graft/` and adds it to `.gitignore` — it's a regenerable local cache, like `node_modules`. What you commit is the small wiring `graft init` drops in (`.claude/`, `AGENTS.md`, the MCP config); each teammate runs `graft build` to generate their own graph. No database, no server, no setup.
|
|
109
112
|
- **Always fresh, automatically.** Every query rebuilds the graph against the working tree first — structural, `$0`, ~3ms when nothing moved — so `ask`/`grep`/`callers`/`skeleton`/`map` describe the code as it is right now, including uncommitted edits. `graft check` is a local freshness signal; there's no stale index to babysit.
|
|
110
|
-
- **Your provider, your key, your model.** Summaries are written by any provider you choose — OpenAI, Anthropic (native), OpenRouter, Fireworks, Groq, a LiteLLM proxy, or a local model — under your own key. The structural code graph (`graft build`, `graft check`) is deterministic tree-sitter and never calls a model at all.
|
|
113
|
+
- **Your provider, your key, your model.** Summaries are written by any provider you choose — OpenAI, Anthropic (native), OpenRouter, Fireworks, Groq, OrcaRouter, a LiteLLM proxy, or a local model — under your own key. The structural code graph (`graft build`, `graft check`) is deterministic tree-sitter and never calls a model at all.
|
|
111
114
|
|
|
112
115
|
<p align="center">
|
|
113
116
|
<picture>
|
|
@@ -193,13 +196,15 @@ compiler-grade layer — all `$0` and deterministic (no model, no key):
|
|
|
193
196
|
- **Full-fidelity** — hand-written extractors with scope-aware, cross-file call
|
|
194
197
|
and import resolution:
|
|
195
198
|
**TypeScript / JavaScript** (incl. JSX & TSX), **Python**, **Go**, **Java**,
|
|
196
|
-
**
|
|
197
|
-
|
|
199
|
+
**Kotlin**, **PHP**, **Swift** (classes, structs, enums, actors, protocols;
|
|
200
|
+
extension members attach to the extended type), **R** (`.R`/`.r` — plain
|
|
201
|
+
functions, S3/S4/R6 classes and methods, roxygen `@export`,
|
|
202
|
+
`library()`/`source()` imports).
|
|
198
203
|
|
|
199
204
|
- **Broad** — symbols (functions, classes, methods, types, …) plus name-resolved
|
|
200
205
|
call edges via a generic tree-sitter extractor, one grammar per language:
|
|
201
|
-
**Rust, C, C++, C#, Ruby,
|
|
202
|
-
OCaml, Zig, Dart, Clojure, Lua**.
|
|
206
|
+
**Rust, C, C++, C#, Ruby, Scala, Elixir, Solidity,
|
|
207
|
+
OCaml, Zig, Dart, Clojure, Nix, Lua**.
|
|
203
208
|
|
|
204
209
|
- **Compiler-grade edges (opt-in)** — `graft build --lsp` adds precise
|
|
205
210
|
`lsp_resolved` call edges (member calls the static pass can't type) when a
|
|
@@ -207,7 +212,7 @@ compiler-grade layer — all `$0` and deterministic (no model, no key):
|
|
|
207
212
|
**gopls** (Go), **pyright** (Python), **typescript-language-server** (TS/JS).
|
|
208
213
|
It's best-effort — with no server installed the graph is unchanged.
|
|
209
214
|
|
|
210
|
-
Twenty-
|
|
215
|
+
Twenty-three languages in total. A file whose language isn't listed is skipped, not
|
|
211
216
|
indexed. Adding a broad-tier language is a small contribution — see
|
|
212
217
|
[CREDITS.md](CREDITS.md) for the folks who added the current set.
|
|
213
218
|
|
|
@@ -238,7 +243,7 @@ _Summary, sources, links, and notes ship today in markdown nodes. The crux ships
|
|
|
238
243
|
## What runs where
|
|
239
244
|
|
|
240
245
|
- **On your machine, no key, no network:** the structural code graph. `graft build` (wiring graph + per-file cards), `graft check`, and `graft ask` are deterministic tree-sitter — they never call a model.
|
|
241
|
-
- **Through your provider key:** the LLM-written parts — `graft build --deep` adds the concept nodes (file summaries + node synthesis) and the per-symbol summaries and cruxes. graft is vendor-neutral: set `GRAFT_PROVIDER` (`openai` for any OpenAI-compatible endpoint,
|
|
246
|
+
- **Through your provider key:** the LLM-written parts — `graft build --deep` adds the concept nodes (file summaries + node synthesis) and the per-symbol summaries and cruxes. graft is vendor-neutral: set `GRAFT_PROVIDER` (`openai` for any OpenAI-compatible endpoint, `anthropic` for the native API, or `litellm` / `orcarouter` for a gateway that speaks the OpenAI-compatible format), your `GRAFT_API_KEY`, `GRAFT_MODEL`, and — for the `openai` wire format — `GRAFT_BASE_URL` to point at OpenRouter, Fireworks, Groq, a LiteLLM proxy, a local server, or OpenAI itself. Or pass `--provider/--model/--api-key/--base-url` on the command line. (`OPENROUTER_API_KEY` still works as a deprecated fallback, and `ORCAROUTER_API_KEY` as a second one.)
|
|
242
247
|
- **Anonymous usage stats** — the only network calls are the LLM requests you configured, a daily npm version check, and one batched usage ping. The ping carries buckets and fixed labels only: never your code, file paths, repo name, symbols, queries, or error messages. [`TELEMETRY.md`](TELEMETRY.md) is the complete list and `graft telemetry debug` prints exactly what your machine would send. Turn it off with `graft telemetry disable`, `DO_NOT_TRACK=1`, or by unchecking the box in `graft init`; it is off in CI and in any build from source.
|
|
243
248
|
|
|
244
249
|
See [`.env.example`](.env.example) for the full list of settings (model, base URL, graph directory).
|
|
@@ -269,6 +274,7 @@ With no TTY to prompt on — CI, a Dockerfile, a piped shell — `init` writes *
|
|
|
269
274
|
| `--list-agents` | print the known agent ids and exit |
|
|
270
275
|
| `--no-mcp` | skip MCP server registration |
|
|
271
276
|
| `--no-hooks` | skip hook installation |
|
|
277
|
+
| `--no-statusline` | skip writing Claude Code `statusLine` (same as `GRAFT_NO_STATUSLINE=1`) |
|
|
272
278
|
| `--no-global` | skip writes outside this repo (the `~/.codex/` entries below) |
|
|
273
279
|
|
|
274
280
|
#### Writes outside the repo
|
|
@@ -322,7 +328,7 @@ Where a CLI agent supports user-level `hooks.json`, `init` also installs Graft's
|
|
|
322
328
|
<br/><sub>edit a file → blast radius appears inline → graph auto-resyncs → confirmed in <code>graft viz</code></sub>
|
|
323
329
|
</p>
|
|
324
330
|
|
|
325
|
-
`graft init` is idempotent and never clobbers your existing `.claude/settings.json` — it merges its blocks and leaves the rest alone. Want the LLM summaries too? Run `graft build --deep` (with a key) whenever you like; auto-sync will never do it for you.
|
|
331
|
+
`graft init` is idempotent and never clobbers your existing `.claude/settings.json` — it merges its blocks and leaves the rest alone. A `statusLine` that is not Graft's (anything whose command does not name `graft-statusline.cjs`) is left untouched; re-running `init` will refresh Graft's own helper command if it is already installed. Pass `--no-statusline` (or `GRAFT_NO_STATUSLINE=1`) to skip installing one at all — a project-level `statusLine` would otherwise hide a custom one in `~/.claude/settings.json`. Want the LLM summaries too? Run `graft build --deep` (with a key) whenever you like; auto-sync will never do it for you.
|
|
326
332
|
|
|
327
333
|
---
|
|
328
334
|
|
|
@@ -335,6 +341,8 @@ graft build --extensions .ts .py # only include these code extensions
|
|
|
335
341
|
graft build --no-reuse # re-parse every file instead of replaying unchanged ones from cache
|
|
336
342
|
graft build --follow-submodules # include initialized submodules; persist the choice for builds + MCP refresh
|
|
337
343
|
graft build --no-follow-submodules # exclude submodules again and persist that choice (the default)
|
|
344
|
+
graft build --follow-nested-repos # include nested git clones the index doesn't track; persist the choice
|
|
345
|
+
graft build --no-follow-nested-repos # exclude nested clones again and persist that choice (the default)
|
|
338
346
|
|
|
339
347
|
graft ask "<task>" [dir] # query the graph — ranked nodes + exact file:line (no LLM, no key)
|
|
340
348
|
graft ask "<task>" --json # machine-readable result
|
|
@@ -358,6 +366,7 @@ graft blast --base origin/main # diff against the merge base with HEAD —
|
|
|
358
366
|
graft blast --format markdown # a PR comment: the areas a change can reach, per-symbol detail collapsed under it
|
|
359
367
|
graft blast --base origin/main --name # name those areas with one cached LLM call, instead of a full --deep build
|
|
360
368
|
graft blast --export-viz site/ # also write the interactive page for this radius (what a PR comment links to)
|
|
369
|
+
graft blast --no-owners # skip "who to tag" — by default git history names the people behind each area
|
|
361
370
|
graft blast --depth all --format json # the full transitive closure, machine-readable
|
|
362
371
|
|
|
363
372
|
graft check [dir] # fail (exit 1) if graft/ has drifted from the code (never auto-refreshes — it's the drift report)
|
|
@@ -377,10 +386,16 @@ graft init --dry-run # list every file it would touch, then exit
|
|
|
377
386
|
graft init --agents cursor kiro # wire only these agents, no prompt (ids: agents, cursor, gemini, grok, copilot, kiro, windsurf, adal, claude)
|
|
378
387
|
graft init --yes # no prompt; wire every detected agent
|
|
379
388
|
graft init --no-global # skip writes outside this repo (~/.codex/ config + hooks)
|
|
389
|
+
graft init --no-statusline # skip Claude Code statusLine (same as GRAFT_NO_STATUSLINE=1)
|
|
380
390
|
graft init --no-build # wire the files only; don't build the graph
|
|
381
391
|
graft init --all-agents # wire every known agent, detected or not
|
|
382
392
|
graft init --list-agents # list known agent ids and exit
|
|
383
393
|
|
|
394
|
+
graft uninstall [dir] # remove every file and config entry graft wrote here (the inverse of init)
|
|
395
|
+
graft uninstall -y # actually remove (without -y it prints what it would remove and exits)
|
|
396
|
+
graft uninstall --keep-cache # wiring only; leave graft/ and the .gitignore entry
|
|
397
|
+
graft uninstall --no-global # leave out-of-repo files alone (~/.codex, ~/.gemini)
|
|
398
|
+
|
|
384
399
|
graft version # print the installed + latest published npm version
|
|
385
400
|
graft upgrade # npm install -g the latest published version
|
|
386
401
|
# a new version is announced automatically (checked once a day);
|
|
@@ -447,6 +462,7 @@ Graft supports these layouts:
|
|
|
447
462
|
is saved in `.graft/config.json`, so later no-flag builds and MCP automatic
|
|
448
463
|
refreshes behave the same way. Run `graft build --no-follow-submodules` to
|
|
449
464
|
restore and persist the default boundary.
|
|
465
|
+
- **A git repo with other repos cloned inside it** (no gitlink, no index entry) — the shape multi-repo manifest tools like `west`, `repo`, `gclient` and `tsrc` check dependencies out into, and the shape you get by cloning an upstream into the tree to patch it locally. `--follow-submodules` cannot reach these: they have no `160000` index entry to follow. Run `graft build --follow-nested-repos` to fold them into one graph, prefixing child paths (for example, `external/parser/src/index.ts`) while honoring each clone's own Git ignore rules. A clone at a git-ignored path stays absent, since Git never reports it. The choice is saved in `.graft/config.json` and is independent of `--follow-submodules` — neither flag implies the other. Run `graft build --no-follow-nested-repos` to restore and persist the default boundary. Prefer this over the multi-repo split below when the nested repos import from each other and you want those edges in one graph; prefer the split when you want each repo scored and refreshed on its own.
|
|
450
466
|
- **A folder of separate git repos** (no `.git` at the top) — `graft build`
|
|
451
467
|
auto-splits: each child gets its own (git-ignored) `graft/`, and the parent
|
|
452
468
|
gets a `graft/workspace.json` index. Queries from the parent federate across
|
package/dist/ai/crux.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* sees each symbol's neighbours, which sharpens the summaries. Line numbers are
|
|
15
15
|
* consumed once, at write time, to slice the crux text verbatim from source.
|
|
16
16
|
*/
|
|
17
|
-
import type { ChatModel } from "./llm/types.js";
|
|
17
|
+
import type { ChatModel, ChatResponse } from "./llm/types.js";
|
|
18
18
|
import type { Kind } from "../graph/types.js";
|
|
19
19
|
/** One definition we want described, located by its line span within the file. */
|
|
20
20
|
export interface NodeRef {
|
|
@@ -37,10 +37,23 @@ export interface NodeCrux {
|
|
|
37
37
|
}
|
|
38
38
|
export interface CruxSummarizer {
|
|
39
39
|
describeFile(input: FileCruxInput): Promise<NodeCrux[]>;
|
|
40
|
+
/** Set by {@link ChatCruxSummarizer} after each call; optional on fakes. */
|
|
41
|
+
lastMiss?: CruxMiss | null;
|
|
40
42
|
}
|
|
43
|
+
/** Why a crux call produced no usable summaries (#235). */
|
|
44
|
+
export type CruxMissKind = "empty-toolCalls" | "unparseable" | "truncated" | "empty-parsed";
|
|
45
|
+
export interface CruxMiss {
|
|
46
|
+
kind: CruxMissKind;
|
|
47
|
+
finishReason: string | null;
|
|
48
|
+
}
|
|
49
|
+
/** Classify an empty/unusable crux reply. `null` means at least one usable summary. */
|
|
50
|
+
export declare function classifyCruxMiss(res: ChatResponse, parsed: NodeCrux[]): CruxMiss | null;
|
|
51
|
+
/** Per-file error text: miss class + the provider's finish_reason (#235). */
|
|
52
|
+
export declare function formatCruxMiss(kind: CruxMissKind, finishReason: string | null): string;
|
|
41
53
|
/** Crux summarizer backed by any {@link ChatModel} via forced tool calling. */
|
|
42
54
|
export declare class ChatCruxSummarizer implements CruxSummarizer {
|
|
43
55
|
private model;
|
|
56
|
+
lastMiss: CruxMiss | null;
|
|
44
57
|
constructor(model: ChatModel);
|
|
45
58
|
describeFile(input: FileCruxInput): Promise<NodeCrux[]>;
|
|
46
59
|
}
|
package/dist/ai/crux.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crux.d.ts","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"crux.d.ts","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,kFAAkF;AAClF,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG,aAAa,GAAG,WAAW,GAAG,cAAc,CAAC;AAE5F,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAQD,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,GAAG,IAAI,CASvF;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGtF;AA+FD,+EAA+E;AAC/E,qBAAa,kBAAmB,YAAW,cAAc;IAG3C,OAAO,CAAC,KAAK;IAFzB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;gBAEb,KAAK,EAAE,SAAS;IAE9B,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAuB9D"}
|
package/dist/ai/crux.js
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
import { recoverToolArgsFromContent, warnToolChoiceIgnored } from "./llm/recover-tool.js";
|
|
2
|
+
function isTruncatedStop(reason) {
|
|
3
|
+
if (!reason)
|
|
4
|
+
return false;
|
|
5
|
+
const r = reason.toLowerCase();
|
|
6
|
+
return r === "length" || r === "max_tokens";
|
|
7
|
+
}
|
|
8
|
+
/** Classify an empty/unusable crux reply. `null` means at least one usable summary. */
|
|
9
|
+
export function classifyCruxMiss(res, parsed) {
|
|
10
|
+
const finishReason = res.stopReason;
|
|
11
|
+
if (parsed.some((p) => p.summary.trim()))
|
|
12
|
+
return null;
|
|
13
|
+
if (isTruncatedStop(finishReason))
|
|
14
|
+
return { kind: "truncated", finishReason };
|
|
15
|
+
if (parsed.length > 0)
|
|
16
|
+
return { kind: "empty-parsed", finishReason };
|
|
17
|
+
const emptyTools = res.toolCalls.length === 0;
|
|
18
|
+
const emptyText = !res.text?.trim();
|
|
19
|
+
if (emptyTools && emptyText)
|
|
20
|
+
return { kind: "empty-toolCalls", finishReason };
|
|
21
|
+
return { kind: "unparseable", finishReason };
|
|
22
|
+
}
|
|
23
|
+
/** Per-file error text: miss class + the provider's finish_reason (#235). */
|
|
24
|
+
export function formatCruxMiss(kind, finishReason) {
|
|
25
|
+
const fr = finishReason == null || finishReason === "" ? "null" : finishReason;
|
|
26
|
+
return `model returned no usable symbol summaries [${kind}, finish_reason=${fr}]`;
|
|
27
|
+
}
|
|
1
28
|
const SYSTEM_PROMPT = `You explain code definitions for a code graph that helps engineers navigate a codebase.
|
|
2
29
|
|
|
3
30
|
You are given ONE source file with 1-based line numbers, and a list of TARGET definitions in it. Describe EVERY target via the record_symbols tool.
|
|
@@ -71,64 +98,23 @@ function argsFromResponse(res) {
|
|
|
71
98
|
if (call?.args && typeof call.args === "object" && !Array.isArray(call.args)) {
|
|
72
99
|
return call.args;
|
|
73
100
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (!
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const asSymbols = (value) => {
|
|
82
|
-
if (!value || typeof value !== "object")
|
|
83
|
-
return undefined;
|
|
84
|
-
if (Array.isArray(value)) {
|
|
85
|
-
for (const item of value) {
|
|
86
|
-
if (!item || typeof item !== "object")
|
|
87
|
-
continue;
|
|
88
|
-
const rec = item;
|
|
89
|
-
const name = typeof rec.name === "string" ? rec.name : "";
|
|
90
|
-
// Accept the crux tool, or a generic emit_json wrapper some gateways use.
|
|
91
|
-
if (name && name !== RECORD_TOOL && name !== "emit_json")
|
|
92
|
-
continue;
|
|
93
|
-
const params = rec.parameters ?? rec.arguments ?? rec.args;
|
|
94
|
-
const hit = asSymbols(params);
|
|
95
|
-
if (hit)
|
|
96
|
-
return hit;
|
|
97
|
-
}
|
|
98
|
-
return undefined;
|
|
99
|
-
}
|
|
100
|
-
const obj = value;
|
|
101
|
-
if (Array.isArray(obj.symbols))
|
|
102
|
-
return obj;
|
|
103
|
-
return undefined;
|
|
104
|
-
};
|
|
105
|
-
try {
|
|
106
|
-
const hit = asSymbols(JSON.parse(stripped));
|
|
107
|
-
if (hit)
|
|
108
|
-
return hit;
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
/* try bracket slice below */
|
|
112
|
-
}
|
|
113
|
-
const start = stripped.indexOf("{");
|
|
114
|
-
const end = stripped.lastIndexOf("}");
|
|
115
|
-
if (start >= 0 && end > start) {
|
|
116
|
-
try {
|
|
117
|
-
return asSymbols(JSON.parse(stripped.slice(start, end + 1)));
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return undefined;
|
|
101
|
+
const recovered = recoverToolArgsFromContent(res.text, {
|
|
102
|
+
toolNames: [RECORD_TOOL, "emit_json"],
|
|
103
|
+
payloadKey: "symbols",
|
|
104
|
+
});
|
|
105
|
+
if (!recovered)
|
|
106
|
+
warnToolChoiceIgnored("crux", res.text?.trim() ? "unparsed" : "empty");
|
|
107
|
+
return recovered;
|
|
124
108
|
}
|
|
125
109
|
/** Crux summarizer backed by any {@link ChatModel} via forced tool calling. */
|
|
126
110
|
export class ChatCruxSummarizer {
|
|
127
111
|
model;
|
|
112
|
+
lastMiss = null;
|
|
128
113
|
constructor(model) {
|
|
129
114
|
this.model = model;
|
|
130
115
|
}
|
|
131
116
|
async describeFile(input) {
|
|
117
|
+
this.lastMiss = null;
|
|
132
118
|
if (input.nodes.length === 0)
|
|
133
119
|
return [];
|
|
134
120
|
const res = await this.model.create({
|
|
@@ -147,7 +133,9 @@ export class ChatCruxSummarizer {
|
|
|
147
133
|
{ role: "user", content: userContent(input) },
|
|
148
134
|
],
|
|
149
135
|
});
|
|
150
|
-
|
|
136
|
+
const parsed = parseResults(argsFromResponse(res));
|
|
137
|
+
this.lastMiss = classifyCruxMiss(res, parsed);
|
|
138
|
+
return parsed;
|
|
151
139
|
}
|
|
152
140
|
}
|
|
153
141
|
//# sourceMappingURL=crux.js.map
|
package/dist/ai/crux.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crux.js","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crux.js","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/failure.d.ts
CHANGED
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
*
|
|
11
11
|
* One gate, shared by both passes, so they agree on what "the provider stopped
|
|
12
12
|
* working" means and a caller can report it once.
|
|
13
|
+
*
|
|
14
|
+
* Content-quality misses (empty/unusable crux, #235) still count as failed files
|
|
15
|
+
* so they are not cached as success (#177), but they do not increment the
|
|
16
|
+
* consecutive-failure cutoff — the provider is answering, just not usefully for
|
|
17
|
+
* those files. Quota/auth stay immediately terminal (#127).
|
|
13
18
|
*/
|
|
14
19
|
/** Consecutive failures that end a pass. One flaky file is normal; five in a row is
|
|
15
20
|
* a provider that is not going to start working, and each further call is spend
|
|
@@ -39,8 +44,14 @@ export declare class LlmFailureGate {
|
|
|
39
44
|
private consecutive;
|
|
40
45
|
/** True once the pass should stop issuing calls. */
|
|
41
46
|
get stopped(): boolean;
|
|
42
|
-
/**
|
|
43
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Record a failed unit; sets {@link fatal} when this failure is the last straw.
|
|
49
|
+
* Pass `{ quality: true }` for a content-quality miss (#235): counted, not fatal,
|
|
50
|
+
* unless the message is quota/auth (those still stop immediately).
|
|
51
|
+
*/
|
|
52
|
+
record(message: string, opts?: {
|
|
53
|
+
quality?: boolean;
|
|
54
|
+
}): void;
|
|
44
55
|
/** Record a success — a failure run only counts while it is unbroken. */
|
|
45
56
|
succeeded(): void;
|
|
46
57
|
/** Record a unit that was not attempted because the pass had stopped. */
|
package/dist/ai/failure.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failure.d.ts","sourceRoot":"","sources":["../../src/ai/failure.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"failure.d.ts","sourceRoot":"","sources":["../../src/ai/failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;iCAEiC;AACjC,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS7D;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,uCAAuC;IACvC,MAAM,SAAK;IACX,mEAAmE;IACnE,OAAO,SAAK;IACZ,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,WAAW,CAAK;IAExB,oDAAoD;IACpD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAc3D,yEAAyE;IACzE,SAAS,IAAI,IAAI;IAIjB,yEAAyE;IACzE,IAAI,IAAI,IAAI;CAGb"}
|
package/dist/ai/failure.js
CHANGED
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
*
|
|
11
11
|
* One gate, shared by both passes, so they agree on what "the provider stopped
|
|
12
12
|
* working" means and a caller can report it once.
|
|
13
|
+
*
|
|
14
|
+
* Content-quality misses (empty/unusable crux, #235) still count as failed files
|
|
15
|
+
* so they are not cached as success (#177), but they do not increment the
|
|
16
|
+
* consecutive-failure cutoff — the provider is answering, just not usefully for
|
|
17
|
+
* those files. Quota/auth stay immediately terminal (#127).
|
|
13
18
|
*/
|
|
14
19
|
/** Consecutive failures that end a pass. One flaky file is normal; five in a row is
|
|
15
20
|
* a provider that is not going to start working, and each further call is spend
|
|
@@ -50,15 +55,22 @@ export class LlmFailureGate {
|
|
|
50
55
|
get stopped() {
|
|
51
56
|
return this.fatal !== undefined;
|
|
52
57
|
}
|
|
53
|
-
/**
|
|
54
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Record a failed unit; sets {@link fatal} when this failure is the last straw.
|
|
60
|
+
* Pass `{ quality: true }` for a content-quality miss (#235): counted, not fatal,
|
|
61
|
+
* unless the message is quota/auth (those still stop immediately).
|
|
62
|
+
*/
|
|
63
|
+
record(message, opts) {
|
|
55
64
|
this.failed++;
|
|
56
|
-
this.consecutive++;
|
|
57
65
|
const terminal = terminalReason(message);
|
|
58
66
|
if (terminal) {
|
|
59
67
|
this.fatal = `${terminal} — stopped after ${this.failed} failed file(s). First error: ${message}`;
|
|
68
|
+
return;
|
|
60
69
|
}
|
|
61
|
-
|
|
70
|
+
if (opts?.quality)
|
|
71
|
+
return;
|
|
72
|
+
this.consecutive++;
|
|
73
|
+
if (this.consecutive >= MAX_CONSECUTIVE_FAILURES) {
|
|
62
74
|
this.fatal = `${this.consecutive} files in a row failed, so the pass stopped rather than keep calling. Last error: ${message}`;
|
|
63
75
|
}
|
|
64
76
|
}
|
package/dist/ai/failure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failure.js","sourceRoot":"","sources":["../../src/ai/failure.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"failure.js","sourceRoot":"","sources":["../../src/ai/failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;iCAEiC;AACjC,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAChC,IAAI,gFAAgF,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7F,OAAO,iEAAiE,CAAC;IAC3E,CAAC;IACD,IAAI,6EAA6E,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1F,OAAO,mCAAmC,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,cAAc;IACzB,uCAAuC;IACvC,MAAM,GAAG,CAAC,CAAC;IACX,mEAAmE;IACnE,OAAO,GAAG,CAAC,CAAC;IACZ,yCAAyC;IACzC,KAAK,CAAU;IACP,WAAW,GAAG,CAAC,CAAC;IAExB,oDAAoD;IACpD,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IAClC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,OAAe,EAAE,IAA4B;QAClD,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,KAAK,GAAG,GAAG,QAAQ,oBAAoB,IAAI,CAAC,MAAM,iCAAiC,OAAO,EAAE,CAAC;YAClG,OAAO;QACT,CAAC;QACD,IAAI,IAAI,EAAE,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,IAAI,CAAC,WAAW,IAAI,wBAAwB,EAAE,CAAC;YACjD,IAAI,CAAC,KAAK,GAAG,GAAG,IAAI,CAAC,WAAW,qFAAqF,OAAO,EAAE,CAAC;QACjI,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,SAAS;QACP,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IACvB,CAAC;IAED,yEAAyE;IACzE,IAAI;QACF,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;CACF"}
|
package/dist/ai/llm/factory.d.ts
CHANGED
|
@@ -4,9 +4,18 @@
|
|
|
4
4
|
* `baseUrl` at OpenRouter, Fireworks, a LiteLLM proxy, Groq, a local server, …),
|
|
5
5
|
* `anthropic` speaks the native Messages API. Adding a vendor is a base URL, not
|
|
6
6
|
* a code change; adding a wire format is one new adapter here.
|
|
7
|
+
*
|
|
8
|
+
* `litellm` is a convenience over `openai`: same wire format, but pointed at a
|
|
9
|
+
* LiteLLM proxy by default and paired with `/v1/models` auto-discovery
|
|
10
|
+
* (see litellm.ts), so one endpoint reaches 100+ providers.
|
|
11
|
+
*
|
|
12
|
+
* `orcarouter` is the same kind of convenience over `openai`, pointed at the
|
|
13
|
+
* OrcaRouter AI gateway by default (see orcarouter.ts) so its users get the
|
|
14
|
+
* gateway's routing, failover, and guardrails behind a named provider instead
|
|
15
|
+
* of a bare custom base URL.
|
|
7
16
|
*/
|
|
8
17
|
import type { ChatModel } from "./types.js";
|
|
9
|
-
export type ProviderKind = "openai" | "anthropic";
|
|
18
|
+
export type ProviderKind = "openai" | "anthropic" | "litellm" | "orcarouter";
|
|
10
19
|
export interface ChatModelConfig {
|
|
11
20
|
provider: ProviderKind;
|
|
12
21
|
apiKey: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/factory.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC;AAE7E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,SAAS,CA8B/D"}
|
package/dist/ai/llm/factory.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { OpenAIChatModel } from "./openai.js";
|
|
2
2
|
import { AnthropicChatModel } from "./anthropic.js";
|
|
3
|
+
import { LiteLLMChatModel } from "./litellm.js";
|
|
4
|
+
import { OrcaRouterChatModel } from "./orcarouter.js";
|
|
3
5
|
export function createChatModel(cfg) {
|
|
4
6
|
switch (cfg.provider) {
|
|
5
7
|
case "anthropic":
|
|
@@ -11,6 +13,20 @@ export function createChatModel(cfg) {
|
|
|
11
13
|
baseUrl: cfg.baseUrl,
|
|
12
14
|
headers: cfg.headers,
|
|
13
15
|
});
|
|
16
|
+
case "litellm":
|
|
17
|
+
return new LiteLLMChatModel({
|
|
18
|
+
apiKey: cfg.apiKey,
|
|
19
|
+
model: cfg.model,
|
|
20
|
+
baseUrl: cfg.baseUrl,
|
|
21
|
+
headers: cfg.headers,
|
|
22
|
+
});
|
|
23
|
+
case "orcarouter":
|
|
24
|
+
return new OrcaRouterChatModel({
|
|
25
|
+
apiKey: cfg.apiKey,
|
|
26
|
+
model: cfg.model,
|
|
27
|
+
baseUrl: cfg.baseUrl,
|
|
28
|
+
headers: cfg.headers,
|
|
29
|
+
});
|
|
14
30
|
default: {
|
|
15
31
|
const _exhaustive = cfg.provider;
|
|
16
32
|
throw new Error(`unknown provider: ${String(_exhaustive)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/ai/llm/factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/ai/llm/factory.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAatD,MAAM,UAAU,eAAe,CAAC,GAAoB;IAClD,QAAQ,GAAG,CAAC,QAAQ,EAAE,CAAC;QACrB,KAAK,WAAW;YACd,OAAO,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChG,KAAK,QAAQ;YACX,OAAO,IAAI,eAAe,CAAC;gBACzB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,KAAK,SAAS;YACZ,OAAO,IAAI,gBAAgB,CAAC;gBAC1B,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,KAAK,YAAY;YACf,OAAO,IAAI,mBAAmB,CAAC;gBAC7B,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,WAAW,GAAU,GAAG,CAAC,QAAQ,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteLLM transport. A LiteLLM proxy speaks the OpenAI-compatible wire format,
|
|
3
|
+
* so this reuses the OpenAI adapter's request/response translation and simply
|
|
4
|
+
* points it at the proxy. The value over a bare `openai` provider + `baseUrl`
|
|
5
|
+
* is a first-class, self-describing provider: a sensible default proxy address
|
|
6
|
+
* and `/v1/models` auto-discovery (see {@link listLiteLLMModels}), so callers
|
|
7
|
+
* can enumerate whatever models the proxy is configured to serve — reaching
|
|
8
|
+
* 100+ providers (OpenAI, Anthropic, Gemini, Bedrock, Vertex, …) through one
|
|
9
|
+
* endpoint without a per-vendor code path.
|
|
10
|
+
*/
|
|
11
|
+
import OpenAI from "openai";
|
|
12
|
+
import { OpenAIChatModel, type OpenAIChatModelOptions } from "./openai.js";
|
|
13
|
+
/** Conventional local address of a self-hosted LiteLLM proxy. */
|
|
14
|
+
export declare const DEFAULT_LITELLM_BASE_URL = "http://localhost:4000";
|
|
15
|
+
export type LiteLLMChatModelOptions = OpenAIChatModelOptions;
|
|
16
|
+
/**
|
|
17
|
+
* ChatModel backed by a LiteLLM proxy. Inherits every translation detail from
|
|
18
|
+
* {@link OpenAIChatModel} (the proxy is OpenAI-compatible) and only changes the
|
|
19
|
+
* defaults: the proxy base URL and a `litellm:<model>` label.
|
|
20
|
+
*/
|
|
21
|
+
export declare class LiteLLMChatModel extends OpenAIChatModel {
|
|
22
|
+
constructor(opts: LiteLLMChatModelOptions);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Auto-discover the models a LiteLLM proxy is serving via `GET /v1/models`.
|
|
26
|
+
* Returns the model ids so a caller can present/validate them instead of
|
|
27
|
+
* hardcoding a model string. A stub `client` may be injected for tests.
|
|
28
|
+
*/
|
|
29
|
+
export declare function listLiteLLMModels(opts: {
|
|
30
|
+
apiKey: string;
|
|
31
|
+
baseUrl?: string;
|
|
32
|
+
client?: OpenAI;
|
|
33
|
+
}): Promise<string[]>;
|
|
34
|
+
//# sourceMappingURL=litellm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"litellm.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/litellm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG3E,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,0BAA0B,CAAC;AAEhE,MAAM,MAAM,uBAAuB,GAAG,sBAAsB,CAAC;AAE7D;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,eAAe;gBACvC,IAAI,EAAE,uBAAuB;CAO1C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUpB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteLLM transport. A LiteLLM proxy speaks the OpenAI-compatible wire format,
|
|
3
|
+
* so this reuses the OpenAI adapter's request/response translation and simply
|
|
4
|
+
* points it at the proxy. The value over a bare `openai` provider + `baseUrl`
|
|
5
|
+
* is a first-class, self-describing provider: a sensible default proxy address
|
|
6
|
+
* and `/v1/models` auto-discovery (see {@link listLiteLLMModels}), so callers
|
|
7
|
+
* can enumerate whatever models the proxy is configured to serve — reaching
|
|
8
|
+
* 100+ providers (OpenAI, Anthropic, Gemini, Bedrock, Vertex, …) through one
|
|
9
|
+
* endpoint without a per-vendor code path.
|
|
10
|
+
*/
|
|
11
|
+
import OpenAI from "openai";
|
|
12
|
+
import { OpenAIChatModel } from "./openai.js";
|
|
13
|
+
import { transportRetries } from "./types.js";
|
|
14
|
+
/** Conventional local address of a self-hosted LiteLLM proxy. */
|
|
15
|
+
export const DEFAULT_LITELLM_BASE_URL = "http://localhost:4000";
|
|
16
|
+
/**
|
|
17
|
+
* ChatModel backed by a LiteLLM proxy. Inherits every translation detail from
|
|
18
|
+
* {@link OpenAIChatModel} (the proxy is OpenAI-compatible) and only changes the
|
|
19
|
+
* defaults: the proxy base URL and a `litellm:<model>` label.
|
|
20
|
+
*/
|
|
21
|
+
export class LiteLLMChatModel extends OpenAIChatModel {
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
super({
|
|
24
|
+
...opts,
|
|
25
|
+
baseUrl: opts.baseUrl ?? DEFAULT_LITELLM_BASE_URL,
|
|
26
|
+
label: opts.label ?? `litellm:${opts.model}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Auto-discover the models a LiteLLM proxy is serving via `GET /v1/models`.
|
|
32
|
+
* Returns the model ids so a caller can present/validate them instead of
|
|
33
|
+
* hardcoding a model string. A stub `client` may be injected for tests.
|
|
34
|
+
*/
|
|
35
|
+
export async function listLiteLLMModels(opts) {
|
|
36
|
+
const client = opts.client ??
|
|
37
|
+
new OpenAI({
|
|
38
|
+
apiKey: opts.apiKey,
|
|
39
|
+
baseURL: opts.baseUrl ?? DEFAULT_LITELLM_BASE_URL,
|
|
40
|
+
maxRetries: transportRetries(),
|
|
41
|
+
});
|
|
42
|
+
const res = await client.models.list();
|
|
43
|
+
return res.data.map((m) => m.id);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=litellm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"litellm.js","sourceRoot":"","sources":["../../../src/ai/llm/litellm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,eAAe,EAA+B,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,iEAAiE;AACjE,MAAM,CAAC,MAAM,wBAAwB,GAAG,uBAAuB,CAAC;AAIhE;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,eAAe;IACnD,YAAY,IAA6B;QACvC,KAAK,CAAC;YACJ,GAAG,IAAI;YACP,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,wBAAwB;YACjD,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,WAAW,IAAI,CAAC,KAAK,EAAE;SAC7C,CAAC,CAAC;IACL,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAIvC;IACC,MAAM,MAAM,GACV,IAAI,CAAC,MAAM;QACX,IAAI,MAAM,CAAC;YACT,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,wBAAwB;YACjD,UAAU,EAAE,gBAAgB,EAAE;SAC/B,CAAC,CAAC;IACL,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACvC,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAsC,MAAM,YAAY,CAAC;AAM3G,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAsC,MAAM,YAAY,CAAC;AAM3G,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAuGD,qBAAa,eAAgB,YAAW,SAAS;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,IAAI,EAAE,sBAAsB;IAalC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA2BrD;;;;;;;;;OASG;YACW,oBAAoB;IAkClC,OAAO,CAAC,YAAY;CA6CrB"}
|