@nanonets/graft 0.10.1 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.md +32 -11
  2. package/dist/ai/crux.d.ts.map +1 -1
  3. package/dist/ai/crux.js +7 -6
  4. package/dist/ai/crux.js.map +1 -1
  5. package/dist/ai/failure.d.ts +49 -0
  6. package/dist/ai/failure.d.ts.map +1 -0
  7. package/dist/ai/failure.js +74 -0
  8. package/dist/ai/failure.js.map +1 -0
  9. package/dist/ai/llm/anthropic.d.ts.map +1 -1
  10. package/dist/ai/llm/anthropic.js +4 -1
  11. package/dist/ai/llm/anthropic.js.map +1 -1
  12. package/dist/ai/llm/claude-cli.d.ts +60 -0
  13. package/dist/ai/llm/claude-cli.d.ts.map +1 -0
  14. package/dist/ai/llm/claude-cli.js +417 -0
  15. package/dist/ai/llm/claude-cli.js.map +1 -0
  16. package/dist/ai/llm/openai.d.ts.map +1 -1
  17. package/dist/ai/llm/openai.js +2 -0
  18. package/dist/ai/llm/openai.js.map +1 -1
  19. package/dist/ai/llm/types.d.ts +9 -0
  20. package/dist/ai/llm/types.d.ts.map +1 -1
  21. package/dist/ai/llm/types.js +12 -1
  22. package/dist/ai/llm/types.js.map +1 -1
  23. package/dist/ask/ask.d.ts.map +1 -1
  24. package/dist/ask/ask.js +39 -45
  25. package/dist/ask/ask.js.map +1 -1
  26. package/dist/ask/fuse.d.ts +83 -46
  27. package/dist/ask/fuse.d.ts.map +1 -1
  28. package/dist/ask/fuse.js +165 -67
  29. package/dist/ask/fuse.js.map +1 -1
  30. package/dist/blast/blast-cli.d.ts +30 -0
  31. package/dist/blast/blast-cli.d.ts.map +1 -0
  32. package/dist/blast/blast-cli.js +163 -0
  33. package/dist/blast/blast-cli.js.map +1 -0
  34. package/dist/blast/blast.d.ts +119 -0
  35. package/dist/blast/blast.d.ts.map +1 -0
  36. package/dist/blast/blast.js +383 -0
  37. package/dist/blast/blast.js.map +1 -0
  38. package/dist/blast/diff.d.ts +53 -0
  39. package/dist/blast/diff.d.ts.map +1 -0
  40. package/dist/blast/diff.js +193 -0
  41. package/dist/blast/diff.js.map +1 -0
  42. package/dist/blast/modules.d.ts +33 -0
  43. package/dist/blast/modules.d.ts.map +1 -0
  44. package/dist/blast/modules.js +83 -0
  45. package/dist/blast/modules.js.map +1 -0
  46. package/dist/blast/name.d.ts +61 -0
  47. package/dist/blast/name.d.ts.map +1 -0
  48. package/dist/blast/name.js +233 -0
  49. package/dist/blast/name.js.map +1 -0
  50. package/dist/blast/render.d.ts +37 -0
  51. package/dist/blast/render.d.ts.map +1 -0
  52. package/dist/blast/render.js +300 -0
  53. package/dist/blast/render.js.map +1 -0
  54. package/dist/blast/viz.d.ts +17 -0
  55. package/dist/blast/viz.d.ts.map +1 -0
  56. package/dist/blast/viz.js +324 -0
  57. package/dist/blast/viz.js.map +1 -0
  58. package/dist/claude/hooks.d.ts.map +1 -1
  59. package/dist/claude/hooks.js +18 -2
  60. package/dist/claude/hooks.js.map +1 -1
  61. package/dist/claude/shim-template.d.ts.map +1 -1
  62. package/dist/claude/shim-template.js +45 -15
  63. package/dist/claude/shim-template.js.map +1 -1
  64. package/dist/claude/state.d.ts +5 -0
  65. package/dist/claude/state.d.ts.map +1 -1
  66. package/dist/claude/state.js.map +1 -1
  67. package/dist/cli-picker.d.ts +44 -5
  68. package/dist/cli-picker.d.ts.map +1 -1
  69. package/dist/cli-picker.js +73 -26
  70. package/dist/cli-picker.js.map +1 -1
  71. package/dist/cli.js +270 -14
  72. package/dist/cli.js.map +1 -1
  73. package/dist/context/build.d.ts +8 -0
  74. package/dist/context/build.d.ts.map +1 -1
  75. package/dist/context/build.js +57 -9
  76. package/dist/context/build.js.map +1 -1
  77. package/dist/context/check.d.ts.map +1 -1
  78. package/dist/context/check.js +7 -6
  79. package/dist/context/check.js.map +1 -1
  80. package/dist/context/node-file.d.ts.map +1 -1
  81. package/dist/context/node-file.js +9 -2
  82. package/dist/context/node-file.js.map +1 -1
  83. package/dist/graph/bindings.d.ts.map +1 -1
  84. package/dist/graph/bindings.js +171 -8
  85. package/dist/graph/bindings.js.map +1 -1
  86. package/dist/graph/build.d.ts.map +1 -1
  87. package/dist/graph/build.js +36 -16
  88. package/dist/graph/build.js.map +1 -1
  89. package/dist/graph/check.d.ts +4 -0
  90. package/dist/graph/check.d.ts.map +1 -1
  91. package/dist/graph/check.js +8 -1
  92. package/dist/graph/check.js.map +1 -1
  93. package/dist/graph/container.d.ts +63 -0
  94. package/dist/graph/container.d.ts.map +1 -0
  95. package/dist/graph/container.js +185 -0
  96. package/dist/graph/container.js.map +1 -0
  97. package/dist/graph/enrich.d.ts +18 -0
  98. package/dist/graph/enrich.d.ts.map +1 -1
  99. package/dist/graph/enrich.js +55 -2
  100. package/dist/graph/enrich.js.map +1 -1
  101. package/dist/graph/extract.d.ts +3 -1
  102. package/dist/graph/extract.d.ts.map +1 -1
  103. package/dist/graph/extract.js +215 -8
  104. package/dist/graph/extract.js.map +1 -1
  105. package/dist/graph/generic.d.ts +34 -1
  106. package/dist/graph/generic.d.ts.map +1 -1
  107. package/dist/graph/generic.js +152 -3
  108. package/dist/graph/generic.js.map +1 -1
  109. package/dist/graph/queries/clojure.scm +58 -0
  110. package/dist/graph/resolve.d.ts.map +1 -1
  111. package/dist/graph/resolve.js +132 -2
  112. package/dist/graph/resolve.js.map +1 -1
  113. package/dist/graph/scopes.d.ts +2 -2
  114. package/dist/graph/scopes.d.ts.map +1 -1
  115. package/dist/graph/scopes.js +8 -6
  116. package/dist/graph/scopes.js.map +1 -1
  117. package/dist/graph/source-files.d.ts +13 -4
  118. package/dist/graph/source-files.d.ts.map +1 -1
  119. package/dist/graph/source-files.js +35 -12
  120. package/dist/graph/source-files.js.map +1 -1
  121. package/dist/graph/types.d.ts +1 -1
  122. package/dist/graph/types.d.ts.map +1 -1
  123. package/dist/graph/workspace-cli.d.ts +2 -0
  124. package/dist/graph/workspace-cli.d.ts.map +1 -1
  125. package/dist/graph/workspace-cli.js +10 -4
  126. package/dist/graph/workspace-cli.js.map +1 -1
  127. package/dist/graph/workspace.d.ts +11 -6
  128. package/dist/graph/workspace.d.ts.map +1 -1
  129. package/dist/graph/workspace.js +24 -13
  130. package/dist/graph/workspace.js.map +1 -1
  131. package/dist/graph/write.d.ts.map +1 -1
  132. package/dist/graph/write.js +25 -2
  133. package/dist/graph/write.js.map +1 -1
  134. package/dist/hosts/antigravity.d.ts +7 -0
  135. package/dist/hosts/antigravity.d.ts.map +1 -0
  136. package/dist/hosts/antigravity.js +42 -0
  137. package/dist/hosts/antigravity.js.map +1 -0
  138. package/dist/hosts/init.d.ts.map +1 -1
  139. package/dist/hosts/init.js +6 -1
  140. package/dist/hosts/init.js.map +1 -1
  141. package/dist/hosts/mcp-config.d.ts.map +1 -1
  142. package/dist/hosts/mcp-config.js +7 -0
  143. package/dist/hosts/mcp-config.js.map +1 -1
  144. package/dist/hosts/plan.d.ts +1 -1
  145. package/dist/hosts/plan.d.ts.map +1 -1
  146. package/dist/hosts/plan.js +2 -0
  147. package/dist/hosts/plan.js.map +1 -1
  148. package/dist/hosts/registry.d.ts.map +1 -1
  149. package/dist/hosts/registry.js +13 -0
  150. package/dist/hosts/registry.js.map +1 -1
  151. package/dist/ingest/fs.d.ts +9 -2
  152. package/dist/ingest/fs.d.ts.map +1 -1
  153. package/dist/ingest/fs.js +100 -16
  154. package/dist/ingest/fs.js.map +1 -1
  155. package/dist/mcp/server.d.ts.map +1 -1
  156. package/dist/mcp/server.js +45 -2
  157. package/dist/mcp/server.js.map +1 -1
  158. package/dist/telemetry/contract.d.ts +90 -0
  159. package/dist/telemetry/contract.d.ts.map +1 -0
  160. package/dist/telemetry/contract.js +203 -0
  161. package/dist/telemetry/contract.js.map +1 -0
  162. package/dist/telemetry/flush.d.ts +21 -0
  163. package/dist/telemetry/flush.d.ts.map +1 -0
  164. package/dist/telemetry/flush.js +84 -0
  165. package/dist/telemetry/flush.js.map +1 -0
  166. package/dist/telemetry/gate.d.ts +16 -0
  167. package/dist/telemetry/gate.d.ts.map +1 -0
  168. package/dist/telemetry/gate.js +64 -0
  169. package/dist/telemetry/gate.js.map +1 -0
  170. package/dist/telemetry/identity.d.ts +33 -0
  171. package/dist/telemetry/identity.d.ts.map +1 -0
  172. package/dist/telemetry/identity.js +80 -0
  173. package/dist/telemetry/identity.js.map +1 -0
  174. package/dist/telemetry/index.d.ts +18 -0
  175. package/dist/telemetry/index.d.ts.map +1 -0
  176. package/dist/telemetry/index.js +16 -0
  177. package/dist/telemetry/index.js.map +1 -0
  178. package/dist/telemetry/key.d.ts +21 -0
  179. package/dist/telemetry/key.d.ts.map +1 -0
  180. package/dist/telemetry/key.js +49 -0
  181. package/dist/telemetry/key.js.map +1 -0
  182. package/dist/telemetry/notice.d.ts +19 -0
  183. package/dist/telemetry/notice.d.ts.map +1 -0
  184. package/dist/telemetry/notice.js +90 -0
  185. package/dist/telemetry/notice.js.map +1 -0
  186. package/dist/telemetry/queue.d.ts +35 -0
  187. package/dist/telemetry/queue.d.ts.map +1 -0
  188. package/dist/telemetry/queue.js +162 -0
  189. package/dist/telemetry/queue.js.map +1 -0
  190. package/dist/telemetry/send.d.ts +19 -0
  191. package/dist/telemetry/send.d.ts.map +1 -0
  192. package/dist/telemetry/send.js +84 -0
  193. package/dist/telemetry/send.js.map +1 -0
  194. package/dist/telemetry/sessions.d.ts +8 -0
  195. package/dist/telemetry/sessions.d.ts.map +1 -0
  196. package/dist/telemetry/sessions.js +81 -0
  197. package/dist/telemetry/sessions.js.map +1 -0
  198. package/dist/telemetry/track.d.ts +43 -0
  199. package/dist/telemetry/track.d.ts.map +1 -0
  200. package/dist/telemetry/track.js +108 -0
  201. package/dist/telemetry/track.js.map +1 -0
  202. package/dist/upkeep-run.d.ts +15 -0
  203. package/dist/upkeep-run.d.ts.map +1 -0
  204. package/dist/upkeep-run.js +60 -0
  205. package/dist/upkeep-run.js.map +1 -0
  206. package/dist/upkeep.d.ts +114 -0
  207. package/dist/upkeep.d.ts.map +1 -0
  208. package/dist/upkeep.js +232 -0
  209. package/dist/upkeep.js.map +1 -0
  210. package/dist/util/num.d.ts +3 -0
  211. package/dist/util/num.d.ts.map +1 -0
  212. package/dist/util/num.js +20 -0
  213. package/dist/util/num.js.map +1 -0
  214. package/dist/util/state.d.ts +11 -4
  215. package/dist/util/state.d.ts.map +1 -1
  216. package/dist/util/state.js +9 -0
  217. package/dist/util/state.js.map +1 -1
  218. package/dist/viewer/app.js +1 -1
  219. package/dist/viewer/index.html +1 -0
  220. package/dist/viewer/style.css +23 -2
  221. package/dist/viz/assemble.d.ts +33 -0
  222. package/dist/viz/assemble.d.ts.map +1 -1
  223. package/dist/viz/assemble.js.map +1 -1
  224. package/dist/viz/export.d.ts +39 -0
  225. package/dist/viz/export.d.ts.map +1 -0
  226. package/dist/viz/export.js +98 -0
  227. package/dist/viz/export.js.map +1 -0
  228. package/package.json +6 -5
  229. package/scripts/stamp-telemetry-key.mjs +53 -0
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/%40nanonets%2Fgraft?style=for-the-badge&logo=nodedotjs&logoColor=white" /></a>
14
14
  <img src="https://img.shields.io/badge/TypeScript-strict-3178C6?style=for-the-badge&logo=typescript&logoColor=white" />
15
15
  <img src="https://img.shields.io/badge/License-MIT-20C997?style=for-the-badge" />
16
- <img src="https://img.shields.io/badge/telemetry-none-546FFF?style=for-the-badge" />
16
+ <a href="TELEMETRY.md"><img src="https://img.shields.io/badge/telemetry-anonymous%2C%20opt--out-546FFF?style=for-the-badge" /></a>
17
17
  <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>
18
18
  </p>
19
19
 
@@ -105,8 +105,8 @@ Graft builds that understanding **once** and writes it into your repo as a folde
105
105
 
106
106
  - **Real explanations, not a list of symbols.** Each node says, in plain English, what a part of the system does and how it connects to the rest, the way a senior engineer would explain it. That is the part an agent actually needs so it can skip the exploration. It is not a dump of function names.
107
107
  - **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
- - **Grafted into git.** The graph is just files in `graft/`. Commit it, and anyone who clones the repo has it. No database, no server, no setup. Git does the syncing, and a stale graph shows up as a diff in review instead of rotting in some external store.
109
- - **The diff lives with the code.** When a change moves things around, you see it in the graph diff in the same pull request, right next to the code that caused it.
108
+ - **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
+ - **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
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.
111
111
 
112
112
  <p align="center">
@@ -179,7 +179,7 @@ flowchart LR
179
179
 
180
180
  Every pass is cached by content hash — the LLM ones and the tree-sitter parse alike. Re-running only touches the files that changed, so the second build is fast and cheap (on this repo, 124 files: 0.74s cold, 0.18s after one edited file, 0.18s with nothing changed). `graft build --no-reuse` forces a cold re-parse.
181
181
 
182
- That cheapness is what lets **every query refresh the graph before it answers**. A retrieval call stats the tree against the last build's fingerprint (~3ms), and rebuilds only if something moved — so `ask`/`grep`/`callers`/`skeleton`/`map` describe the code as it is right now, including edits that are unsaved to git: uncommitted, unstaged, or staged all look the same to graft, which never reads git at all. The refresh is structural and `$0`; it never calls the LLM. Turn it off per-command with `--no-refresh`, or everywhere with `GRAFT_NO_REFRESH=1`.
182
+ That cheapness is what lets **every query refresh the graph before it answers**. A retrieval call stats the tree against the last build's fingerprint (~3ms), and rebuilds only if something moved — so `ask`/`grep`/`callers`/`skeleton`/`map` describe the code as it is right now, including edits that are unsaved to git: uncommitted, unstaged, or staged all look the same to graft. Git determines the visible file set; freshness compares the working-tree bytes rather than commit or index state. The refresh is structural and `$0`; it never calls the LLM. Turn it off per-command with `--no-refresh`, or everywhere with `GRAFT_NO_REFRESH=1`.
183
183
 
184
184
  Alongside the markdown graph, `graft build` builds `graft/.graph/wiring.json` — a per-symbol code graph — plus a per-file wiring card mirroring your source tree. Tier 1 is pure tree-sitter (every function, class, and call edge; deterministic, no model, no network), which is why plain `graft build` needs no key. The `--deep` pass adds a one-line summary and a crux excerpt per symbol, cached by body hash.
185
185
 
@@ -197,7 +197,7 @@ compiler-grade layer — all `$0` and deterministic (no model, no key):
197
197
  - **Broad** — symbols (functions, classes, methods, types, …) plus name-resolved
198
198
  call edges via a generic tree-sitter extractor, one grammar per language:
199
199
  **Rust, C, C++, C#, Ruby, PHP, Kotlin, Scala, Swift, Elixir, Solidity,
200
- OCaml, Zig, Dart**.
200
+ OCaml, Zig, Dart, Clojure**.
201
201
 
202
202
  - **Compiler-grade edges (opt-in)** — `graft build --lsp` adds precise
203
203
  `lsp_resolved` call edges (member calls the static pass can't type) when a
@@ -205,7 +205,7 @@ compiler-grade layer — all `$0` and deterministic (no model, no key):
205
205
  **gopls** (Go), **pyright** (Python), **typescript-language-server** (TS/JS).
206
206
  It's best-effort — with no server installed the graph is unchanged.
207
207
 
208
- Twenty languages in total. A file whose language isn't listed is skipped, not
208
+ Twenty-one languages in total. A file whose language isn't listed is skipped, not
209
209
  indexed. Adding a broad-tier language is a small contribution — see
210
210
  [CREDITS.md](CREDITS.md) for the folks who added the current set.
211
211
 
@@ -237,7 +237,7 @@ _Summary, sources, links, and notes ship today in markdown nodes. The crux ships
237
237
 
238
238
  - **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.
239
239
  - **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, or `anthropic` for the native API), 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.)
240
- - **No telemetry** and no analytics — the only network calls are the LLM requests you configured.
240
+ - **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.
241
241
 
242
242
  See [`.env.example`](.env.example) for the full list of settings (model, base URL, graph directory).
243
243
 
@@ -331,6 +331,8 @@ graft build [dir] # build graft/ from the code at [dir]: wiri
331
331
  graft build --deep # add the LLM layer: concept nodes + per-symbol summary/crux (cached)
332
332
  graft build --extensions .ts .py # only include these code extensions
333
333
  graft build --no-reuse # re-parse every file instead of replaying unchanged ones from cache
334
+ graft build --follow-submodules # include initialized submodules; persist the choice for builds + MCP refresh
335
+ graft build --no-follow-submodules # exclude submodules again and persist that choice (the default)
334
336
 
335
337
  graft ask "<task>" [dir] # query the graph — ranked nodes + exact file:line (no LLM, no key)
336
338
  graft ask "<task>" --json # machine-readable result
@@ -349,16 +351,24 @@ graft grep "<regex>" -i --fixed # case-insensitive; treat the pattern as a
349
351
  graft map [dir] # token-budgeted repo orientation — dir clusters, hubs, hotspots (no LLM, no key)
350
352
  graft map --max-dirs N # raise/lower the number of directories shown
351
353
 
354
+ graft blast [dir] # blast radius of a diff: what depends on the lines this change touched (no LLM, no key)
355
+ graft blast --base origin/main # diff against the merge base with HEAD — what a PR job runs
356
+ graft blast --format markdown # a PR comment: the areas a change can reach, per-symbol detail collapsed under it
357
+ graft blast --base origin/main --name # name those areas with one cached LLM call, instead of a full --deep build
358
+ graft blast --export-viz site/ # also write the interactive page for this radius (what a PR comment links to)
359
+ graft blast --depth all --format json # the full transitive closure, machine-readable
360
+
352
361
  graft check [dir] # fail (exit 1) if graft/ has drifted from the code (never auto-refreshes — it's the drift report)
353
362
  graft check --json # print the drift report as JSON
354
363
 
355
- # ask / skeleton / callers / grep / map all refresh the graph first if the working tree moved:
364
+ # ask / skeleton / callers / grep / map / blast all refresh the graph first if the working tree moved:
356
365
  # --no-refresh # answer from the graph exactly as it is on disk
357
366
  # GRAFT_NO_REFRESH=1 # same, for every command
358
367
  # GRAFT_REFRESH=hash # hash every file instead of trusting size+mtime
359
368
 
360
369
  graft viz [dir] # see the graph: serves an interactive viewer on localhost
361
370
  graft viz --port 5000 --no-open # pick a port; don't auto-open the browser
371
+ graft viz --export site/ --title "PR #12" # one self-contained index.html — for CI, GitHub Pages, or a build artifact
362
372
 
363
373
  graft init [dir] # pick which agents to wire (prompts on a terminal; writes nothing until you choose)
364
374
  graft init --dry-run # list every file it would touch, then exit
@@ -371,6 +381,8 @@ graft init --list-agents # list known agent ids and exit
371
381
 
372
382
  graft version # print the installed + latest published npm version
373
383
  graft upgrade # npm install -g the latest published version
384
+ # a new version is announced automatically (checked once a day);
385
+ # after upgrading, the next session refreshes this repo's wiring itself
374
386
 
375
387
  # global
376
388
  graft --dir <path> # use a context dir other than <repo>/graft
@@ -414,9 +426,9 @@ scripts/ 2 files · 0 symbols
414
426
  hotspots: contextDirFor · function · src/context/node-file.ts:L100-L103 · 21← wiringPath · function · src/graph/write.ts:L20-L22 · 14← buildGraph · function · src/graph/build.ts:L104-L218 · 11← ...
415
427
  ```
416
428
 
417
- ## Monorepos & multi-repo folders
429
+ ## Monorepos, submodules & multi-repo folders
418
430
 
419
- Graft handles two shapes without any config:
431
+ Graft supports these layouts:
420
432
 
421
433
  - **A monorepo with one `.git`** (a `pnpm-workspace.yaml`/`package.json`
422
434
  `workspaces`, or per-package `go.mod`/`pyproject.toml`/`Cargo.toml`) —
@@ -424,13 +436,22 @@ Graft handles two shapes without any config:
424
436
  rank every scope on its own terms and fuse the results, so the biggest
425
437
  sub-project can't drown a small one; hits carry `[scope/]` labels, and
426
438
  `graft map` groups its directory clusters by scope first.
439
+ - **A Git superproject with initialized submodules** — submodules stay excluded
440
+ by default. Run `graft build --follow-submodules` to fold initialized gitlinks
441
+ into one graph, prefixing child paths (for example,
442
+ `deps/parser/src/index.ts`) while honoring each submodule's own Git ignore
443
+ rules. Visible untracked files are included too; uninitialized submodules
444
+ remain absent until `git submodule update --init` checks them out. The choice
445
+ is saved in `.graft/config.json`, so later no-flag builds and MCP automatic
446
+ refreshes behave the same way. Run `graft build --no-follow-submodules` to
447
+ restore and persist the default boundary.
427
448
  - **A folder of separate git repos** (no `.git` at the top) — `graft build`
428
449
  auto-splits: each child gets its own (git-ignored) `graft/`, and the parent
429
450
  gets a `graft/workspace.json` index. Queries from the parent federate across
430
451
  every child, always labeled `<child>/`. Run `graft build` inside a child to
431
452
  work on just that repo.
432
453
 
433
- Either way, narrow to one sub-project with `graft ask "<task>" --in <scope>/`
454
+ In every layout, narrow to one sub-project with `graft ask "<task>" --in <scope>/`
434
455
  once you know where you're working.
435
456
 
436
457
  `graft init` at the parent of a multi-repo folder wires **every child repo too**,
@@ -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;AAChD,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;CACzD;AAwED,+EAA+E;AAC/E,qBAAa,kBAAmB,YAAW,cAAc;IAC3C,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,SAAS;IAE9B,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAoB9D"}
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;AAChD,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;CACzD;AAyED,+EAA+E;AAC/E,qBAAa,kBAAmB,YAAW,cAAc;IAC3C,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,SAAS;IAE9B,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAoB9D"}
package/dist/ai/crux.js CHANGED
@@ -1,12 +1,12 @@
1
1
  const SYSTEM_PROMPT = `You explain code definitions for a code graph that helps engineers navigate a codebase.
2
2
 
3
- You are given ONE source file with 1-based line numbers, and a list of TARGET definitions in it. For every target, record its purpose and the line range of its core logic via the record_symbols tool.
3
+ 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.
4
4
 
5
5
  Rules:
6
- - Emit exactly one entry per target id given, using that id verbatim.
7
- - summary: ONE sentence — what the symbol is FOR at the business-logic level. Say what problem it solves or rule it enforces, not what its signature already says.
8
- - crux_start / crux_end: FILE line numbers (as shown), inside that symbol's own line range. Pick the SINGLE most important contiguous span — the core branch, formula, guard, or state change. Keep it TIGHT: at most ~8 lines, and NEVER the whole function. If you can't narrow it below that, the symbol has no distinct crux — use 0/0.
9
- - Skip boilerplate, logging, and plumbing. If a symbol has no meaningful crux (trivial getter, data holder, one-line delegation, or logic spread evenly with no focal point), use "crux_start": 0 and "crux_end": 0.`;
6
+ - Return EXACTLY ONE entry for EVERY target id, using that id verbatim. The number of entries you return MUST equal the number of targets. Never omit a target: a reply missing any id is invalid and will be re-requested.
7
+ - A trivial symbol is NOT an exception. You still return it — with a one-sentence summary and crux 0/0 (see below). "Skip" means "give it no crux span", NEVER "leave it out".
8
+ - summary: ONE sentence — what the symbol is FOR at the business-logic level (the problem it solves or the rule it enforces), not a restatement of its signature.
9
+ - crux_start / crux_end: FILE line numbers (as shown), inside that symbol's own line range. Pick the SINGLE most important contiguous span — the core branch, formula, guard, or state change — at most ~8 lines, and NEVER the whole function. When there is no single focal span (a trivial getter, a plain data holder, a one-line delegation, or logic spread evenly), use crux_start: 0 and crux_end: 0. That 0/0 IS the answer — do not drop the entry.`;
10
10
  const RECORD_TOOL = "record_symbols";
11
11
  const SYMBOLS_SCHEMA = {
12
12
  type: "object",
@@ -41,7 +41,8 @@ function userContent(input) {
41
41
  .map((n) => `- id=${n.id} | ${n.kind} | lines L${n.startLine}-L${n.endLine}` +
42
42
  (n.signature ? ` | ${n.signature}` : ""))
43
43
  .join("\n");
44
- return `FILE: ${input.path}\n\n${numberLines(input.source)}\n\nTARGETS:\n${targets}`;
44
+ const n = input.nodes.length;
45
+ return `FILE: ${input.path}\n\n${numberLines(input.source)}\n\nTARGETS (${n} — return all ${n}, one entry per id):\n${targets}`;
45
46
  }
46
47
  /** Normalize the tool's parsed argument object into a {@link NodeCrux} list. */
47
48
  function parseResults(obj) {
@@ -1 +1 @@
1
- {"version":3,"file":"crux.js","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"crux.js","sourceRoot":"","sources":["../../src/ai/crux.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * When to stop calling a provider that is failing (#127).
3
+ *
4
+ * Both LLM passes of `build --deep` — the per-file concept summaries
5
+ * (`context/build.ts`) and the per-file crux pass (`graph/enrich.ts`) — used to
6
+ * catch every error per file, keep going, and finish with a clean exit. On an
7
+ * 884-file repo behind a gateway whose token quota was spent that produced 1,617
8
+ * failed calls, a normal success footer, and a graph whose meaning tier was
9
+ * missing; the degradation surfaced days later as bad `ask` results.
10
+ *
11
+ * One gate, shared by both passes, so they agree on what "the provider stopped
12
+ * working" means and a caller can report it once.
13
+ */
14
+ /** Consecutive failures that end a pass. One flaky file is normal; five in a row is
15
+ * a provider that is not going to start working, and each further call is spend
16
+ * with no chance of a result. */
17
+ export declare const MAX_CONSECUTIVE_FAILURES = 5;
18
+ /**
19
+ * Errors no amount of retrying fixes: the key is wrong, or the account/quota is
20
+ * spent. Matched on the message because that is all a provider-neutral transport
21
+ * carries — over-matching costs a build that was going to fail anyway, while
22
+ * under-matching just falls back to the consecutive-failure cutoff.
23
+ */
24
+ export declare function terminalReason(message: string): string | null;
25
+ /**
26
+ * Failure bookkeeping for one LLM pass: how many units failed, how many were
27
+ * skipped once it gave up, and the human-readable reason it did.
28
+ *
29
+ * Single-threaded by construction — every mutation happens between awaits in the
30
+ * pass's own worker — so no locking is needed despite the concurrency above it.
31
+ */
32
+ export declare class LlmFailureGate {
33
+ /** Units (files) whose call failed. */
34
+ failed: number;
35
+ /** Units never attempted because the pass had already given up. */
36
+ skipped: number;
37
+ /** Why the pass stopped, when it did. */
38
+ fatal?: string;
39
+ private consecutive;
40
+ /** True once the pass should stop issuing calls. */
41
+ get stopped(): boolean;
42
+ /** Record a failed unit; sets {@link fatal} when this failure is the last straw. */
43
+ record(message: string): void;
44
+ /** Record a success — a failure run only counts while it is unbroken. */
45
+ succeeded(): void;
46
+ /** Record a unit that was not attempted because the pass had stopped. */
47
+ skip(): void;
48
+ }
49
+ //# sourceMappingURL=failure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure.d.ts","sourceRoot":"","sources":["../../src/ai/failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;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,oFAAoF;IACpF,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAW7B,yEAAyE;IACzE,SAAS,IAAI,IAAI;IAIjB,yEAAyE;IACzE,IAAI,IAAI,IAAI;CAGb"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * When to stop calling a provider that is failing (#127).
3
+ *
4
+ * Both LLM passes of `build --deep` — the per-file concept summaries
5
+ * (`context/build.ts`) and the per-file crux pass (`graph/enrich.ts`) — used to
6
+ * catch every error per file, keep going, and finish with a clean exit. On an
7
+ * 884-file repo behind a gateway whose token quota was spent that produced 1,617
8
+ * failed calls, a normal success footer, and a graph whose meaning tier was
9
+ * missing; the degradation surfaced days later as bad `ask` results.
10
+ *
11
+ * One gate, shared by both passes, so they agree on what "the provider stopped
12
+ * working" means and a caller can report it once.
13
+ */
14
+ /** Consecutive failures that end a pass. One flaky file is normal; five in a row is
15
+ * a provider that is not going to start working, and each further call is spend
16
+ * with no chance of a result. */
17
+ export const MAX_CONSECUTIVE_FAILURES = 5;
18
+ /**
19
+ * Errors no amount of retrying fixes: the key is wrong, or the account/quota is
20
+ * spent. Matched on the message because that is all a provider-neutral transport
21
+ * carries — over-matching costs a build that was going to fail anyway, while
22
+ * under-matching just falls back to the consecutive-failure cutoff.
23
+ */
24
+ export function terminalReason(message) {
25
+ const m = message.toLowerCase();
26
+ if (/quota|insufficient[_ ]funds|insufficient[_ ]quota|billing|payment required|402/.test(m)) {
27
+ return "the provider reports the quota/credit for this key is exhausted";
28
+ }
29
+ if (/\b401\b|\b403\b|unauthorized|invalid api key|invalid_api_key|authentication/.test(m)) {
30
+ return "the provider rejected the API key";
31
+ }
32
+ return null;
33
+ }
34
+ /**
35
+ * Failure bookkeeping for one LLM pass: how many units failed, how many were
36
+ * skipped once it gave up, and the human-readable reason it did.
37
+ *
38
+ * Single-threaded by construction — every mutation happens between awaits in the
39
+ * pass's own worker — so no locking is needed despite the concurrency above it.
40
+ */
41
+ export class LlmFailureGate {
42
+ /** Units (files) whose call failed. */
43
+ failed = 0;
44
+ /** Units never attempted because the pass had already given up. */
45
+ skipped = 0;
46
+ /** Why the pass stopped, when it did. */
47
+ fatal;
48
+ consecutive = 0;
49
+ /** True once the pass should stop issuing calls. */
50
+ get stopped() {
51
+ return this.fatal !== undefined;
52
+ }
53
+ /** Record a failed unit; sets {@link fatal} when this failure is the last straw. */
54
+ record(message) {
55
+ this.failed++;
56
+ this.consecutive++;
57
+ const terminal = terminalReason(message);
58
+ if (terminal) {
59
+ this.fatal = `${terminal} — stopped after ${this.failed} failed file(s). First error: ${message}`;
60
+ }
61
+ else if (this.consecutive >= MAX_CONSECUTIVE_FAILURES) {
62
+ this.fatal = `${this.consecutive} files in a row failed, so the pass stopped rather than keep calling. Last error: ${message}`;
63
+ }
64
+ }
65
+ /** Record a success — a failure run only counts while it is unbroken. */
66
+ succeeded() {
67
+ this.consecutive = 0;
68
+ }
69
+ /** Record a unit that was not attempted because the pass had stopped. */
70
+ skip() {
71
+ this.skipped++;
72
+ }
73
+ }
74
+ //# sourceMappingURL=failure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure.js","sourceRoot":"","sources":["../../src/ai/failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;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,oFAAoF;IACpF,MAAM,CAAC,OAAe;QACpB,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,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;QACpG,CAAC;aAAM,IAAI,IAAI,CAAC,WAAW,IAAI,wBAAwB,EAAE,CAAC;YACxD,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAsC,MAAM,YAAY,CAAC;AAM3G,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAKD,qBAAa,kBAAmB,YAAW,SAAS;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,IAAI,EAAE,yBAAyB;IAMrC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAuDrD,sFAAsF;IACtF,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;CA4BrB"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/ai/llm/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAsC,MAAM,YAAY,CAAC;AAM3G,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAKD,qBAAa,kBAAmB,YAAW,SAAS;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,IAAI,EAAE,yBAAyB;IAQrC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAuDrD,sFAAsF;IACtF,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;CA4BrB"}
@@ -15,6 +15,7 @@
15
15
  * signatures survive a multi-turn loop.
16
16
  */
17
17
  import Anthropic from "@anthropic-ai/sdk";
18
+ import { transportRetries } from "./types.js";
18
19
  const PROVIDER = "anthropic";
19
20
  const JSON_TOOL = "emit_json";
20
21
  const DEFAULT_MAX_TOKENS = 4096;
@@ -26,7 +27,9 @@ export class AnthropicChatModel {
26
27
  constructor(opts) {
27
28
  this.model = opts.model;
28
29
  this.label = opts.label ?? `${PROVIDER}:${opts.model}`;
29
- this.client = opts.client ?? new Anthropic({ apiKey: opts.apiKey, baseURL: opts.baseUrl });
30
+ this.client =
31
+ opts.client ??
32
+ new Anthropic({ apiKey: opts.apiKey, baseURL: opts.baseUrl, maxRetries: transportRetries() });
30
33
  }
31
34
  async create(req) {
32
35
  const system = [];
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/ai/llm/anthropic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/ai/llm/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
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
@@ -0,0 +1 @@
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"}