ashlr-plugin 1.36.4

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 (499) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +39 -0
  3. package/.claude-plugin/plugin.json +36 -0
  4. package/.codex/agents/README.md +9 -0
  5. package/.codex/agents/ashlr-explorer.md +36 -0
  6. package/.codex/agents/ashlr-worker.md +38 -0
  7. package/.codex-plugin/plugin.json +45 -0
  8. package/.mcp.json +14 -0
  9. package/LICENSE +21 -0
  10. package/README.md +311 -0
  11. package/agents/ashlr-code.md +69 -0
  12. package/agents/ashlr-explore.md +44 -0
  13. package/agents/ashlr-plan.md +54 -0
  14. package/bench/fixtures/notebook-large.ipynb +660 -0
  15. package/bench/fixtures/notebook-small.ipynb +39 -0
  16. package/bench/fixtures/tasklist-closed.json +62 -0
  17. package/bench/fixtures/tasklist-large-mixed.json +27 -0
  18. package/bench/fixtures/tasklist-sprint-open.json +79 -0
  19. package/bench/fixtures/websearch-bun-runtime.json +32 -0
  20. package/bench/fixtures/websearch-react-hooks-patterns.json +32 -0
  21. package/bench/fixtures/websearch-typescript-generics.json +38 -0
  22. package/bench/refs/node-sdk/.refrev +1 -0
  23. package/bench/refs/node-sdk/README.md +17 -0
  24. package/bench/refs/node-sdk/src/agent/agent.ts +163 -0
  25. package/bench/refs/node-sdk/src/agent/create-agent-ui-stream.ts +83 -0
  26. package/bench/refs/node-sdk/src/agent/tool-loop-agent.ts +238 -0
  27. package/bench/refs/node-sdk/src/embed/embed-many.ts +409 -0
  28. package/bench/refs/node-sdk/src/embed/embed-result.ts +50 -0
  29. package/bench/refs/node-sdk/src/embed/embed.ts +257 -0
  30. package/bench/refs/node-sdk/src/error/invalid-argument-error.ts +34 -0
  31. package/bench/refs/node-sdk/src/error/invalid-tool-input-error.ts +33 -0
  32. package/bench/refs/node-sdk/src/error/no-object-generated-error.ts +70 -0
  33. package/bench/refs/node-sdk/src/generate-image/generate-image-result.ts +42 -0
  34. package/bench/refs/node-sdk/src/generate-image/generate-image.ts +362 -0
  35. package/bench/refs/node-sdk/src/generate-object/generate-object-result.ts +67 -0
  36. package/bench/refs/node-sdk/src/generate-object/generate-object.ts +501 -0
  37. package/bench/refs/node-sdk/src/generate-object/inject-json-instruction.ts +30 -0
  38. package/bench/refs/node-sdk/src/generate-object/output-strategy.ts +415 -0
  39. package/bench/refs/node-sdk/src/generate-object/repair-text.ts +12 -0
  40. package/bench/refs/node-sdk/src/generate-object/stream-object.ts +960 -0
  41. package/bench/refs/node-sdk/src/generate-speech/generate-speech.ts +191 -0
  42. package/bench/refs/node-sdk/src/generate-text/generate-text.ts +1484 -0
  43. package/bench/refs/node-sdk/src/generate-text/stream-text.ts +2708 -0
  44. package/bench/refs/node-sdk/src/generate-text/tool-call.ts +59 -0
  45. package/bench/refs/node-sdk/src/generate-text/tool-result.ts +36 -0
  46. package/bench/refs/node-sdk/src/telemetry/create-telemetry-dispatcher.ts +146 -0
  47. package/bench/refs/node-sdk/src/telemetry/telemetry-registry.ts +15 -0
  48. package/bench/refs/node-sdk/src/telemetry/telemetry.ts +223 -0
  49. package/bench/refs/node-sdk/src/util/async-iterable-stream.ts +94 -0
  50. package/bench/refs/node-sdk/src/util/consume-stream.ts +31 -0
  51. package/bench/refs/node-sdk/src/util/create-stitchable-stream.ts +112 -0
  52. package/bench/refs/node-sdk/src/util/download/download.ts +74 -0
  53. package/bench/refs/node-sdk/src/util/error-handler.ts +1 -0
  54. package/bench/refs/node-sdk/src/util/fix-json.ts +401 -0
  55. package/bench/refs/node-sdk/src/util/merge-objects.ts +79 -0
  56. package/bench/refs/python-lib/.refrev +1 -0
  57. package/bench/refs/python-lib/README.md +18 -0
  58. package/bench/refs/python-lib/pandas/core/_numba/executor.py +249 -0
  59. package/bench/refs/python-lib/pandas/core/_numba/extensions.py +586 -0
  60. package/bench/refs/python-lib/pandas/core/_numba/kernels/mean_.py +198 -0
  61. package/bench/refs/python-lib/pandas/core/_numba/kernels/min_max_.py +178 -0
  62. package/bench/refs/python-lib/pandas/core/_numba/kernels/sum_.py +255 -0
  63. package/bench/refs/python-lib/pandas/core/_numba/kernels/var_.py +251 -0
  64. package/bench/refs/python-lib/pandas/core/accessor.py +520 -0
  65. package/bench/refs/python-lib/pandas/core/api.py +138 -0
  66. package/bench/refs/python-lib/pandas/core/array_algos/masked_accumulations.py +97 -0
  67. package/bench/refs/python-lib/pandas/core/array_algos/masked_reductions.py +252 -0
  68. package/bench/refs/python-lib/pandas/core/array_algos/putmask.py +150 -0
  69. package/bench/refs/python-lib/pandas/core/array_algos/quantile.py +232 -0
  70. package/bench/refs/python-lib/pandas/core/array_algos/replace.py +159 -0
  71. package/bench/refs/python-lib/pandas/core/array_algos/take.py +609 -0
  72. package/bench/refs/python-lib/pandas/core/arraylike.py +534 -0
  73. package/bench/refs/python-lib/pandas/core/arrays/_arrow_string_mixins.py +434 -0
  74. package/bench/refs/python-lib/pandas/core/arrays/_mixins.py +630 -0
  75. package/bench/refs/python-lib/pandas/core/arrays/_ranges.py +276 -0
  76. package/bench/refs/python-lib/pandas/core/arrays/_utils.py +77 -0
  77. package/bench/refs/python-lib/pandas/core/arrays/arrow/accessors.py +521 -0
  78. package/bench/refs/python-lib/pandas/core/arrays/arrow/extension_types.py +174 -0
  79. package/bench/refs/python-lib/pandas/core/arrays/boolean.py +454 -0
  80. package/bench/refs/python-lib/pandas/core/arrays/floating.py +192 -0
  81. package/bench/refs/python-lib/pandas/core/arrays/integer.py +296 -0
  82. package/bench/refs/python-lib/pandas/core/arrays/masked.py +2140 -0
  83. package/bench/refs/python-lib/pandas/core/arrays/numeric.py +305 -0
  84. package/bench/refs/python-lib/pandas/core/arrays/numpy_.py +701 -0
  85. package/bench/refs/python-lib/pandas/core/arrays/period.py +1586 -0
  86. package/bench/refs/python-lib/pandas/core/arrays/sparse/accessor.py +505 -0
  87. package/bench/refs/python-lib/pandas/core/arrays/sparse/scipy_sparse.py +208 -0
  88. package/bench/refs/rust-project/.refrev +1 -0
  89. package/bench/refs/rust-project/README.md +17 -0
  90. package/bench/refs/rust-project/tokio/src/doc/os.rs +68 -0
  91. package/bench/refs/rust-project/tokio/src/fs/dir_builder.rs +129 -0
  92. package/bench/refs/rust-project/tokio/src/fs/file.rs +1113 -0
  93. package/bench/refs/rust-project/tokio/src/fs/mocks.rs +176 -0
  94. package/bench/refs/rust-project/tokio/src/fs/mod.rs +324 -0
  95. package/bench/refs/rust-project/tokio/src/fs/open_options.rs +856 -0
  96. package/bench/refs/rust-project/tokio/src/fs/read.rs +80 -0
  97. package/bench/refs/rust-project/tokio/src/fs/read_dir.rs +357 -0
  98. package/bench/refs/rust-project/tokio/src/fs/write.rs +99 -0
  99. package/bench/refs/rust-project/tokio/src/future/maybe_done.rs +123 -0
  100. package/bench/refs/rust-project/tokio/src/future/try_join.rs +82 -0
  101. package/bench/refs/rust-project/tokio/src/io/async_buf_read.rs +117 -0
  102. package/bench/refs/rust-project/tokio/src/io/async_read.rs +133 -0
  103. package/bench/refs/rust-project/tokio/src/io/async_seek.rs +96 -0
  104. package/bench/refs/rust-project/tokio/src/io/async_write.rs +402 -0
  105. package/bench/refs/rust-project/tokio/src/io/blocking.rs +339 -0
  106. package/bench/refs/rust-project/tokio/src/io/interest.rs +345 -0
  107. package/bench/refs/rust-project/tokio/src/io/join.rs +130 -0
  108. package/bench/refs/rust-project/tokio/src/io/mod.rs +308 -0
  109. package/bench/refs/rust-project/tokio/src/io/poll_evented.rs +319 -0
  110. package/bench/refs/rust-project/tokio/src/io/read_buf.rs +344 -0
  111. package/bench/refs/rust-project/tokio/src/io/ready.rs +322 -0
  112. package/bench/refs/rust-project/tokio/src/io/split.rs +166 -0
  113. package/bench/refs/rust-project/tokio/src/io/stderr.rs +148 -0
  114. package/bench/refs/rust-project/tokio/src/io/stdin.rs +98 -0
  115. package/bench/refs/rust-project/tokio/src/io/stdio_common.rs +222 -0
  116. package/bench/refs/rust-project/tokio/src/io/stdout.rs +197 -0
  117. package/bench/refs/rust-project/tokio/src/io/util/buf_reader.rs +311 -0
  118. package/bench/refs/rust-project/tokio/src/io/util/buf_stream.rs +207 -0
  119. package/bench/refs/rust-project/tokio/src/io/util/buf_writer.rs +310 -0
  120. package/bench/refs/rust-project/tokio/src/io/util/chain.rs +144 -0
  121. package/commands/_spawn-patterns.json +32 -0
  122. package/commands/ashlr-allow.md +21 -0
  123. package/commands/ashlr-benchmark.md +53 -0
  124. package/commands/ashlr-brief.md +97 -0
  125. package/commands/ashlr-budget.md +76 -0
  126. package/commands/ashlr-compact.md +54 -0
  127. package/commands/ashlr-dashboard.md +37 -0
  128. package/commands/ashlr-doctor.md +56 -0
  129. package/commands/ashlr-eco-mode.md +92 -0
  130. package/commands/ashlr-genome-init.md +24 -0
  131. package/commands/ashlr-genome-keygen.md +36 -0
  132. package/commands/ashlr-genome-loop.md +118 -0
  133. package/commands/ashlr-genome-push.md +36 -0
  134. package/commands/ashlr-genome-rewrap.md +48 -0
  135. package/commands/ashlr-genome-team-init.md +67 -0
  136. package/commands/ashlr-handoff.md +23 -0
  137. package/commands/ashlr-help.md +86 -0
  138. package/commands/ashlr-hook-timings.md +30 -0
  139. package/commands/ashlr-leaderboard.md +50 -0
  140. package/commands/ashlr-ollama-setup.md +39 -0
  141. package/commands/ashlr-orchestrate-status.md +72 -0
  142. package/commands/ashlr-orchestrate.md +83 -0
  143. package/commands/ashlr-parallelize.md +63 -0
  144. package/commands/ashlr-report-crash.md +33 -0
  145. package/commands/ashlr-resume.md +72 -0
  146. package/commands/ashlr-savings.md +24 -0
  147. package/commands/ashlr-settings.md +50 -0
  148. package/commands/ashlr-spawn.md +61 -0
  149. package/commands/ashlr-start.md +60 -0
  150. package/commands/ashlr-status.md +64 -0
  151. package/commands/ashlr-team-invite.md +47 -0
  152. package/commands/ashlr-tier.md +122 -0
  153. package/commands/ashlr-tour.md +60 -0
  154. package/commands/ashlr-update.md +155 -0
  155. package/commands/ashlr-upgrade.md +96 -0
  156. package/commands/goal.md +58 -0
  157. package/commands/loop.md +40 -0
  158. package/docs/CNAME +1 -0
  159. package/docs/PRO_TIER.md +104 -0
  160. package/docs/README.md +21 -0
  161. package/docs/architecture.md +415 -0
  162. package/docs/ashlr-pipe.md +161 -0
  163. package/docs/assets/logo.svg +19 -0
  164. package/docs/assets/og.png +0 -0
  165. package/docs/assets/og.svg +41 -0
  166. package/docs/benchmarks-v2.json +355 -0
  167. package/docs/benchmarks.json +102 -0
  168. package/docs/benchmarks.md +211 -0
  169. package/docs/billing.md +185 -0
  170. package/docs/cloud-genome.md +155 -0
  171. package/docs/competitive-wozcode.md +92 -0
  172. package/docs/deploy.md +325 -0
  173. package/docs/email-templates.md +113 -0
  174. package/docs/embed-tuning.md +103 -0
  175. package/docs/genome.md +120 -0
  176. package/docs/github-oauth-onboarding.md +127 -0
  177. package/docs/hero-video-script.md +129 -0
  178. package/docs/index.html +2262 -0
  179. package/docs/install-prompt.md +78 -0
  180. package/docs/install-windows.md +132 -0
  181. package/docs/install.ps1 +171 -0
  182. package/docs/install.sh +146 -0
  183. package/docs/launch/README.md +114 -0
  184. package/docs/launch/hn.md +49 -0
  185. package/docs/launch/reddit.md +45 -0
  186. package/docs/launch/x.md +65 -0
  187. package/docs/launch-post.md +112 -0
  188. package/docs/legal.md +78 -0
  189. package/docs/llm-providers.md +99 -0
  190. package/docs/multi-host-mcp.md +217 -0
  191. package/docs/multi-turn-architecture.md +108 -0
  192. package/docs/operations/deployment.md +108 -0
  193. package/docs/operations/error-monitoring.md +70 -0
  194. package/docs/operations/weekly-digest.md +70 -0
  195. package/docs/operations.md +494 -0
  196. package/docs/platform-support.md +133 -0
  197. package/docs/policy-packs.md +188 -0
  198. package/docs/press-kit/fact-sheet.md +78 -0
  199. package/docs/press-kit/founder-bio.md +3 -0
  200. package/docs/press-kit/logo.svg +33 -0
  201. package/docs/press-kit/screenshots.md +146 -0
  202. package/docs/pricing.md +251 -0
  203. package/docs/pro-backend-architecture.md +523 -0
  204. package/docs/producthunt.md +52 -0
  205. package/docs/router-migration.md +110 -0
  206. package/docs/session-log-schema.md +68 -0
  207. package/docs/social.md +95 -0
  208. package/docs/team-genome.md +528 -0
  209. package/docs/telemetry.md +194 -0
  210. package/docs/test-isolation.md +100 -0
  211. package/docs/upgrade.md +159 -0
  212. package/hooks/_hook-errors.ts +87 -0
  213. package/hooks/_hook-perf-emit.ts +113 -0
  214. package/hooks/_hook-timing-ledger.ts +1133 -0
  215. package/hooks/_nudge-throttle.ts +127 -0
  216. package/hooks/_recent-blocks.ts +127 -0
  217. package/hooks/audit-upload.ts +86 -0
  218. package/hooks/codex-hooks.json +171 -0
  219. package/hooks/commit-attribution.ts +179 -0
  220. package/hooks/edit-batching-nudge.ts +154 -0
  221. package/hooks/genome-scribe-hook.ts +219 -0
  222. package/hooks/hooks.json +294 -0
  223. package/hooks/orient-nudge-hook.ts +242 -0
  224. package/hooks/policy-enforce.ts +214 -0
  225. package/hooks/post-tool-use-embedding.ts +93 -0
  226. package/hooks/post-tool-use-genome.sh +37 -0
  227. package/hooks/post-tool-use-genome.ts +69 -0
  228. package/hooks/posttooluse-correlate.ts +122 -0
  229. package/hooks/posttooluse-dedupe-output.ts +241 -0
  230. package/hooks/posttooluse-genome-refresh.ts +223 -0
  231. package/hooks/posttooluse-native-nudge.ts +205 -0
  232. package/hooks/posttooluse-stale-result.ts +277 -0
  233. package/hooks/pretooluse-bash.ts +63 -0
  234. package/hooks/pretooluse-budget-guard.ts +318 -0
  235. package/hooks/pretooluse-common.ts +985 -0
  236. package/hooks/pretooluse-eco-router.ts +196 -0
  237. package/hooks/pretooluse-edit.sh +93 -0
  238. package/hooks/pretooluse-edit.ts +197 -0
  239. package/hooks/pretooluse-glob.ts +70 -0
  240. package/hooks/pretooluse-grep.sh +85 -0
  241. package/hooks/pretooluse-grep.ts +96 -0
  242. package/hooks/pretooluse-notebookedit.ts +121 -0
  243. package/hooks/pretooluse-read.sh +118 -0
  244. package/hooks/pretooluse-read.ts +118 -0
  245. package/hooks/pretooluse-task.ts +130 -0
  246. package/hooks/pretooluse-webfetch.ts +66 -0
  247. package/hooks/pretooluse-websearch.ts +85 -0
  248. package/hooks/pretooluse-write.ts +128 -0
  249. package/hooks/pulse-emit.ts +197 -0
  250. package/hooks/session-end-consolidate.sh +35 -0
  251. package/hooks/session-end-consolidate.ts +188 -0
  252. package/hooks/session-end-stats-gc.ts +80 -0
  253. package/hooks/session-log-append.sh +94 -0
  254. package/hooks/session-log-append.ts +116 -0
  255. package/hooks/session-start.ts +993 -0
  256. package/hooks/sessionend-hook-health-nudge.ts +451 -0
  257. package/hooks/sessionend-savings-nudge.ts +318 -0
  258. package/hooks/sessionend-status-line-nudge.ts +296 -0
  259. package/hooks/sessionstart-brief.ts +140 -0
  260. package/hooks/sessionstart-cost-refactor.ts +95 -0
  261. package/hooks/sessionstart-efficient.ts +110 -0
  262. package/hooks/sessionstart-genome-author.ts +93 -0
  263. package/hooks/sessionstart-lean-tools.ts +98 -0
  264. package/hooks/sessionstart-search.ts +97 -0
  265. package/hooks/stop-accounting.ts +152 -0
  266. package/hooks/subagent-stop-rollup.ts +162 -0
  267. package/hooks/userpromptsubmit-brief-trigger.ts +156 -0
  268. package/package.json +56 -0
  269. package/ports/README.md +108 -0
  270. package/ports/cursor/mcp.json +11 -0
  271. package/ports/goose/recipe.yaml +26 -0
  272. package/scripts/ashlr-mcp.ts +40 -0
  273. package/scripts/auto-update.ts +177 -0
  274. package/scripts/baseline-scan.ts +831 -0
  275. package/scripts/bench-orchestrate.ts +377 -0
  276. package/scripts/benchmark-refs.ts +292 -0
  277. package/scripts/bootstrap.mjs +100 -0
  278. package/scripts/brief-eval.ts +234 -0
  279. package/scripts/bump-version.ts +82 -0
  280. package/scripts/bun-resolve.d.mts +11 -0
  281. package/scripts/bun-resolve.mjs +59 -0
  282. package/scripts/calibrate-grep.ts +467 -0
  283. package/scripts/capture-grep-workload.ts +260 -0
  284. package/scripts/check-tier.ts +61 -0
  285. package/scripts/check-version-sync.ts +97 -0
  286. package/scripts/cli-orchestrate-status.ts +386 -0
  287. package/scripts/cli-orchestrate.ts +303 -0
  288. package/scripts/cli.ts +478 -0
  289. package/scripts/cloud-smoke-test.ts +313 -0
  290. package/scripts/coach-report.ts +373 -0
  291. package/scripts/context-db-migrate.ts +137 -0
  292. package/scripts/context-status.ts +43 -0
  293. package/scripts/demo-run.ts +255 -0
  294. package/scripts/deploy-smoke.sh +140 -0
  295. package/scripts/doctor.ts +992 -0
  296. package/scripts/embed-file-worker.ts +104 -0
  297. package/scripts/embed-tune.ts +308 -0
  298. package/scripts/errors-report.ts +428 -0
  299. package/scripts/find-test-leak.ts +69 -0
  300. package/scripts/generate-badge.ts +357 -0
  301. package/scripts/genome-auto-consolidate.ts +533 -0
  302. package/scripts/genome-auto-propose.ts +578 -0
  303. package/scripts/genome-cloud-pull.ts +363 -0
  304. package/scripts/genome-cloud-push.ts +382 -0
  305. package/scripts/genome-cloud-sync.ts +587 -0
  306. package/scripts/genome-commit-watcher.ts +295 -0
  307. package/scripts/genome-init.ts +986 -0
  308. package/scripts/genome-key.ts +233 -0
  309. package/scripts/genome-keygen.ts +168 -0
  310. package/scripts/genome-link.ts +59 -0
  311. package/scripts/genome-refresh-worker.ts +364 -0
  312. package/scripts/genome-rewrap.ts +90 -0
  313. package/scripts/genome-synthesizer.ts +679 -0
  314. package/scripts/genome-team-init.ts +312 -0
  315. package/scripts/handoff-pack.ts +438 -0
  316. package/scripts/hook-bootstrap.mjs +75 -0
  317. package/scripts/hook-timing-reader.ts +371 -0
  318. package/scripts/hook-timings-report.ts +554 -0
  319. package/scripts/install-genome-hooks.ts +196 -0
  320. package/scripts/install-onnx-model.ts +154 -0
  321. package/scripts/install-permissions.ts +276 -0
  322. package/scripts/install-status-line.ts +150 -0
  323. package/scripts/mcp-entrypoint.sh +78 -0
  324. package/scripts/mcp-entrypoint.ts +130 -0
  325. package/scripts/mdx-angle-bracket-lint.ts +152 -0
  326. package/scripts/measure-cold-start.ts +126 -0
  327. package/scripts/migrate-stats-to-sqlite.ts +86 -0
  328. package/scripts/ollama-setup.ts +498 -0
  329. package/scripts/onboarding-wizard.ts +1691 -0
  330. package/scripts/orchestrate-emit-telemetry.ts +177 -0
  331. package/scripts/orchestrate-expand.ts +517 -0
  332. package/scripts/orchestrate-run.ts +744 -0
  333. package/scripts/print-legend.ts +50 -0
  334. package/scripts/product-metadata.ts +38 -0
  335. package/scripts/prune-old-memories.ts +208 -0
  336. package/scripts/publish.sh +88 -0
  337. package/scripts/read-calibration.ts +122 -0
  338. package/scripts/report-crash.ts +226 -0
  339. package/scripts/run-benchmark.ts +750 -0
  340. package/scripts/run-root-tests.ts +28 -0
  341. package/scripts/savings-dashboard.ts +1258 -0
  342. package/scripts/savings-report-extras.ts +447 -0
  343. package/scripts/savings-status-line.ts +873 -0
  344. package/scripts/session-event-emit.ts +266 -0
  345. package/scripts/session-greet.ts +686 -0
  346. package/scripts/session-log-report.ts +551 -0
  347. package/scripts/session-resume.ts +614 -0
  348. package/scripts/set-hook-mode.ts +83 -0
  349. package/scripts/smoke-cross-platform.ts +117 -0
  350. package/scripts/smoke-realtime.ts +257 -0
  351. package/scripts/smoke-tools.ts +21 -0
  352. package/scripts/stats-cloud-pull.ts +213 -0
  353. package/scripts/stats-cloud-sync.ts +261 -0
  354. package/scripts/telemetry-flush.ts +160 -0
  355. package/scripts/telemetry-status.ts +266 -0
  356. package/scripts/timing-budget.ts +35 -0
  357. package/scripts/ui-animation.ts +120 -0
  358. package/scripts/ui.ts +431 -0
  359. package/scripts/upgrade-flow.ts +775 -0
  360. package/scripts/weekly-digest.ts +394 -0
  361. package/servers/_accounting.ts +78 -0
  362. package/servers/_ask-router.ts +84 -0
  363. package/servers/_ast-chunker.ts +434 -0
  364. package/servers/_ast-helpers.ts +183 -0
  365. package/servers/_ast-languages.ts +153 -0
  366. package/servers/_ast-refactor/_shared.ts +221 -0
  367. package/servers/_ast-refactor/cross-file-rename.ts +560 -0
  368. package/servers/_ast-refactor/extract-function.ts +357 -0
  369. package/servers/_ast-refactor/file-local-rename.ts +172 -0
  370. package/servers/_ast-refactor.ts +41 -0
  371. package/servers/_ast-skeleton.ts +107 -0
  372. package/servers/_bash-summarizers-registry.ts +1096 -0
  373. package/servers/_crash-dump.ts +210 -0
  374. package/servers/_cwd-clamp.ts +342 -0
  375. package/servers/_edit-log.ts +28 -0
  376. package/servers/_edit-match.ts +236 -0
  377. package/servers/_edit-validate.ts +196 -0
  378. package/servers/_embed-calibration.ts +123 -0
  379. package/servers/_embedding-cache.ts +401 -0
  380. package/servers/_embedding-model.ts +329 -0
  381. package/servers/_events.ts +113 -0
  382. package/servers/_genome-cache.ts +154 -0
  383. package/servers/_genome-commits.ts +172 -0
  384. package/servers/_genome-crypto-v2.ts +242 -0
  385. package/servers/_genome-crypto.ts +259 -0
  386. package/servers/_genome-discoveries.ts +140 -0
  387. package/servers/_genome-embed-populator.ts +216 -0
  388. package/servers/_genome-freshness.ts +134 -0
  389. package/servers/_genome-issues.ts +171 -0
  390. package/servers/_genome-live.ts +263 -0
  391. package/servers/_genome-prs.ts +188 -0
  392. package/servers/_genome-search.ts +479 -0
  393. package/servers/_genome-sync.ts +297 -0
  394. package/servers/_history-tracker.ts +279 -0
  395. package/servers/_http-helpers.ts +151 -0
  396. package/servers/_identity-hash.ts +151 -0
  397. package/servers/_llm-providers/anthropic.ts +132 -0
  398. package/servers/_llm-providers/cloud.ts +195 -0
  399. package/servers/_llm-providers/index.ts +350 -0
  400. package/servers/_llm-providers/local.ts +127 -0
  401. package/servers/_llm-providers/onnx.ts +393 -0
  402. package/servers/_llm-providers/types.ts +27 -0
  403. package/servers/_manifest-v2.ts +447 -0
  404. package/servers/_mcp-host.ts +93 -0
  405. package/servers/_nudge-events.ts +451 -0
  406. package/servers/_orchestrate-executor.ts +316 -0
  407. package/servers/_prefetch.ts +435 -0
  408. package/servers/_pricing.ts +256 -0
  409. package/servers/_pro.ts +381 -0
  410. package/servers/_read-cache.ts +44 -0
  411. package/servers/_router-handlers.ts +46 -0
  412. package/servers/_router.ts +55 -0
  413. package/servers/_run-with-timeout.ts +173 -0
  414. package/servers/_savings-render.ts +214 -0
  415. package/servers/_stats-sqlite.ts +640 -0
  416. package/servers/_stats.ts +1183 -0
  417. package/servers/_status-line-cells.ts +469 -0
  418. package/servers/_streaks.ts +162 -0
  419. package/servers/_summarize.ts +208 -0
  420. package/servers/_task-graph-render.ts +217 -0
  421. package/servers/_task-graph.ts +545 -0
  422. package/servers/_telemetry.ts +719 -0
  423. package/servers/_test-parsers.ts +530 -0
  424. package/servers/_test-watch.ts +626 -0
  425. package/servers/_text-helpers.ts +37 -0
  426. package/servers/_token-measure.ts +160 -0
  427. package/servers/_tool-base.ts +248 -0
  428. package/servers/_weekly-digest.ts +391 -0
  429. package/servers/ask-server-handlers.ts +49 -0
  430. package/servers/ask-server.ts +174 -0
  431. package/servers/bash-server-handlers.ts +138 -0
  432. package/servers/bash-server.ts +1136 -0
  433. package/servers/bench.ts +186 -0
  434. package/servers/diff-semantic-server-handlers.ts +39 -0
  435. package/servers/diff-semantic-server.ts +566 -0
  436. package/servers/diff-server-handlers.ts +317 -0
  437. package/servers/diff-server.ts +20 -0
  438. package/servers/edit-server-handlers.ts +31 -0
  439. package/servers/edit-server.ts +209 -0
  440. package/servers/edit-structural-server-handlers.ts +322 -0
  441. package/servers/efficiency-server-handlers.ts +161 -0
  442. package/servers/efficiency-server.ts +1135 -0
  443. package/servers/flush-server-handlers.ts +21 -0
  444. package/servers/flush-server.ts +19 -0
  445. package/servers/genome-server-handlers.ts +118 -0
  446. package/servers/genome-server.ts +397 -0
  447. package/servers/github-server-handlers.ts +187 -0
  448. package/servers/github-server.ts +856 -0
  449. package/servers/glob-server-handlers.ts +233 -0
  450. package/servers/glob-server.ts +20 -0
  451. package/servers/grep-server-handlers.ts +61 -0
  452. package/servers/grep-server.ts +564 -0
  453. package/servers/http-server-handlers.ts +36 -0
  454. package/servers/http-server.ts +149 -0
  455. package/servers/logs-server-handlers.ts +42 -0
  456. package/servers/logs-server.ts +359 -0
  457. package/servers/ls-server-handlers.ts +166 -0
  458. package/servers/ls-server.ts +20 -0
  459. package/servers/multi-edit-server-handlers.ts +51 -0
  460. package/servers/multi-edit-server.ts +422 -0
  461. package/servers/notebook-edit-server-handlers.ts +34 -0
  462. package/servers/notebook-edit-server.ts +181 -0
  463. package/servers/orient-server-handlers.ts +52 -0
  464. package/servers/orient-server.ts +436 -0
  465. package/servers/pipe-server-handlers.ts +68 -0
  466. package/servers/pipe-server.ts +300 -0
  467. package/servers/read-server-handlers.ts +42 -0
  468. package/servers/read-server.ts +206 -0
  469. package/servers/rename-file-server-handlers.ts +67 -0
  470. package/servers/rename-file-server.ts +699 -0
  471. package/servers/savings-server-handlers.ts +27 -0
  472. package/servers/savings-server.ts +54 -0
  473. package/servers/search-replace-regex-server-handlers.ts +96 -0
  474. package/servers/search-replace-regex-server.ts +751 -0
  475. package/servers/sql-server-handlers.ts +58 -0
  476. package/servers/sql-server.ts +505 -0
  477. package/servers/task-server-handlers.ts +358 -0
  478. package/servers/task-server.ts +24 -0
  479. package/servers/test-server-handlers.ts +351 -0
  480. package/servers/test-server.ts +20 -0
  481. package/servers/tree-server-handlers.ts +503 -0
  482. package/servers/tree-server.ts +20 -0
  483. package/servers/webfetch-server-handlers.ts +166 -0
  484. package/servers/webfetch-server.ts +20 -0
  485. package/servers/websearch-server-handlers.ts +215 -0
  486. package/servers/websearch-server.ts +19 -0
  487. package/servers/write-server-handlers.ts +27 -0
  488. package/servers/write-server.ts +97 -0
  489. package/skills/ashlr-brief/SKILL.md +80 -0
  490. package/skills/ashlr-codex-doctor/SKILL.md +21 -0
  491. package/skills/ashlr-codex-efficient-tools/SKILL.md +17 -0
  492. package/skills/ashlr-codex-genome-refresh/SKILL.md +20 -0
  493. package/skills/ashlr-codex-parallel-explore/SKILL.md +20 -0
  494. package/skills/ashlr-codex-savings/SKILL.md +17 -0
  495. package/skills/ashlr-cost-refactor/SKILL.md +50 -0
  496. package/skills/ashlr-efficient/SKILL.md +64 -0
  497. package/skills/ashlr-genome-author/SKILL.md +43 -0
  498. package/skills/ashlr-lean-tools/SKILL.md +43 -0
  499. package/skills/ashlr-search/SKILL.md +46 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * grep-server — ashlr__grep tool implementation.
3
+ *
4
+ * Genome-aware retrieval when .ashlrcode/genome/ exists, ripgrep fallback
5
+ * otherwise. Uses _embed-calibration for threshold constants and A/B logging.
6
+ */
7
+
8
+ import { spawnSync } from "child_process";
9
+ import { createHash } from "crypto";
10
+ import { readdirSync, statSync } from "fs";
11
+ import {
12
+ formatGenomeForPrompt,
13
+ genomeExists,
14
+ } from "@ashlr/core-efficiency";
15
+ import { retrieveCached } from "./_genome-cache";
16
+ import { refreshGenomeAfterEdit } from "./_genome-live";
17
+ import { retrieveCommitSections, formatCommitsForPrompt } from "./_genome-commits";
18
+ import { loadSectionFreshnessMap, decorateGenomeOutputWithFreshness } from "./_genome-freshness";
19
+ import { retrieveDiscoverySections, formatDiscoveriesForPrompt } from "./_genome-discoveries";
20
+ import { retrievePrSections, formatPrsForPrompt } from "./_genome-prs";
21
+ import { retrieveIssueSections, formatIssuesForPrompt } from "./_genome-issues";
22
+ import { summarizeIfLarge, PROMPTS, confidenceBadge, confidenceTier } from "./_summarize";
23
+ import { logEvent } from "./_events";
24
+ import { findParentGenome } from "../scripts/genome-link";
25
+ import { getCalibrationMultiplier } from "../scripts/read-calibration";
26
+ import { recordSaving } from "./_stats";
27
+ import { logGenomeCompressionRatioEvent } from "./_telemetry";
28
+ import { clampToCwd, primaryProjectRoot } from "./_cwd-clamp";
29
+ import { getEmbeddingCache } from "./_tool-base";
30
+ import { embed, upsertCorpus } from "./_embedding-model";
31
+ import { populateGenomeEmbeddings } from "./_genome-embed-populator";
32
+ import { currentSessionId } from "./_stats";
33
+ import {
34
+ EMBED_HIT_THRESHOLD,
35
+ BM25_CORPUS_MIN,
36
+ projectHash,
37
+ readCorpusDocCount,
38
+ recordEmbedCalibration,
39
+ computeCorpusTier,
40
+ computeWarmThreshold,
41
+ type CorpusTier,
42
+ } from "./_embed-calibration";
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Stale-detection counters (in-process, per session)
46
+ // ---------------------------------------------------------------------------
47
+
48
+ /** How many ripgrep fallbacks (with genome present) trigger the stale nudge. */
49
+ const STALE_NUDGE_THRESHOLD = 3;
50
+
51
+ let _staleFallbackCount = 0;
52
+ let _staleNudgeFired = false;
53
+ /** v1.34: in-process flag so the cold-corpus nudge only fires once per session. */
54
+ let _coldCorpusNudgeFired = false;
55
+
56
+ /** Reset for test isolation. */
57
+ export function _resetStaleFallbackCount(): void {
58
+ _staleFallbackCount = 0;
59
+ _staleNudgeFired = false;
60
+ _coldCorpusNudgeFired = false;
61
+ }
62
+
63
+ /** Current stale fallback count (visible to tests). */
64
+ export function _getStaleFallbackCount(): number {
65
+ return _staleFallbackCount;
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Warm-start background indexing (v1.24 Track E)
70
+ // ---------------------------------------------------------------------------
71
+
72
+ /**
73
+ * Source-file extensions eligible for warm-start background indexing.
74
+ * We stay away from generated / binary / lock files.
75
+ */
76
+ const WARM_INDEX_EXTENSIONS = new Set([
77
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
78
+ ".py", ".rb", ".go", ".rs", ".java", ".kt",
79
+ ".md", ".mdx", ".txt", ".json",
80
+ ]);
81
+
82
+ /**
83
+ * Return up to `limit` source files from `dir` using a fast shallow scan
84
+ * (no recursive walk — we only need a small sample, not full coverage).
85
+ * Randomised via Fisher-Yates so repeated warm calls spread coverage.
86
+ */
87
+ export function _sampleSourceFiles(dir: string, limit = 2): string[] {
88
+ try {
89
+ const entries = readdirSync(dir, { withFileTypes: true });
90
+ const files: string[] = [];
91
+ for (const e of entries) {
92
+ if (!e.isFile()) continue;
93
+ const dot = e.name.lastIndexOf(".");
94
+ if (dot < 0) continue;
95
+ const ext = e.name.slice(dot).toLowerCase();
96
+ if (!WARM_INDEX_EXTENSIONS.has(ext)) continue;
97
+ files.push(`${dir}/${e.name}`);
98
+ }
99
+ // Fisher-Yates shuffle (in-place), then take first `limit`.
100
+ for (let i = files.length - 1; i > 0; i--) {
101
+ const j = Math.floor(Math.random() * (i + 1));
102
+ const tmp = files[i]!;
103
+ files[i] = files[j]!;
104
+ files[j] = tmp;
105
+ }
106
+ return files.slice(0, limit);
107
+ } catch {
108
+ return [];
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Fire-and-forget: read 1-2 random source files from `cwd`, chunk them via
114
+ * upsertCorpus + embed, and upsert into the context DB. This grows the warm
115
+ * corpus without blocking the grep response.
116
+ *
117
+ * Exported for test injection — production callers use setImmediate wrapper.
118
+ */
119
+ export async function _warmIndexFiles(
120
+ cwd: string,
121
+ pHash: string,
122
+ ctxDb: import("./_embedding-cache").ContextDb,
123
+ ): Promise<void> {
124
+ const { readFileSync } = await import("fs");
125
+ const files = _sampleSourceFiles(cwd, 2);
126
+ for (const filePath of files) {
127
+ try {
128
+ let size = 0;
129
+ try { size = statSync(filePath).size; } catch { continue; }
130
+ if (size === 0 || size > 200_000) continue; // skip empty / huge files
131
+ const text = readFileSync(filePath, "utf-8").slice(0, 4000);
132
+ if (text.length < 20) continue;
133
+ upsertCorpus(text);
134
+ const vec = await embed(text);
135
+ ctxDb.upsertEmbedding({
136
+ projectHash: pHash,
137
+ sectionPath: `warm:${filePath}`,
138
+ sectionText: text.slice(0, 2000),
139
+ embedding: vec,
140
+ embeddingDim: vec.length,
141
+ source: "code",
142
+ });
143
+ void logEvent("embed_warm_index", {
144
+ tool: "ashlr__grep",
145
+ extra: { filePath, textLen: text.length },
146
+ });
147
+ } catch {
148
+ /* best-effort — never block grep */
149
+ }
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Resolve rg via Bun.which (walks PATH and common Homebrew locations). Shell
155
+ * aliases like Claude Code's own rg wrapper don't resolve under spawn, so we
156
+ * need the actual binary. Returns "rg" as last resort so spawn can at least
157
+ * surface a useful error.
158
+ */
159
+ function resolveRg(): string {
160
+ return (
161
+ (typeof (globalThis as { Bun?: { which(bin: string): string | null } }).Bun !== "undefined"
162
+ ? (globalThis as { Bun: { which(bin: string): string | null } }).Bun.which("rg")
163
+ : null) ??
164
+ ["/opt/homebrew/bin/rg", "/usr/local/bin/rg", "/usr/bin/rg"].find((p) => {
165
+ try {
166
+ require("fs").accessSync(p);
167
+ return true;
168
+ } catch {
169
+ return false;
170
+ }
171
+ }) ??
172
+ "rg"
173
+ );
174
+ }
175
+
176
+ /**
177
+ * Estimate total matches by shelling out to `rg -c` (count-only, small
178
+ * output). Returns null when rg is unavailable or the call fails — callers
179
+ * should treat null as "unknown" rather than zero.
180
+ */
181
+ function estimateMatchCount(pattern: string, cwd: string): number | null {
182
+ try {
183
+ // `--` stops rg flag parsing so a caller-supplied pattern starting with
184
+ // `-` cannot smuggle in flags like --invert-match or -uuu.
185
+ const res = spawnSync(resolveRg(), ["-c", "--", pattern, cwd], {
186
+ encoding: "utf-8",
187
+ timeout: 3_000,
188
+ });
189
+ if (res.status !== 0 && res.status !== 1) return null; // 1 == no matches
190
+ const out = res.stdout ?? "";
191
+ if (!out.trim()) return 0;
192
+ let total = 0;
193
+ for (const line of out.split("\n")) {
194
+ const idx = line.lastIndexOf(":");
195
+ if (idx < 0) continue;
196
+ const n = parseInt(line.slice(idx + 1), 10);
197
+ if (Number.isFinite(n)) total += n;
198
+ }
199
+ return total;
200
+ } catch {
201
+ return null;
202
+ }
203
+ }
204
+
205
+ export async function ashlrGrep(input: {
206
+ pattern: string;
207
+ cwd?: string;
208
+ bypassSummary?: boolean;
209
+ /** Q3 PR/Issue retrieval DSL — default true (no opt-in required). */
210
+ include_prs?: boolean;
211
+ /** Q3 PR/Issue retrieval DSL — default true. */
212
+ include_issues?: boolean;
213
+ /** Q3 — drop PRs/issues older than N days. undefined disables the filter. */
214
+ since_days?: number;
215
+ }): Promise<string> {
216
+ // Clamp input.cwd to process.cwd() — ripgrep spawns below use this path as
217
+ // their search root, so an unclamped caller could exfiltrate /etc, /root, etc.
218
+ // When the caller omits `cwd`, default to the user's project root rather
219
+ // than `process.cwd()` — the MCP server's cwd is the plugin install dir,
220
+ // and searching there returns matches inside the plugin's own source code
221
+ // instead of the user's project.
222
+ const resolvedCwd = input.cwd ?? primaryProjectRoot();
223
+ const clamp = clampToCwd(resolvedCwd, "ashlr__grep");
224
+ if (!clamp.ok) return clamp.message;
225
+ const cwd = clamp.abs;
226
+
227
+ // v1.30 kill-switch: ASHLR_GENOME_RETRIEVAL=off forces the bare ripgrep path
228
+ // even when a genome and populated embedding cache exist. Use this to
229
+ // diagnose retrieval-related regressions or to force-compare ashlr to a
230
+ // vanilla grep call. Honors "0" and "false" as synonyms for "off".
231
+ const retrievalKill = (process.env.ASHLR_GENOME_RETRIEVAL ?? "").trim().toLowerCase();
232
+ const retrievalDisabled =
233
+ retrievalKill === "off" || retrievalKill === "0" || retrievalKill === "false";
234
+
235
+ // Prefer the local genome. If none, walk up to 4 parents (capped at $HOME).
236
+ // The kill-switch shorts this out entirely so the rest of the function
237
+ // behaves as if no genome were present.
238
+ let genomeRoot: string | null = null;
239
+ let genomeIsParent = false;
240
+ if (!retrievalDisabled) {
241
+ if (genomeExists(cwd)) {
242
+ genomeRoot = cwd;
243
+ } else {
244
+ const parent = findParentGenome(cwd);
245
+ if (parent) {
246
+ genomeRoot = parent;
247
+ genomeIsParent = true;
248
+ }
249
+ }
250
+ }
251
+
252
+ if (!genomeRoot) {
253
+ await logEvent("tool_fallback", {
254
+ tool: "ashlr__grep",
255
+ reason: retrievalDisabled ? "kill-switch" : "no-genome",
256
+ });
257
+ }
258
+
259
+ // ------------------------------------------------------------------
260
+ // Embedding cache — three-tier warm-start (v1.24 Track E).
261
+ //
262
+ // cold (0–9 docs) : skip cache entirely; corpus too small for reliable IDF.
263
+ // warm (10–49 docs): consult cache with a stricter per-size gradient threshold
264
+ // (0.80 at N=10, linearly easing to 0.68 at N=50).
265
+ // After the response, enqueue 1-2 random files for background
266
+ // chunking + embedding to grow the corpus (fire-and-forget).
267
+ // hot (50+ docs) : full v1.23 behavior, threshold = EMBED_HIT_THRESHOLD (0.68).
268
+ //
269
+ // The `tier` field is emitted on every embed_cache_hit/miss event so Track A
270
+ // (adaptive thresholds) can later observe whether the gradient is the right curve.
271
+ // ------------------------------------------------------------------
272
+ const pHash = projectHash(cwd);
273
+ const sessionId = currentSessionId();
274
+ let embedCachePrefix = "";
275
+ const corpusSize = readCorpusDocCount();
276
+ const corpusTier: CorpusTier = computeCorpusTier(corpusSize);
277
+ // Effective threshold for this call — only meaningful for warm/hot.
278
+ const effectiveThreshold = corpusTier === "hot" ? EMBED_HIT_THRESHOLD : computeWarmThreshold(corpusSize);
279
+ // The kill-switch (ASHLR_GENOME_RETRIEVAL=off) disables the embedding cache
280
+ // path AND the genome path, falling through to bare ripgrep.
281
+ const embedCacheEnabled = corpusTier !== "cold" && !retrievalDisabled;
282
+
283
+ // v1.34: embedMode indicator for response headers.
284
+ const embedMode: "cold" | "warm" | "hot" | "remote" | "disabled" = retrievalDisabled
285
+ ? "disabled"
286
+ : (corpusTier as "cold" | "warm" | "hot");
287
+
288
+ // v1.34: one-time cold-corpus nudge per process lifetime.
289
+ let coldCorpusNudge = "";
290
+ if (embedMode === "cold" && genomeRoot && !_coldCorpusNudgeFired) {
291
+ _coldCorpusNudgeFired = true;
292
+ coldCorpusNudge =
293
+ `\n\n[ashlr] embedding cache is cold (${corpusSize} doc${corpusSize === 1 ? "" : "s"}) — ` +
294
+ `run /ashlr-genome-init or set ASHLR_EMBED_URL for better retrieval.`;
295
+ }
296
+ try {
297
+ const ctxDb = getEmbeddingCache();
298
+
299
+ if (genomeRoot) {
300
+ try {
301
+ await populateGenomeEmbeddings(genomeRoot, { ctxDb, projectHash: pHash });
302
+ } catch {
303
+ // Populator must never break grep.
304
+ }
305
+ }
306
+
307
+ if (embedCacheEnabled) {
308
+ const queryVec = await embed(input.pattern);
309
+ const hits = ctxDb.searchSimilar({ projectHash: pHash, embedding: queryVec, limit: 3 });
310
+ const topHit = hits[0];
311
+ const topSim = topHit?.similarity ?? 0;
312
+ const isHit = Boolean(topHit) && topSim >= effectiveThreshold;
313
+ let hitContentLength = 0;
314
+ if (isHit) {
315
+ const hitSections = hits
316
+ .filter((h) => h.similarity >= effectiveThreshold)
317
+ .map((h) => `[embedding-cache hit | sim=${h.similarity.toFixed(3)} | ${h.sectionPath}]\n${h.sectionText}`)
318
+ .join("\n\n");
319
+ hitContentLength = hitSections.length;
320
+ const tokensSaved = Math.round(hitContentLength / 4);
321
+ embedCachePrefix = hitSections + "\n\n";
322
+ ctxDb.recordRetrieval({ sessionId, projectHash: pHash, pattern: input.pattern, hit: true, tokensSaved });
323
+ } else {
324
+ ctxDb.recordRetrieval({ sessionId, projectHash: pHash, pattern: input.pattern, hit: false, tokensSaved: 0 });
325
+ }
326
+ const queryHashHex = createHash("sha256").update(input.pattern).digest("hex").slice(0, 12);
327
+ void recordEmbedCalibration({
328
+ queryHashHex,
329
+ topSimilarity: topSim,
330
+ hit: isHit,
331
+ contentLength: hitContentLength,
332
+ threshold: effectiveThreshold,
333
+ });
334
+ void logEvent(isHit ? "embed_cache_hit" : "embed_cache_miss", {
335
+ tool: "ashlr__grep",
336
+ extra: { topSimilarity: topSim, corpusSize, tier: corpusTier, threshold: effectiveThreshold },
337
+ });
338
+
339
+ // Warm tier: fire-and-forget background indexing of 1-2 random files
340
+ // to grow the corpus without blocking the grep response.
341
+ if (corpusTier === "warm") {
342
+ setImmediate(() => {
343
+ try {
344
+ _warmIndexFiles(cwd, pHash, getEmbeddingCache()).catch(() => {});
345
+ } catch {
346
+ /* best-effort */
347
+ }
348
+ });
349
+ }
350
+ } else {
351
+ // cold — record the miss for accounting but don't touch the cache.
352
+ ctxDb.recordRetrieval({ sessionId, projectHash: pHash, pattern: input.pattern, hit: false, tokensSaved: 0 });
353
+ }
354
+ } catch {
355
+ // Embedding cache errors must never break grep. Silently skip.
356
+ }
357
+
358
+ if (genomeRoot) {
359
+ const sections = await retrieveCached(genomeRoot, input.pattern, 4000);
360
+ // v2: commit sections are additive — they widen the corpus with diff history.
361
+ // Best-effort: on any failure we get [] back and behave as if v1.
362
+ const commits = await retrieveCommitSections(genomeRoot, input.pattern, 3);
363
+ // v2 Q2: AI-synthesized discoveries. Best-effort — failure → empty list,
364
+ // same backward-compat envelope as commit retrieval.
365
+ const discoveries = await retrieveDiscoverySections(genomeRoot, input.pattern, 3);
366
+
367
+ // Q3 PR/Issue retrieval — default ON. Gate I/O on the include_* flags so
368
+ // `include_prs:false` skips the directory scan entirely (no disk I/O,
369
+ // no parsing) — this is tested.
370
+ const includePrs = input.include_prs !== false;
371
+ const includeIssues = input.include_issues !== false;
372
+ const sinceDays = input.since_days;
373
+ const prs = includePrs
374
+ ? await retrievePrSections(genomeRoot, input.pattern, 3, sinceDays)
375
+ : [];
376
+ const issues = includeIssues
377
+ ? await retrieveIssueSections(genomeRoot, input.pattern, 3, sinceDays)
378
+ : [];
379
+
380
+ const discoveryBlock = discoveries.length > 0 ? `${formatDiscoveriesForPrompt(discoveries)}\n\n---\n\n` : "";
381
+ const prBlock = prs.length > 0 ? `${formatPrsForPrompt(prs)}\n\n---\n\n` : "";
382
+ const issueBlock = issues.length > 0 ? `${formatIssuesForPrompt(issues)}\n\n---\n\n` : "";
383
+ const commitBlock = commits.length > 0 ? `${formatCommitsForPrompt(commits)}\n\n---\n\n` : "";
384
+ if (
385
+ sections.length === 0 &&
386
+ commits.length === 0 &&
387
+ discoveries.length === 0 &&
388
+ prs.length === 0 &&
389
+ issues.length === 0
390
+ ) {
391
+ await logEvent("tool_fallback", { tool: "ashlr__grep", reason: "genome-empty" });
392
+ }
393
+ if (
394
+ sections.length === 0 &&
395
+ (commits.length > 0 || discoveries.length > 0 || prs.length > 0 || issues.length > 0)
396
+ ) {
397
+ // Static retrieval missed, but at least one cloud/derived section hit
398
+ // — return that block alone with a clear header. Avoid the stale-genome
399
+ // fallback path.
400
+ const parentNote = genomeIsParent ? ` (from parent genome at ${genomeRoot})` : "";
401
+ const header =
402
+ `[ashlr__grep] genome-retrieved 0 static section(s), ${commits.length} commit section(s), ` +
403
+ `${discoveries.length} discovery section(s), ${prs.length} pr section(s), ${issues.length} issue section(s)${parentNote} [embed:${embedMode}]`;
404
+ // Ordering: discoveries → PRs → issues → commits.
405
+ const formattedCombined =
406
+ (discoveries.length > 0 ? `${formatDiscoveriesForPrompt(discoveries)}\n\n---\n\n` : "") +
407
+ (prs.length > 0 ? `${formatPrsForPrompt(prs)}\n\n---\n\n` : "") +
408
+ (issues.length > 0 ? `${formatIssuesForPrompt(issues)}\n\n---\n\n` : "") +
409
+ formatCommitsForPrompt(commits);
410
+ const grepsMultiplier = getCalibrationMultiplier();
411
+ const rawBytesEstimate = formattedCombined.length * grepsMultiplier;
412
+ await recordSaving(rawBytesEstimate, formattedCombined.length, "ashlr__grep");
413
+ logGenomeCompressionRatioEvent({
414
+ tool: "ashlr__grep",
415
+ raw_bytes: Math.round(rawBytesEstimate),
416
+ compressed_bytes: formattedCombined.length,
417
+ });
418
+ const commitBadgeOpts = {
419
+ toolName: "ashlr__grep",
420
+ rawBytes: Math.round(rawBytesEstimate),
421
+ outputBytes: formattedCombined.length,
422
+ };
423
+ return embedCachePrefix + `${header}\n\n${formattedCombined}` + confidenceBadge(commitBadgeOpts);
424
+ }
425
+ if (sections.length > 0) {
426
+ // Q2 prep: stamp each section header with a freshness badge from
427
+ // the v2 manifest. Discoveries + PRs + issues + commits are prepended
428
+ // first, then the whole block (including static sections) is
429
+ // decorated. Legacy v1 sections without lastUpdatedAt are left un-badged.
430
+ const freshnessMap = await loadSectionFreshnessMap(genomeRoot);
431
+ // Q3 ordering: discoveries → PRs → issues → commits → static sections.
432
+ const rawFormatted =
433
+ discoveryBlock + prBlock + issueBlock + commitBlock + formatGenomeForPrompt(sections);
434
+ const formatted = decorateGenomeOutputWithFreshness(rawFormatted, freshnessMap);
435
+ const grepsMultiplier = getCalibrationMultiplier();
436
+ let rawBytesEstimate = formatted.length * grepsMultiplier;
437
+
438
+ if (process.env.ASHLR_CALIBRATE === "1") {
439
+ try {
440
+ const calibRes = spawnSync(resolveRg(), ["--json", "-n", "--", input.pattern, cwd], {
441
+ encoding: "buffer",
442
+ timeout: 5_000,
443
+ maxBuffer: 64 * 1024 * 1024,
444
+ });
445
+ const calibBuf = calibRes.stdout as Buffer | null;
446
+ const trueRawBytes = calibBuf ? calibBuf.length : 0;
447
+ rawBytesEstimate = Math.max(trueRawBytes, formatted.length * grepsMultiplier);
448
+ } catch {
449
+ // Calibration run failed — silently fall through to the estimate.
450
+ }
451
+ }
452
+
453
+ // v1.18 Trust Pass: neither side of recordSaving includes embedCachePrefix.
454
+ await recordSaving(rawBytesEstimate, formatted.length, "ashlr__grep");
455
+ // 1.3 — genome_compression_ratio telemetry (opt-in, no paths/content).
456
+ logGenomeCompressionRatioEvent({
457
+ tool: "ashlr__grep",
458
+ raw_bytes: Math.round(rawBytesEstimate),
459
+ compressed_bytes: formatted.length,
460
+ });
461
+ const estimated = estimateMatchCount(input.pattern, cwd);
462
+ if (estimated !== null && estimated > sections.length * 4) {
463
+ await logEvent("tool_escalate", {
464
+ tool: "ashlr__grep",
465
+ reason: "incomplete-genome",
466
+ extra: { sections: sections.length, estimated },
467
+ });
468
+ }
469
+ const parentNote = genomeIsParent ? ` (from parent genome at ${genomeRoot})` : "";
470
+ const countNote =
471
+ estimated === null
472
+ ? ""
473
+ : ` · rg estimates ${estimated.toLocaleString()} total match${estimated === 1 ? "" : "es"}${
474
+ estimated > sections.length * 4
475
+ ? " · call with bypassSummary:true for the full ripgrep list"
476
+ : ""
477
+ }`;
478
+ const commitNote = commits.length > 0 ? `, ${commits.length} commit section(s)` : "";
479
+ const prNote = prs.length > 0 ? `, ${prs.length} pr section(s)` : "";
480
+ const issueNote = issues.length > 0 ? `, ${issues.length} issue section(s)` : "";
481
+ const header = `[ashlr__grep] genome-retrieved ${sections.length} section(s)${commitNote}${prNote}${issueNote}${parentNote}${countNote} [embed:${embedMode}]`;
482
+ const genomeBadgeOpts = {
483
+ toolName: "ashlr__grep",
484
+ rawBytes: Math.round(rawBytesEstimate),
485
+ outputBytes: formatted.length,
486
+ };
487
+ if (confidenceTier(genomeBadgeOpts) === "low") {
488
+ await logEvent("tool_low_confidence_shipped", { tool: "ashlr__grep", reason: "low-confidence" });
489
+ }
490
+ return embedCachePrefix + `${header}\n\n${formatted}` + confidenceBadge(genomeBadgeOpts);
491
+ }
492
+ }
493
+
494
+ // Genome was present but had no matching sections — this is a stale-genome
495
+ // signal. Increment the in-session counter and emit a telemetry event.
496
+ if (genomeRoot) {
497
+ _staleFallbackCount++;
498
+ await logEvent("genome_stale_detected", {
499
+ tool: "ashlr__grep",
500
+ reason: "genome-miss",
501
+ extra: { pattern: input.pattern, sessionFallbackCount: _staleFallbackCount },
502
+ });
503
+ }
504
+
505
+ const rgBin = resolveRg();
506
+
507
+ const res = spawnSync(rgBin, ["--json", "-n", "--", input.pattern, cwd], {
508
+ encoding: "utf-8",
509
+ timeout: 15_000,
510
+ });
511
+ const raw = res.stdout ?? "";
512
+ const truncated = raw.length > 4000 ? raw.slice(0, 2000) + "\n\n[... truncated ...]\n\n" + raw.slice(-1000) : raw;
513
+ const summarized = await summarizeIfLarge(truncated, {
514
+ toolName: "ashlr__grep",
515
+ systemPrompt: PROMPTS.grep,
516
+ bypass: input.bypassSummary === true,
517
+ });
518
+ await recordSaving(raw.length, summarized.outputBytes, "ashlr__grep");
519
+ const rgBadgeOpts = {
520
+ toolName: "ashlr__grep",
521
+ rawBytes: raw.length,
522
+ outputBytes: summarized.outputBytes,
523
+ fellBack: summarized.fellBack,
524
+ };
525
+ if (confidenceTier(rgBadgeOpts) === "low") {
526
+ await logEvent("tool_low_confidence_shipped", { tool: "ashlr__grep", reason: "low-confidence" });
527
+ }
528
+
529
+ // Post-grep: upsert matched snippets into the embedding cache (fire-and-forget).
530
+ setImmediate(() => {
531
+ try {
532
+ const snippet = (summarized.text || raw).slice(0, 2000);
533
+ if (snippet.length > 50) {
534
+ upsertCorpus(snippet);
535
+ const ctxDb = getEmbeddingCache();
536
+ embed(snippet).then((vec) => {
537
+ ctxDb.upsertEmbedding({
538
+ projectHash: pHash,
539
+ sectionPath: `grep:${input.pattern}`,
540
+ sectionText: snippet,
541
+ embedding: vec,
542
+ embeddingDim: vec.length,
543
+ source: "code",
544
+ });
545
+ }).catch(() => {});
546
+ }
547
+ } catch {
548
+ /* best-effort */
549
+ }
550
+ });
551
+
552
+ // Stale-genome nudge: after N fallbacks in the same session, surface a
553
+ // hint so the user knows to refresh. Fire once per session.
554
+ let staleNudge = "";
555
+ if (genomeRoot && _staleFallbackCount >= STALE_NUDGE_THRESHOLD && !_staleNudgeFired) {
556
+ _staleNudgeFired = true;
557
+ staleNudge =
558
+ `\n\n[ashlr] genome may be stale (${_staleFallbackCount} grep queries fell through to ripgrep). ` +
559
+ `Run \`bun run scripts/genome-refresh-worker.ts\` to refresh, or ` +
560
+ `\`bun run scripts/genome-refresh-worker.ts --full\` for a complete rebuild.`;
561
+ }
562
+
563
+ return embedCachePrefix + (summarized.text || "[no matches]") + confidenceBadge(rgBadgeOpts) + staleNudge + coldCorpusNudge;
564
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * http-server-handlers — registers ashlr__http on the shared registry so the
3
+ * router can dispatch it alongside the other migrated tools. Logic remains in
4
+ * http-server.ts for the standalone entry point.
5
+ */
6
+
7
+ import { registerTool, type ToolCallContext, type ToolResult } from "./_tool-base";
8
+ import { doFetch, type HttpArgs } from "./http-server";
9
+
10
+ registerTool({
11
+ name: "ashlr__http",
12
+ description:
13
+ "HTTP fetch with compressed output. Readable-extracts main content from " +
14
+ "HTML, pretty-prints + array-elides JSON, bounded byte cap. Refuses " +
15
+ "non-http/https schemes and private hosts by default.",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ url: { type: "string" },
20
+ method: { type: "string", description: "HTTP method (default GET)" },
21
+ headers: { type: "object", description: "Request headers" },
22
+ body: { type: "string", description: "Request body for POST/PUT" },
23
+ mode: {
24
+ type: "string",
25
+ description: "'readable' (HTML→main content) | 'raw' | 'json' | 'headers'",
26
+ },
27
+ maxBytes: { type: "number", description: "Response body cap before compression (default 2_000_000)" },
28
+ timeoutMs: { type: "number", description: "Request timeout (default 15000)" },
29
+ },
30
+ required: ["url"],
31
+ },
32
+ handler: async (args: Record<string, unknown>, _ctx: ToolCallContext): Promise<ToolResult> => {
33
+ const text = await doFetch(args as unknown as HttpArgs);
34
+ return { content: [{ type: "text", text }] };
35
+ },
36
+ });