@sean.holung/minicode 0.3.11 → 0.4.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.
Files changed (191) hide show
  1. package/README.md +5 -3
  2. package/dist/scripts/run-benchmarks.js +7 -1
  3. package/dist/src/benchmark/runner.js +66 -3
  4. package/dist/src/cli/benchmark-run.js +1 -1
  5. package/dist/src/index.js +1 -1
  6. package/dist/src/indexer/code-map.js +16 -1
  7. package/dist/src/serve/agent-bridge.js +1 -1
  8. package/dist/src/session/session-store.js +1 -1
  9. package/dist/src/shared/symbol-resolution.js +24 -3
  10. package/dist/src/tools/find-path.js +1 -1
  11. package/dist/src/tools/find-references.js +1 -1
  12. package/dist/src/tools/get-dependencies.js +1 -1
  13. package/dist/src/tools/post-edit-diagnostics.js +185 -0
  14. package/dist/src/tools/read-symbol.js +2 -2
  15. package/dist/src/tools/registry.js +18 -3
  16. package/dist/src/tools/search-code-map.js +101 -9
  17. package/dist/src/ui/cli-ink.js +1 -1
  18. package/dist/tests/agent.test.js +1 -1
  19. package/dist/tests/context-indicator.test.js +1 -1
  20. package/dist/tests/file-tools.test.js +2 -2
  21. package/dist/tests/focus-tracker.test.js +1 -1
  22. package/dist/tests/guardrails.test.js +1 -1
  23. package/dist/tests/indexer.test.js +59 -28
  24. package/dist/tests/model-client-openai.test.js +1 -1
  25. package/dist/tests/model-selection.test.js +1 -1
  26. package/dist/tests/python-plugin.test.js +3 -3
  27. package/dist/tests/read-symbol.test.js +84 -10
  28. package/dist/tests/reasoning-effort.test.js +1 -1
  29. package/dist/tests/search-code-map.test.js +132 -1
  30. package/dist/tests/serve.integration.test.js +1 -1
  31. package/dist/tests/session-store.test.js +1 -1
  32. package/dist/tests/session.test.js +1 -1
  33. package/dist/tests/symbol-resolution.test.js +57 -0
  34. package/dist/tests/system-prompt.test.js +1 -1
  35. package/dist/tests/tool-registry.test.js +1 -1
  36. package/node_modules/@sean.holung/minicode-sdk/LICENSE +201 -0
  37. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/README.md +43 -22
  38. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/agent.d.ts +10 -1
  39. package/node_modules/@sean.holung/minicode-sdk/dist/src/agent/agent.d.ts.map +1 -0
  40. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/agent.js +18 -9
  41. package/node_modules/@sean.holung/minicode-sdk/dist/src/agent/agent.js.map +1 -0
  42. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/index.d.ts +1 -1
  43. package/node_modules/@sean.holung/minicode-sdk/dist/src/index.d.ts.map +1 -0
  44. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/index.js +1 -1
  45. package/node_modules/@sean.holung/minicode-sdk/dist/src/index.js.map +1 -0
  46. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.js +1 -1
  47. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.js.map +1 -1
  48. package/node_modules/@sean.holung/minicode-sdk/dist/src/model/client.d.ts.map +1 -0
  49. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/model/client.js +71 -4
  50. package/node_modules/@sean.holung/minicode-sdk/dist/src/model/client.js.map +1 -0
  51. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.d.ts.map +1 -1
  52. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.js +1 -1
  53. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.js.map +1 -1
  54. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.d.ts +59 -0
  55. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.d.ts.map +1 -0
  56. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.js +392 -0
  57. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.js.map +1 -0
  58. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file.d.ts +19 -0
  59. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file.d.ts.map +1 -0
  60. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/edit-file.js +14 -25
  61. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file.js.map +1 -0
  62. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.d.ts.map +1 -1
  63. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.js +11 -5
  64. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.js.map +1 -1
  65. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/run-command.d.ts.map +1 -1
  66. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/run-command.js +3 -0
  67. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/run-command.js.map +1 -0
  68. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/search.d.ts.map +1 -1
  69. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/search.js +52 -25
  70. package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/search.js.map +1 -0
  71. package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/package.json +6 -2
  72. package/node_modules/minicode-plugin-python/dist/src/index.d.ts +1 -1
  73. package/node_modules/minicode-plugin-python/dist/src/index.d.ts.map +1 -1
  74. package/node_modules/minicode-plugin-python/dist/tsconfig.tsbuildinfo +1 -1
  75. package/node_modules/minicode-plugin-python/package.json +2 -2
  76. package/package.json +3 -3
  77. package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.d.ts.map +0 -1
  78. package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.js.map +0 -1
  79. package/node_modules/@minicode/agent-sdk/dist/src/index.d.ts.map +0 -1
  80. package/node_modules/@minicode/agent-sdk/dist/src/index.js.map +0 -1
  81. package/node_modules/@minicode/agent-sdk/dist/src/model/client.d.ts.map +0 -1
  82. package/node_modules/@minicode/agent-sdk/dist/src/model/client.js.map +0 -1
  83. package/node_modules/@minicode/agent-sdk/dist/src/tools/edit-file.d.ts +0 -13
  84. package/node_modules/@minicode/agent-sdk/dist/src/tools/edit-file.d.ts.map +0 -1
  85. package/node_modules/@minicode/agent-sdk/dist/src/tools/edit-file.js.map +0 -1
  86. package/node_modules/@minicode/agent-sdk/dist/src/tools/run-command.js.map +0 -1
  87. package/node_modules/@minicode/agent-sdk/dist/src/tools/search.js.map +0 -1
  88. package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.d.ts +0 -2
  89. package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.d.ts.map +0 -1
  90. package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.js +0 -569
  91. package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.js.map +0 -1
  92. package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.d.ts +0 -2
  93. package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.d.ts.map +0 -1
  94. package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.js +0 -131
  95. package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.js.map +0 -1
  96. package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.d.ts +0 -2
  97. package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.d.ts.map +0 -1
  98. package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.js +0 -54
  99. package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.js.map +0 -1
  100. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.d.ts +0 -2
  101. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.d.ts.map +0 -1
  102. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.js +0 -64
  103. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.js.map +0 -1
  104. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.d.ts +0 -2
  105. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.d.ts.map +0 -1
  106. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.js +0 -350
  107. package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.js.map +0 -1
  108. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.d.ts +0 -2
  109. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.d.ts.map +0 -1
  110. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.js +0 -211
  111. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.js.map +0 -1
  112. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.d.ts +0 -2
  113. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.d.ts.map +0 -1
  114. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.js +0 -330
  115. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.js.map +0 -1
  116. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.d.ts +0 -2
  117. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.d.ts.map +0 -1
  118. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.js +0 -171
  119. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.js.map +0 -1
  120. package/node_modules/@minicode/agent-sdk/dist/tests/session.test.d.ts +0 -2
  121. package/node_modules/@minicode/agent-sdk/dist/tests/session.test.d.ts.map +0 -1
  122. package/node_modules/@minicode/agent-sdk/dist/tests/session.test.js +0 -226
  123. package/node_modules/@minicode/agent-sdk/dist/tests/session.test.js.map +0 -1
  124. package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.d.ts +0 -2
  125. package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.d.ts.map +0 -1
  126. package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.js +0 -212
  127. package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.js.map +0 -1
  128. package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.d.ts +0 -2
  129. package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.d.ts.map +0 -1
  130. package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.js +0 -76
  131. package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.js.map +0 -1
  132. package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.d.ts +0 -3
  133. package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.d.ts.map +0 -1
  134. package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.js +0 -20
  135. package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.js.map +0 -1
  136. package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.d.ts +0 -2
  137. package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.d.ts.map +0 -1
  138. package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.js +0 -72
  139. package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.js.map +0 -1
  140. package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.d.ts +0 -2
  141. package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.d.ts.map +0 -1
  142. package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.js +0 -69
  143. package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.js.map +0 -1
  144. package/node_modules/@minicode/agent-sdk/dist/tsconfig.tsbuildinfo +0 -1
  145. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.d.ts +0 -0
  146. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.d.ts.map +0 -0
  147. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.js +0 -0
  148. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.js.map +0 -0
  149. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.d.ts +0 -0
  150. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.d.ts.map +0 -0
  151. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.js +0 -0
  152. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.js.map +0 -0
  153. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.d.ts +0 -0
  154. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.d.ts.map +0 -0
  155. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.js +0 -0
  156. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.js.map +0 -0
  157. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.d.ts +0 -0
  158. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.d.ts.map +0 -0
  159. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.js +0 -0
  160. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.js.map +0 -0
  161. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.d.ts +0 -0
  162. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.d.ts.map +0 -0
  163. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/model/client.d.ts +0 -0
  164. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.d.ts +0 -0
  165. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.d.ts +0 -0
  166. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.d.ts.map +0 -0
  167. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.js +0 -0
  168. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.js.map +0 -0
  169. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.d.ts +0 -0
  170. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.d.ts.map +0 -0
  171. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.js +0 -0
  172. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.js.map +0 -0
  173. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.d.ts +0 -0
  174. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.d.ts.map +0 -0
  175. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.js +0 -0
  176. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.js.map +0 -0
  177. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.d.ts +0 -0
  178. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.d.ts.map +0 -0
  179. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.js +0 -0
  180. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.js.map +0 -0
  181. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.d.ts +0 -0
  182. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.d.ts +0 -0
  183. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.d.ts.map +0 -0
  184. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.js +0 -0
  185. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.js.map +0 -0
  186. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/run-command.d.ts +0 -0
  187. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/search.d.ts +0 -0
  188. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.d.ts +0 -0
  189. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.d.ts.map +0 -0
  190. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.js +0 -0
  191. /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.js.map +0 -0
package/README.md CHANGED
@@ -4,9 +4,11 @@
4
4
 
5
5
  A graph-native coding agent and code exploration environment built around structural context optimization that leverages symbol-aware retrieval, dependency graphs, and targeted context. It started as a way to make local models viable under tighter context budgets, and it now also works well with hosted frontier models through the same runtime, web UI, and OpenAI-compatible serve mode.
6
6
 
7
- minicode is built on a simple bet: models perform better when you give them less, but better context. Bloated context directly degrades output quality: attention dilutes, positional biases cause mid-context information loss, and inference latency grows as token count increases.
7
+ minicode is built on a simple bet: models perform better when you give them more useful context, not less raw context. Bloated context dilutes attention; targeted context lets the model build a structural picture of the codebase before answering.
8
8
 
9
- Read operations dominate token usage in typical agent sessions; minicode addresses this by optimizing for **specific languages**. It indexes your project at startup with language plugins, injects a compact **code map** (signatures only) into the system prompt, and exposes symbol-level tools (`read_symbol`, `find_references`, `get_dependencies`) so the model reads only what it needs instead of entire files. This also enables the agent to walk the code structurally to gain a better understanding of the codebase at a structural level. TypeScript and JavaScript support come built-in, with custom language plugins leaving room for broader language support over time.
9
+ Read operations dominate token usage in typical agent sessions; minicode addresses this by optimizing for **specific languages**. It indexes your project at startup with language plugins, injects a compact **code map** (signatures only) into the system prompt, and exposes symbol-level tools (`read_symbol`, `find_references`, `get_dependencies`) so the model can walk the code structurally instead of grepping and reading entire files. TypeScript and JavaScript support come built-in, with custom language plugins leaving room for broader language support over time.
10
+
11
+ In our own ablation on a 25-task local benchmark suite (see [`benchmarks/RESULTS-GEMMA-4.md`](benchmarks/RESULTS-GEMMA-4.md)), turning the structural tools on raises pass rate from 47% to 61% on Gemma 4 26B-A4B — a +14.7 pp lift driven mostly by **comprehension-heavy tasks** (planning and refactor work where the agent has to trace relationships across files). The tradeoff is honest: graph tools cost about 30% more tokens per task when actually used, but they buy correctness on the kinds of questions where reading whole files just runs out the model's reasoning budget without building a structural picture.
10
12
 
11
13
  _Run `minicode serve` to get the web UI on localhost: chat, tool activity, session controls, model switching, symbol focus, annotations, and a live dependency graph._
12
14
 
@@ -118,7 +120,7 @@ For agent-loop internals (session lifecycle, tool execution, streaming, loop det
118
120
 
119
121
  For the proposed reusable package architecture and public interfaces for a standalone runtime SDK, see [docs/SDK_SPEC.md](docs/SDK_SPEC.md).
120
122
 
121
- minicode reduces token usage by indexing your project and providing targeted tools:
123
+ minicode replaces grep-and-read-whole-file with symbol-level navigation:
122
124
 
123
125
  - **Code map** — A compact project skeleton (signatures only) is injected into the system prompt so the model can orient itself without reading full files.
124
126
  - `read_symbol` — Read a specific function or class by name, with referenced types.
@@ -19,7 +19,7 @@ import { readFileSync, existsSync } from "node:fs";
19
19
  import path from "node:path";
20
20
  import { homedir } from "node:os";
21
21
  import { writeFile } from "node:fs/promises";
22
- import { createModelClient, } from "@minicode/agent-sdk";
22
+ import { createModelClient, } from "@sean.holung/minicode-sdk";
23
23
  import { parse as parseDotenv } from "dotenv";
24
24
  import { loadBenchmarkTasks, loadBenchmarkTask } from "../src/benchmark/task-loader.js";
25
25
  import { runBenchmarkSuite } from "../src/benchmark/runner.js";
@@ -111,6 +111,12 @@ export function buildConfig(options = {}) {
111
111
  maxToolOutputChars: getNumberSetting(getShellOverride("MAX_TOOL_OUTPUT_CHARS"), fileConfig.maxToolOutputChars, 8000),
112
112
  openAiBaseUrl,
113
113
  ...(openAiApiKey ? { openAiApiKey } : {}),
114
+ // Explicit match to the loadAgentConfig default so benchmark runs see
115
+ // the same static-system-prompt behavior that real CLI/serve users
116
+ // get. Omitting this routes through the SDK fallback in agent.ts and,
117
+ // depending on its semantics, may flip behavior unintentionally — see
118
+ // PR #138 follow-up for the original issue.
119
+ enableDynamicPrompt: false,
114
120
  };
115
121
  }
116
122
  /* ------------------------------------------------------------------ */
@@ -5,10 +5,10 @@
5
5
  * over tool-call instrumentation and trace capture.
6
6
  */
7
7
  import { execSync } from "node:child_process";
8
- import { cp, mkdtemp, rm } from "node:fs/promises";
8
+ import { cp, mkdir, mkdtemp, rm, symlink, stat } from "node:fs/promises";
9
9
  import { tmpdir } from "node:os";
10
10
  import path from "node:path";
11
- import { CodingAgent, Session, ToolRegistry, } from "@minicode/agent-sdk";
11
+ import { CodingAgent, Session, ToolRegistry, } from "@sean.holung/minicode-sdk";
12
12
  const COPY_SKIP_NAMES = new Set([
13
13
  ".git",
14
14
  "node_modules",
@@ -16,6 +16,24 @@ const COPY_SKIP_NAMES = new Set([
16
16
  "build",
17
17
  "coverage",
18
18
  ]);
19
+ /**
20
+ * Narrow symlinks (relative to the source workspace) injected into the
21
+ * isolated workspace after the bulk copy. Goal: make `tsc --noEmit`
22
+ * resolve types for the post-edit diagnostic, *without* enabling the
23
+ * full test runner.
24
+ *
25
+ * - `node_modules/@types` lets tsc resolve `@types/node` etc. (the
26
+ * typescript binary itself runs from the source workspace and finds
27
+ * its lib files relative to its own install location, so we don't
28
+ * need to symlink it into the temp tree.)
29
+ *
30
+ * We intentionally do NOT symlink `node_modules/.bin` or general deps,
31
+ * so commands like `npm test` fail fast with "tsc: not found" rather
32
+ * than running the full pretest+test suite and burning per-task
33
+ * tool-call budget on infrastructure noise. See benchmarks/RESULTS for
34
+ * the symlink scope conversation.
35
+ */
36
+ const SYMLINK_FROM_SOURCE = ["node_modules/@types"];
19
37
  function getGitCommitSha() {
20
38
  try {
21
39
  return execSync("git rev-parse HEAD", { encoding: "utf8" }).trim();
@@ -45,6 +63,31 @@ function shouldCopyPath(src) {
45
63
  const name = path.basename(src);
46
64
  return !COPY_SKIP_NAMES.has(name);
47
65
  }
66
+ async function linkSharedDirs(sourceRoot, workspaceRoot) {
67
+ for (const rel of SYMLINK_FROM_SOURCE) {
68
+ const sourcePath = path.join(sourceRoot, rel);
69
+ try {
70
+ const s = await stat(sourcePath);
71
+ if (!s.isDirectory())
72
+ continue;
73
+ }
74
+ catch {
75
+ continue;
76
+ }
77
+ const linkPath = path.join(workspaceRoot, rel);
78
+ // Ensure parent dir exists — e.g. `node_modules/@types` requires
79
+ // `node_modules/` to be created first (we don't bulk-copy it).
80
+ await mkdir(path.dirname(linkPath), { recursive: true });
81
+ try {
82
+ await symlink(sourcePath, linkPath, "dir");
83
+ }
84
+ catch {
85
+ // Best-effort: a parent path may not exist or the link target
86
+ // already does. Skip silently — tools that need these resolve
87
+ // to a useful error if missing.
88
+ }
89
+ }
90
+ }
48
91
  async function prepareTaskWorkspace(task, options) {
49
92
  const sourceWorkspaceRoot = resolveSourceWorkspaceRoot(task, options);
50
93
  if (options.isolateWorkspace === false) {
@@ -60,6 +103,7 @@ async function prepareTaskWorkspace(task, options) {
60
103
  recursive: true,
61
104
  filter: shouldCopyPath,
62
105
  });
106
+ await linkSharedDirs(sourceWorkspaceRoot, isolatedWorkspaceRoot);
63
107
  return {
64
108
  sourceWorkspaceRoot,
65
109
  workspaceRoot: isolatedWorkspaceRoot,
@@ -74,7 +118,26 @@ function getTrackedSymbolNames(toolName, input) {
74
118
  .filter((value) => typeof value === "string" && value.length > 0);
75
119
  return [...new Set(names)];
76
120
  }
77
- const name = input.symbol ?? input.symbolName ?? input.name ?? input.query;
121
+ // search_code_map uses `pattern`, the other graph tools use one of
122
+ // `name`/`symbol`/`symbolName`/`query`. Without `pattern` in this
123
+ // fallback chain, a search_code_map call would fail to register a
124
+ // queried symbol even when the pattern is the literal symbol name —
125
+ // and any rubric using `expectedSymbols` would then incorrectly fail
126
+ // tasks that were answered correctly via the code-map search.
127
+ //
128
+ // We register the pattern verbatim without resolving it against the
129
+ // project index. The downstream `expectedSymbols` matcher in
130
+ // `evaluator.ts` uses `queried.includes(expected)`, so a too-narrow
131
+ // query (e.g. `"Tool"` against `expectedSymbols: ["ToolRegistry"]`)
132
+ // correctly fails to satisfy the rubric. A too-broad query
133
+ // (`"ToolRegistryFactory"` against `["ToolRegistry"]`) does satisfy
134
+ // it — that asymmetry is intentional and matches how a user-facing
135
+ // search-by-substring is normally interpreted.
136
+ const name = input.symbol ??
137
+ input.symbolName ??
138
+ input.name ??
139
+ input.query ??
140
+ input.pattern;
78
141
  return typeof name === "string" && name.length > 0 ? [name] : [];
79
142
  }
80
143
  function trackStructuralFileReads(toolName, projectIndex, input, filesRead) {
@@ -1,7 +1,7 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
- import { CodingAgent, createModelClient, } from "@minicode/agent-sdk";
4
+ import { CodingAgent, createModelClient, } from "@sean.holung/minicode-sdk";
5
5
  import { getConfigSetupMessage } from "../agent/config.js";
6
6
  import { buildBenchmarkAgentConfig, resolveBenchmarkEnv, } from "../benchmark/config.js";
7
7
  import { collectWorkspaceChanges, writeWorkspaceDiff, } from "../benchmark/workspace-changes.js";
package/dist/src/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import process from "node:process";
3
3
  import { writeFile } from "node:fs/promises";
4
4
  import { createInterface } from "node:readline/promises";
5
- import { CodingAgent, createModelClient } from "@minicode/agent-sdk";
5
+ import { CodingAgent, createModelClient } from "@sean.holung/minicode-sdk";
6
6
  import { loadAgentConfig, getConfigSetupMessage } from "./agent/config.js";
7
7
  import { listSessions, loadSession, loadSessionByLabel, saveSession, } from "./session/session-store.js";
8
8
  import { computeFileHashes, getWorkspaceCacheDir, loadIndex, saveIndex, } from "./indexer/cache.js";
@@ -10,6 +10,20 @@ function formatSymbol(symbol, indent, isMethod) {
10
10
  }
11
11
  return `${indent}${symbol.kind} ${getSymbolDisplayName(symbol)}\n${indent} ${symbol.signature}`;
12
12
  }
13
+ // Terser per-line format: "{indent}- {displayName} ({kind})". Roughly halves
14
+ // the chars/symbol cost vs the legacy full-signature format, doubling
15
+ // coverage at the same token budget. Default since Experiment 17 (May 2026)
16
+ // — pooled n=6 benchmark showed +3.4pp overall and +16.7pp planning vs the
17
+ // full format, matching the dynamic-prompt baseline with zero cache cost.
18
+ // Opt out with MINICODE_CODE_MAP_FORMAT=full.
19
+ function formatSymbolNamed(symbol, indent) {
20
+ return `${indent}- ${getSymbolDisplayName(symbol)} (${symbol.kind})`;
21
+ }
22
+ function selectFormatter() {
23
+ return process.env.MINICODE_CODE_MAP_FORMAT === "full"
24
+ ? formatSymbol
25
+ : formatSymbolNamed;
26
+ }
13
27
  function isEntryPointFile(filePath) {
14
28
  const name = filePath.replace(/\\/g, "/");
15
29
  return /(?:^|\/)index\.[jt]sx?$/.test(name);
@@ -97,6 +111,7 @@ function createSymbolRanker(adjacency, focusSymbols) {
97
111
  export function generateCodeMap(symbolsByFile, tokenBudget = DEFAULT_TOKEN_BUDGET, dependencyEdges, focusSymbols) {
98
112
  const totalCount = [...symbolsByFile.values()].reduce((sum, syms) => sum + syms.length, 0);
99
113
  const lines = ["# Project Code Map", ""];
114
+ const formatter = selectFormatter();
100
115
  const adjacency = dependencyEdges
101
116
  ? buildAdjacencyMaps(dependencyEdges)
102
117
  : { byFrom: new Map(), byTo: new Map() };
@@ -137,7 +152,7 @@ export function generateCodeMap(symbolsByFile, tokenBudget = DEFAULT_TOKEN_BUDGE
137
152
  else if (!isMethod) {
138
153
  currentClass = null;
139
154
  }
140
- const block = formatSymbol(symbol, indent, isMethod);
155
+ const block = formatter(symbol, indent, isMethod);
141
156
  const blockTokens = estimateTokens(block);
142
157
  if (totalTokens + blockTokens > tokenBudget) {
143
158
  truncatedSymbols += 1;
@@ -1,4 +1,4 @@
1
- import { CodingAgent, createModelClient } from "@minicode/agent-sdk";
1
+ import { CodingAgent, createModelClient } from "@sean.holung/minicode-sdk";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { isGatedTool, shouldAutoAllow, } from "../auto-allow.js";
4
4
  import { readFile } from "node:fs/promises";
@@ -1,7 +1,7 @@
1
1
  import { mkdir, readdir, readFile, unlink, writeFile } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import { Session } from "@minicode/agent-sdk";
4
+ import { Session } from "@sean.holung/minicode-sdk";
5
5
  let sessionsDir = path.join(os.homedir(), ".minicode", "sessions");
6
6
  export class DuplicateSessionLabelError extends Error {
7
7
  label;
@@ -12,13 +12,34 @@ export function resolveSymbolInput(projectIndex, name) {
12
12
  export function formatSymbolMatch(match) {
13
13
  return `${getSymbolDisplayName(match)} (${match.kind}) — ${match.filePath}:${match.startLine} — qualified: ${match.qualifiedName}`;
14
14
  }
15
+ /**
16
+ * Cap on how many disambiguation entries we render. The output flows
17
+ * through the agent's generic tool-output truncator, which clips by
18
+ * character count — including in the middle of a qualified name. That
19
+ * leaves the model with an unparseable lookup key, which it then
20
+ * guesses at, producing a "not found" loop. Bounding by entry count
21
+ * here means each shown match has its full qualified name intact, and
22
+ * generic char-truncation never has occasion to fire on the result.
23
+ *
24
+ * 12 covers virtually every real ambiguity (a name that genuinely
25
+ * matches more is too generic to be useful — the agent should refine
26
+ * regardless). Each entry is ~150-300 chars, so 12 entries fit
27
+ * comfortably under typical maxToolOutputChars caps.
28
+ */
29
+ const MAX_AMBIGUOUS_MATCHES = 12;
15
30
  export function formatAmbiguousSymbolMatches(toolName, name, matches) {
16
- return [
31
+ const shown = matches.slice(0, MAX_AMBIGUOUS_MATCHES);
32
+ const elided = matches.length - shown.length;
33
+ const lines = [
17
34
  `Symbol "${name}" is ambiguous; ${matches.length} matches were found.`,
18
35
  `Re-run ${toolName} with one of these qualified or disambiguated names:`,
19
36
  "",
20
- ...matches.map((match) => `- ${formatSymbolMatch(match)}`),
21
- ].join("\n");
37
+ ...shown.map((match) => `- ${formatSymbolMatch(match)}`),
38
+ ];
39
+ if (elided > 0) {
40
+ lines.push("", `[... and ${elided} more match(es) not shown. Refine the name (e.g. include the file path or use the qualified form like "Foo#class@path/to/file.ts") to narrow further.]`);
41
+ }
42
+ return lines.join("\n");
22
43
  }
23
44
  export function serializeSymbolMatch(match) {
24
45
  return {
@@ -1,4 +1,4 @@
1
- import { expectNonEmptyString, expectOptionalNumber } from "@minicode/agent-sdk";
1
+ import { expectNonEmptyString, expectOptionalNumber } from "@sean.holung/minicode-sdk";
2
2
  import { getSymbolDisplayName } from "../indexer/symbol-names.js";
3
3
  import { formatAmbiguousSymbolMatches, resolveSymbolInput, } from "../shared/symbol-resolution.js";
4
4
  export function createFindPathTool(projectIndex) {
@@ -1,4 +1,4 @@
1
- import { expectNonEmptyString, expectOptionalNumber } from "@minicode/agent-sdk";
1
+ import { expectNonEmptyString, expectOptionalNumber } from "@sean.holung/minicode-sdk";
2
2
  import { getSymbolDisplayName } from "../indexer/symbol-names.js";
3
3
  import { formatAmbiguousSymbolMatches, resolveSymbolInput, } from "../shared/symbol-resolution.js";
4
4
  const DEFAULT_LIMIT = 50;
@@ -1,4 +1,4 @@
1
- import { expectNonEmptyString, expectOptionalNumber } from "@minicode/agent-sdk";
1
+ import { expectNonEmptyString, expectOptionalNumber } from "@sean.holung/minicode-sdk";
2
2
  import { getSymbolDisplayName } from "../indexer/symbol-names.js";
3
3
  import { formatAmbiguousSymbolMatches, resolveSymbolInput, } from "../shared/symbol-resolution.js";
4
4
  export function createGetDependenciesTool(projectIndex) {
@@ -0,0 +1,185 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, stat } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ import { getWorkspaceCacheDir } from "../indexer/cache.js";
6
+ const require = createRequire(import.meta.url);
7
+ let cachedTscPath;
8
+ function resolveTscPath() {
9
+ if (cachedTscPath !== undefined)
10
+ return cachedTscPath;
11
+ try {
12
+ cachedTscPath = require.resolve("typescript/bin/tsc");
13
+ }
14
+ catch {
15
+ cachedTscPath = null;
16
+ }
17
+ return cachedTscPath;
18
+ }
19
+ const TS_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"]);
20
+ const MAX_PER_FILE = 20;
21
+ const TSC_TIMEOUT_MS = 15_000;
22
+ const DISABLE_ENV = "MINICODE_DISABLE_POST_EDIT_DIAGNOSTICS";
23
+ /**
24
+ * Walk up from `startDir` looking for the nearest tsconfig.json. Stops at
25
+ * `workspaceRoot` (inclusive). Returns null if none found.
26
+ */
27
+ async function findNearestTsconfig(startDir, workspaceRoot) {
28
+ const root = path.resolve(workspaceRoot);
29
+ let dir = path.resolve(startDir);
30
+ while (true) {
31
+ const candidate = path.join(dir, "tsconfig.json");
32
+ try {
33
+ const s = await stat(candidate);
34
+ if (s.isFile())
35
+ return candidate;
36
+ }
37
+ catch {
38
+ // not present, keep walking
39
+ }
40
+ if (dir === root)
41
+ return null;
42
+ const parent = path.dirname(dir);
43
+ if (parent === dir)
44
+ return null;
45
+ dir = parent;
46
+ }
47
+ }
48
+ const TSC_LINE_RE = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s*(.*)$/;
49
+ function parseTscOutput(output, projectDir) {
50
+ const lines = output.split(/\r?\n/);
51
+ const out = [];
52
+ for (const line of lines) {
53
+ const m = line.match(TSC_LINE_RE);
54
+ if (!m)
55
+ continue;
56
+ const [, rawFile, ln, col, sev, code, msg] = m;
57
+ if (!rawFile || !ln || !col || !sev || !code || msg === undefined)
58
+ continue;
59
+ const absFile = path.isAbsolute(rawFile)
60
+ ? rawFile
61
+ : path.resolve(projectDir, rawFile);
62
+ out.push({
63
+ file: absFile,
64
+ line: Number(ln),
65
+ col: Number(col),
66
+ severity: sev === "error" ? "error" : "warning",
67
+ code,
68
+ message: msg.trim(),
69
+ });
70
+ }
71
+ return out;
72
+ }
73
+ async function runTsc(tsconfigPath, workspaceRoot) {
74
+ const cacheDir = path.join(getWorkspaceCacheDir(workspaceRoot), "diagnostics");
75
+ await mkdir(cacheDir, { recursive: true });
76
+ const tsbuildinfo = path.join(cacheDir, `${path.basename(path.dirname(tsconfigPath))}.tsbuildinfo`);
77
+ const tscPath = resolveTscPath();
78
+ if (!tscPath)
79
+ return null;
80
+ return await new Promise((resolve) => {
81
+ const child = spawn(process.execPath, [
82
+ tscPath,
83
+ "--noEmit",
84
+ "--pretty",
85
+ "false",
86
+ "--incremental",
87
+ "--tsBuildInfoFile",
88
+ tsbuildinfo,
89
+ "-p",
90
+ tsconfigPath,
91
+ ], {
92
+ cwd: workspaceRoot,
93
+ stdio: ["ignore", "pipe", "pipe"],
94
+ env: process.env,
95
+ });
96
+ let stdout = "";
97
+ let stderr = "";
98
+ let settled = false;
99
+ const timer = setTimeout(() => {
100
+ if (settled)
101
+ return;
102
+ settled = true;
103
+ child.kill("SIGKILL");
104
+ resolve(null);
105
+ }, TSC_TIMEOUT_MS);
106
+ child.stdout?.on("data", (chunk) => {
107
+ stdout += chunk.toString("utf8");
108
+ });
109
+ child.stderr?.on("data", (chunk) => {
110
+ stderr += chunk.toString("utf8");
111
+ });
112
+ child.on("error", () => {
113
+ if (settled)
114
+ return;
115
+ settled = true;
116
+ clearTimeout(timer);
117
+ resolve(null);
118
+ });
119
+ child.on("close", () => {
120
+ if (settled)
121
+ return;
122
+ settled = true;
123
+ clearTimeout(timer);
124
+ const combined = stdout + (stderr ? `\n${stderr}` : "");
125
+ resolve({
126
+ diagnostics: parseTscOutput(combined, path.dirname(tsconfigPath)),
127
+ });
128
+ });
129
+ });
130
+ }
131
+ function formatDiagnostics(filePath, diags) {
132
+ const errors = diags.filter((d) => d.severity === "error");
133
+ if (errors.length === 0)
134
+ return undefined;
135
+ const limited = errors.slice(0, MAX_PER_FILE);
136
+ const more = errors.length - MAX_PER_FILE;
137
+ const suffix = more > 0 ? `\n... and ${more} more` : "";
138
+ const body = limited
139
+ .map((d) => `ERROR [${d.line}:${d.col}] ${d.message}`)
140
+ .join("\n");
141
+ return `<diagnostics file="${filePath}">\n${body}${suffix}\n</diagnostics>`;
142
+ }
143
+ /**
144
+ * Serialize diagnostic runs per workspace+tsconfig so concurrent edits don't
145
+ * trigger overlapping tsc processes that fight over the same .tsbuildinfo.
146
+ */
147
+ const inflightByKey = new Map();
148
+ function serializedRun(tsconfigPath, workspaceRoot) {
149
+ const key = `${workspaceRoot}::${tsconfigPath}`;
150
+ const prev = inflightByKey.get(key) ?? Promise.resolve(null);
151
+ const next = prev.then(() => runTsc(tsconfigPath, workspaceRoot));
152
+ inflightByKey.set(key, next.finally(() => {
153
+ if (inflightByKey.get(key) === next)
154
+ inflightByKey.delete(key);
155
+ }));
156
+ return next;
157
+ }
158
+ /**
159
+ * Run tsc against the nearest tsconfig and return an opencode-style
160
+ * `<diagnostics file="...">` block listing errors in the touched file. Returns
161
+ * undefined when there are no errors, when the file is not a TypeScript-family
162
+ * file, when no tsconfig is found, or when the run fails. Never throws.
163
+ */
164
+ export async function buildPostEditDiagnostic(filePath, workspaceRoot) {
165
+ if (process.env[DISABLE_ENV] === "1")
166
+ return undefined;
167
+ const ext = path.extname(filePath).toLowerCase();
168
+ if (!TS_EXTENSIONS.has(ext))
169
+ return undefined;
170
+ try {
171
+ const tsconfig = await findNearestTsconfig(path.dirname(filePath), workspaceRoot);
172
+ if (!tsconfig)
173
+ return undefined;
174
+ const result = await serializedRun(tsconfig, workspaceRoot);
175
+ if (!result)
176
+ return undefined;
177
+ const absTouched = path.resolve(filePath);
178
+ const forFile = result.diagnostics.filter((d) => path.resolve(d.file) === absTouched);
179
+ const displayPath = path.relative(workspaceRoot, absTouched) || filePath;
180
+ return formatDiagnostics(displayPath, forFile);
181
+ }
182
+ catch {
183
+ return undefined;
184
+ }
185
+ }
@@ -1,5 +1,5 @@
1
1
  import { readFile, stat } from "node:fs/promises";
2
- import { resolveWorkspacePath, validateFileReadSize, expectNonEmptyString, expectOptionalBoolean, } from "@minicode/agent-sdk";
2
+ import { resolveWorkspacePath, validateFileReadSize, expectNonEmptyString, expectOptionalBoolean, } from "@sean.holung/minicode-sdk";
3
3
  import { getSymbolDisplayName } from "../indexer/symbol-names.js";
4
4
  import { formatAmbiguousSymbolMatches, resolveSymbolInput, } from "../shared/symbol-resolution.js";
5
5
  const LEADING_CONTEXT_LINES = 3;
@@ -30,7 +30,7 @@ export function createReadSymbolTool(config, projectIndex) {
30
30
  const includeBody = expectOptionalBoolean(input, "includeBody") ?? true;
31
31
  const resolution = resolveSymbolInput(projectIndex, name);
32
32
  if (resolution.status === "missing") {
33
- return `Symbol "${name}" not found in the project index. Try using search to find it, or use read_file to read the full file.`;
33
+ return `Symbol "${name}" not found in the project index. Try search_code_map first (grep over symbol signatures), then fall back to search (text/regex) or read_file (whole file) only if symbol-based retrieval can't find what you need.`;
34
34
  }
35
35
  if (resolution.status === "ambiguous") {
36
36
  return formatAmbiguousSymbolMatches("read_symbol", name, resolution.matches);
@@ -1,9 +1,11 @@
1
- import { ToolRegistry, createReadFileTool, createWriteFileTool, createEditFileTool, createSearchTool, createListFilesTool, createRunCommandTool, } from "@minicode/agent-sdk";
1
+ import path from "node:path";
2
+ import { ToolRegistry, createReadFileTool, createWriteFileTool, createEditFileTool, createSearchTool, createListFilesTool, createRunCommandTool, } from "@sean.holung/minicode-sdk";
2
3
  import { createFindReferencesTool } from "./find-references.js";
3
4
  import { createGetDependenciesTool } from "./get-dependencies.js";
4
5
  import { createReadSymbolTool } from "./read-symbol.js";
5
6
  import { createFindPathTool } from "./find-path.js";
6
7
  import { createSearchCodeMapTool } from "./search-code-map.js";
8
+ import { buildPostEditDiagnostic } from "./post-edit-diagnostics.js";
7
9
  export { ToolRegistry };
8
10
  async function reindexBestEffort(projectIndex, relPath, content) {
9
11
  try {
@@ -16,12 +18,23 @@ async function reindexBestEffort(projectIndex, relPath, content) {
16
18
  /**
17
19
  * Create a ToolRegistry with the SDK's core tools plus indexer-specific tools
18
20
  * when a ProjectIndex is available.
21
+ *
22
+ * Set `MINICODE_TOOL_PROFILE=file-search-only` (or pass it via the env to a
23
+ * benchmark run) to omit the five graph-aware tools (`read_symbol`,
24
+ * `find_references`, `get_dependencies`, `find_path`, `search_code_map`).
25
+ * The code map remains in the system prompt — only the interactive tools
26
+ * are gated. This is the right shape for an `all-tools` vs.
27
+ * `file-search-only` ablation per `benchmarks/STRATEGY.md`.
19
28
  */
20
29
  export function createToolRegistry(config, projectIndex) {
21
30
  const hooks = projectIndex
22
31
  ? {
23
32
  afterWrite: (relPath, content) => reindexBestEffort(projectIndex, relPath, content),
24
- afterEdit: (relPath, content) => reindexBestEffort(projectIndex, relPath, content),
33
+ afterEdit: async (absPath, content) => {
34
+ const relPath = path.relative(config.workspaceRoot, absPath);
35
+ await reindexBestEffort(projectIndex, relPath, content);
36
+ return await buildPostEditDiagnostic(absPath, config.workspaceRoot);
37
+ },
25
38
  afterCommand: async () => {
26
39
  await projectIndex.refreshFromWorkspace();
27
40
  },
@@ -35,7 +48,9 @@ export function createToolRegistry(config, projectIndex) {
35
48
  createListFilesTool(config),
36
49
  createRunCommandTool(config, hooks ? { afterCommand: hooks.afterCommand } : undefined),
37
50
  ];
38
- if (projectIndex) {
51
+ const includeGraphTools = projectIndex !== undefined &&
52
+ process.env.MINICODE_TOOL_PROFILE !== "file-search-only";
53
+ if (includeGraphTools && projectIndex) {
39
54
  tools.splice(1, 0, createReadSymbolTool(config, projectIndex));
40
55
  tools.splice(2, 0, createFindReferencesTool(projectIndex));
41
56
  tools.splice(3, 0, createGetDependenciesTool(projectIndex));