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,196 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-eco-router.ts — Auto-route question-shaped Task calls to ashlr:ashlr:explore.
4
+ *
5
+ * PreToolUse hook. When ASHLR_ECO=1 and the Task tool is invoked without an
6
+ * explicit subagent_type, AND the prompt starts with a question word, inject
7
+ * subagent_type: "ashlr:ashlr:explore" into the tool input.
8
+ *
9
+ * Question word regex: /^(what|where|how|find|explain|why|which|when|who)\b/i
10
+ *
11
+ * If subagent_type is already set, passes through unchanged.
12
+ * If ASHLR_ECO is not set or is "0", passes through unchanged.
13
+ * If the tool is not "Task", passes through unchanged.
14
+ *
15
+ * Emits nudge event "eco_router_redirected" when a redirect occurs.
16
+ *
17
+ * Design:
18
+ * - Never blocks (always exits 0).
19
+ * - Best-effort stdin parse — falls through on any error.
20
+ *
21
+ * Hook contract (PreToolUse):
22
+ * stdin → { tool_name, tool_input? }
23
+ * stdout → { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext? } }
24
+ * OR (when injecting): { hookSpecificOutput: { hookEventName: "PreToolUse",
25
+ * toolInputOverride: { ...original_input, subagent_type: "ashlr:ashlr:explore" } } }
26
+ */
27
+
28
+ import { appendFileSync, mkdirSync } from "fs";
29
+ import { homedir } from "os";
30
+ import { dirname, join } from "path";
31
+
32
+ import { installHookTimeout } from "./pretooluse-common";
33
+
34
+ if (import.meta.main) installHookTimeout("pretooluse-eco-router");
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Types
38
+ // ---------------------------------------------------------------------------
39
+
40
+ interface HookOutput {
41
+ hookSpecificOutput: {
42
+ hookEventName: "PreToolUse";
43
+ additionalContext?: string;
44
+ toolInputOverride?: Record<string, unknown>;
45
+ };
46
+ }
47
+
48
+ interface Payload {
49
+ tool_name?: string;
50
+ tool_input?: Record<string, unknown>;
51
+ }
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // Constants
55
+ // ---------------------------------------------------------------------------
56
+
57
+ const QUESTION_RE = /^(what|where|how|find|explain|why|which|when|who)\b/i;
58
+ const TARGET_SUBAGENT = "ashlr:ashlr:explore";
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Helpers
62
+ // ---------------------------------------------------------------------------
63
+
64
+ function passThrough(): HookOutput {
65
+ return { hookSpecificOutput: { hookEventName: "PreToolUse" } };
66
+ }
67
+
68
+ function home(): string {
69
+ return (
70
+ process.env.ASHLR_HOME_OVERRIDE?.trim() ||
71
+ process.env.HOME ||
72
+ homedir()
73
+ );
74
+ }
75
+
76
+ function emitRedirectEvent(): void {
77
+ try {
78
+ const p = join(home(), ".ashlr", "nudge-events.jsonl");
79
+ mkdirSync(dirname(p), { recursive: true });
80
+ const line = JSON.stringify({
81
+ ts: new Date().toISOString(),
82
+ event: "eco_router_redirected",
83
+ subagent_type: TARGET_SUBAGENT,
84
+ });
85
+ appendFileSync(p, line + "\n", "utf-8");
86
+ } catch {
87
+ // best-effort
88
+ }
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Core decision (exported for tests)
93
+ // ---------------------------------------------------------------------------
94
+
95
+ export interface RouteOpts {
96
+ /** Override ASHLR_ECO env for testing. */
97
+ ecoMode?: string;
98
+ }
99
+
100
+ export interface RouteResult {
101
+ action: "pass" | "inject";
102
+ subagent_type?: string;
103
+ reason?: string;
104
+ }
105
+
106
+ export function route(payload: Payload, opts: RouteOpts = {}): RouteResult {
107
+ const eco = opts.ecoMode ?? process.env.ASHLR_ECO;
108
+
109
+ // Not in eco mode → pass.
110
+ if (!eco || eco === "0") {
111
+ return { action: "pass", reason: "eco_off" };
112
+ }
113
+
114
+ // Not a Task call → pass.
115
+ if (payload.tool_name !== "Task") {
116
+ return { action: "pass", reason: "not_task" };
117
+ }
118
+
119
+ const input = payload.tool_input ?? {};
120
+
121
+ // Already has subagent_type → respect it, pass through.
122
+ if (input.subagent_type != null) {
123
+ return { action: "pass", reason: "subagent_already_set" };
124
+ }
125
+
126
+ // Check if the prompt is question-shaped.
127
+ const prompt = typeof input.prompt === "string" ? input.prompt.trim() : "";
128
+ if (!QUESTION_RE.test(prompt)) {
129
+ return { action: "pass", reason: "not_question_shaped" };
130
+ }
131
+
132
+ return { action: "inject", subagent_type: TARGET_SUBAGENT };
133
+ }
134
+
135
+ export function buildOutput(payload: Payload, result: RouteResult): HookOutput {
136
+ if (result.action !== "inject") {
137
+ return passThrough();
138
+ }
139
+
140
+ const overrideInput: Record<string, unknown> = {
141
+ ...(payload.tool_input ?? {}),
142
+ subagent_type: TARGET_SUBAGENT,
143
+ };
144
+
145
+ return {
146
+ hookSpecificOutput: {
147
+ hookEventName: "PreToolUse",
148
+ additionalContext:
149
+ `[ashlr eco] Task routed to ${TARGET_SUBAGENT} (question-shaped prompt, eco mode on).`,
150
+ toolInputOverride: overrideInput,
151
+ },
152
+ };
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Stdin reading
157
+ // ---------------------------------------------------------------------------
158
+
159
+ async function readStdin(): Promise<string> {
160
+ const chunks: Buffer[] = [];
161
+ for await (const chunk of process.stdin as AsyncIterable<Buffer | string>) {
162
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
163
+ }
164
+ return Buffer.concat(chunks).toString("utf-8");
165
+ }
166
+
167
+ // ---------------------------------------------------------------------------
168
+ // Entry point
169
+ // ---------------------------------------------------------------------------
170
+
171
+ async function main(): Promise<void> {
172
+ let payload: Payload = {};
173
+ try {
174
+ const raw = await readStdin();
175
+ if (raw.trim()) payload = JSON.parse(raw) as Payload;
176
+ } catch {
177
+ process.stdout.write(JSON.stringify(passThrough()));
178
+ process.exit(0);
179
+ }
180
+
181
+ try {
182
+ const result = route(payload);
183
+ if (result.action === "inject") {
184
+ emitRedirectEvent();
185
+ }
186
+ process.stdout.write(JSON.stringify(buildOutput(payload, result)));
187
+ } catch {
188
+ process.stdout.write(JSON.stringify(passThrough()));
189
+ }
190
+
191
+ process.exit(0);
192
+ }
193
+
194
+ if (import.meta.main) {
195
+ await main();
196
+ }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # ashlr-plugin PreToolUse:Edit enforcement hook.
3
+ #
4
+ # Blocks the built-in Edit tool on files larger than 5KB and redirects the
5
+ # agent to ashlr__edit, which applies an in-place strict-by-default
6
+ # search/replace and returns only a compact diff summary — saves ~80% tokens
7
+ # vs shipping the full before+after file contents.
8
+ #
9
+ # Contract (Claude Code PreToolUse):
10
+ # stdin → JSON { tool_name, tool_input: { file_path, ... }, ... }
11
+ # exit 0 → allow the built-in call
12
+ # exit 2 → block; stderr is shown to the agent as a tool error
13
+ #
14
+ # Exceptions (pass-through):
15
+ # - File < 5 KB (round-trip cost is small)
16
+ # - File does not exist (new-file creation via Edit is a rare/edge path;
17
+ # pass through rather than block a legitimate create)
18
+ # - Path is inside this plugin's own source tree
19
+ # - $ASHLR_NO_ENFORCE=1
20
+ # - tool_input.bypassSummary === true
21
+
22
+ set -u
23
+
24
+ THRESHOLD=5120
25
+
26
+ # Enforcement off by default; see pretooluse-read.sh for the full rationale.
27
+ # Day-to-day nudging/redirecting is handled by the .ts replacement
28
+ # (pretooluse-edit.ts); this .sh stays as a fallback for bun-less shells.
29
+ if [ "${ASHLR_ENFORCE:-0}" != "1" ]; then
30
+ exit 0
31
+ fi
32
+ if [ "${ASHLR_NO_ENFORCE:-0}" = "1" ]; then
33
+ exit 0
34
+ fi
35
+
36
+ STDIN_JSON=$(cat)
37
+ if [ -z "${STDIN_JSON// }" ]; then
38
+ exit 0
39
+ fi
40
+
41
+ PARSED=$(printf '%s' "$STDIN_JSON" | bun -e '
42
+ let raw = "";
43
+ process.stdin.on("data", (c) => (raw += c));
44
+ process.stdin.on("end", () => {
45
+ try {
46
+ const p = JSON.parse(raw);
47
+ const name = p?.tool_name ?? "";
48
+ const input = p?.tool_input ?? {};
49
+ const fp = typeof input.file_path === "string" ? input.file_path : "";
50
+ const bypass = input.bypassSummary === true ? "1" : "0";
51
+ process.stdout.write(`${name}\t${fp}\t${bypass}`);
52
+ } catch {
53
+ process.stdout.write("\t\t0");
54
+ }
55
+ });
56
+ ' 2>/dev/null)
57
+
58
+ TOOL_NAME="${PARSED%%$'\t'*}"
59
+ REST="${PARSED#*$'\t'}"
60
+ FILE_PATH="${REST%%$'\t'*}"
61
+ BYPASS="${REST##*$'\t'}"
62
+
63
+ if [ "$TOOL_NAME" != "Edit" ]; then
64
+ exit 0
65
+ fi
66
+
67
+ if [ -z "$FILE_PATH" ]; then
68
+ exit 0
69
+ fi
70
+
71
+ if [ "$BYPASS" = "1" ]; then
72
+ exit 0
73
+ fi
74
+
75
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
76
+ case "$FILE_PATH" in
77
+ "$PLUGIN_ROOT"/*) exit 0 ;;
78
+ esac
79
+
80
+ if [ ! -f "$FILE_PATH" ]; then
81
+ exit 0
82
+ fi
83
+
84
+ SIZE=$(stat -f%z "$FILE_PATH" 2>/dev/null || stat -c%s "$FILE_PATH" 2>/dev/null || echo 0)
85
+
86
+ if [ -z "$SIZE" ] || [ "$SIZE" -le "$THRESHOLD" ] 2>/dev/null; then
87
+ exit 0
88
+ fi
89
+
90
+ cat >&2 <<EOF
91
+ ashlr: refusing full Edit on large file $FILE_PATH ($SIZE bytes). Call ashlr__edit with diff-format to save ~80% tokens. Set ASHLR_NO_ENFORCE=1 to disable this guard.
92
+ EOF
93
+ exit 2
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-edit.ts — Cross-platform replacement for pretooluse-edit.sh.
4
+ *
5
+ * v1.18: by default, this hook BLOCKS the native Edit/Write/MultiEdit tools
6
+ * for existing files larger than 5KB and routes the agent to the matching
7
+ * ashlr__edit / ashlr__multi_edit MCP tool (diff-format, ~80% token savings).
8
+ * Set `ASHLR_HOOK_MODE=nudge` to downgrade to a soft `additionalContext`
9
+ * suggestion (the old v1.17 tool-redirect.ts behavior, absorbed into this
10
+ * hook after tool-redirect.ts was retired). Set `ASHLR_HOOK_MODE=off` — or
11
+ * `~/.ashlr/settings.json { toolRedirect: false }` — for total pass-through.
12
+ *
13
+ * v1.20.2: extended from Edit-only to Edit/Write/MultiEdit. The original
14
+ * v1.18 build only matched "Edit", which left ~200 Write calls/day and the
15
+ * MultiEdit volume completely unrouted. The new matcher in hooks.json fans
16
+ * Edit|Write|MultiEdit into this hook, and the per-tool branch below maps
17
+ * each to its ashlr__* equivalent. Write is special-cased: when the file
18
+ * doesn't exist yet (new-file creation) there's no ashlr equivalent, so we
19
+ * pass through silently rather than refuse.
20
+ *
21
+ * Edit-redirect carries more risk than read-redirect (write semantics), so
22
+ * the hook is conservative:
23
+ * - Only redirects when file size > 5KB (built-in Edit is fine for small
24
+ * files; the diff-summary savings don't materialize until payloads grow).
25
+ * - Never redirects inside CLAUDE_PLUGIN_ROOT (agents edit plugin code).
26
+ * - Never redirects outside cwd (user didn't bring the path into scope).
27
+ * - Skips Write on a non-existent path (new-file creation, no equivalent).
28
+ *
29
+ * Legacy: `ASHLR_ENFORCE=1` continues to use the exit-2 + stderr protocol
30
+ * for back-compat with existing harness configs and the hook-timings tests.
31
+ */
32
+
33
+ import {
34
+ buildNudgeContext,
35
+ buildPassThrough,
36
+ buildToolRedirectBlock,
37
+ enforcementDisabled,
38
+ fileSize,
39
+ flushHookTimings,
40
+ getHookModeFor,
41
+ isInsideCwd,
42
+ isInsidePluginRoot,
43
+ parsePayload,
44
+ pluginRootFrom,
45
+ readStdin,
46
+ recordHookTiming,
47
+ } from "./pretooluse-common";
48
+ import { installHookTimeout } from "./pretooluse-common";
49
+
50
+ if (import.meta.main) installHookTimeout("pretooluse-edit");
51
+ import { recordBlock } from "./_recent-blocks";
52
+
53
+ // Lowered from 5120 (5KB) to 2048 (2KB) based on 30-day edit distribution:
54
+ // 30.8% of edits were below 5KB (missed savings); dropping to 2KB captures
55
+ // an additional 20.2% of calls (2-5KB bucket) while only passing through
56
+ // 10.6% of volume — the <2KB micro-edits where MCP round-trip overhead
57
+ // would exceed the diff-summary savings.
58
+ const THRESHOLD = 2048;
59
+ /**
60
+ * ASHLR_EDIT_MIN_CHARS (default 80): minimum combined length of old_string +
61
+ * new_string for a redirect to fire. Edits below this threshold are too small
62
+ * to benefit from the diff-format round-trip — at p90 they produce negative
63
+ * savings because the MCP overhead exceeds the content savings. When a
64
+ * redirect would otherwise fire but the edit is below this threshold, the hook
65
+ * logs a `tool_skip_micro_edit` event and passes through to native Edit.
66
+ */
67
+ const EDIT_MIN_CHARS = (() => {
68
+ const v = parseInt(process.env.ASHLR_EDIT_MIN_CHARS ?? "", 10);
69
+ return Number.isFinite(v) && v > 0 ? v : 80;
70
+ })();
71
+ const hookStartedAt = Date.now();
72
+
73
+ async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
74
+ recordHookTiming({ hook: "pretooluse-edit", tool, durationMs: Date.now() - hookStartedAt, outcome });
75
+ await flushHookTimings();
76
+ process.exit(code);
77
+ }
78
+
79
+ const raw = await readStdin();
80
+ const payload = parsePayload(raw);
81
+ if (!payload) await exit(0, "ok");
82
+
83
+ const tool = payload!.tool_name || undefined;
84
+ const HANDLED_TOOLS = new Set(["Edit", "Write", "MultiEdit", "apply_patch", "functions.apply_patch"]);
85
+ if (!HANDLED_TOOLS.has(payload!.tool_name)) await exit(0, "ok", tool);
86
+
87
+ if (payload!.tool_name === "apply_patch" || payload!.tool_name === "functions.apply_patch") {
88
+ const mode = getHookModeFor("edit");
89
+ if (mode === "off") {
90
+ process.stdout.write(JSON.stringify(buildPassThrough()));
91
+ await exit(0, "ok", tool);
92
+ }
93
+ const nudge = buildNudgeContext(payload!.tool_name, {});
94
+ process.stdout.write(JSON.stringify(nudge ?? buildPassThrough()));
95
+ await exit(0, "ok", tool);
96
+ }
97
+
98
+ if (!payload!.file_path) await exit(0, "ok", tool);
99
+ if (payload!.bypass) await exit(0, "bypass", tool);
100
+
101
+ const pluginRoot = pluginRootFrom(import.meta.url);
102
+ // Never redirect edits inside the plugin's own tree — agents need direct
103
+ // access to modify plugin files without being rerouted into ashlr__edit.
104
+ if (isInsidePluginRoot(payload!.file_path, pluginRoot)) await exit(0, "ok", tool);
105
+
106
+ // Per-tool redirect targets. The MCP-prefixed name is the canonical Claude
107
+ // Code tool name; the short name appears in the nudge fallback message.
108
+ const TOOL_TARGETS: Record<string, { mcp: string; short: string; verb: string }> = {
109
+ Edit: { mcp: "mcp__plugin_ashlr_ashlr__ashlr__edit", short: "ashlr__edit", verb: "Edit" },
110
+ Write: { mcp: "mcp__plugin_ashlr_ashlr__ashlr__edit", short: "ashlr__edit", verb: "Write" },
111
+ MultiEdit: { mcp: "mcp__plugin_ashlr_ashlr__ashlr__multi_edit", short: "ashlr__multi_edit", verb: "MultiEdit" },
112
+ };
113
+ const target = TOOL_TARGETS[payload!.tool_name]!;
114
+
115
+ // Resolve hook mode up-front. "off" short-circuits without touching the
116
+ // filesystem; "nudge" fires for every matched tool regardless of file size
117
+ // (matches the retired tool-redirect.ts behavior where Edit was always nudged).
118
+ const mode = getHookModeFor("edit");
119
+ if (mode === "off") {
120
+ process.stdout.write(JSON.stringify(buildPassThrough()));
121
+ await exit(0, "ok", tool);
122
+ }
123
+ if (mode === "nudge") {
124
+ // Port of the retired hooks/tool-redirect.ts nudge: soft suggestion only,
125
+ // no permission decision. The built-in tool call proceeds unchanged. The
126
+ // nudge builder for "Write" returns null on non-existent files (Write of
127
+ // a new file has no ashlr equivalent), which falls through to passThrough.
128
+ const nudge = buildNudgeContext(payload!.tool_name, { file_path: payload!.file_path });
129
+ process.stdout.write(JSON.stringify(nudge ?? buildPassThrough()));
130
+ await exit(0, "ok", tool);
131
+ }
132
+
133
+ const size = fileSize(payload!.file_path);
134
+ // Write on a non-existent path = new-file creation. No ashlr equivalent
135
+ // (ashlr__edit needs a search string). Pass through silently — refusing
136
+ // would force the agent into a no-win loop with no fallback.
137
+ if (size === null) await exit(0, "ok", tool);
138
+ if (size! <= THRESHOLD) await exit(0, "ok", tool);
139
+
140
+ // Micro-edit guard: if the combined old+new content is smaller than
141
+ // EDIT_MIN_CHARS, the diff-format round-trip costs more than it saves.
142
+ // Pass through to native Edit and log a skip event so it's observable.
143
+ // Only fires when we actually have edit content — an Edit payload missing
144
+ // both strings is treated as "unknown size" and falls through to redirect.
145
+ if (payload!.tool_name === "Edit") {
146
+ const oldLen = payload!.old_string.length;
147
+ const newLen = payload!.new_string.length;
148
+ const combined = oldLen + newLen;
149
+ const haveContent = oldLen > 0 || newLen > 0;
150
+ if (haveContent && combined < EDIT_MIN_CHARS) {
151
+ // Emit a skip event to the session log (best-effort, fire-and-forget).
152
+ import("../servers/_events").then(({ logEvent }) => {
153
+ logEvent("tool_skip_micro_edit", {
154
+ tool: "Edit",
155
+ reason: "micro-edit",
156
+ extra: { combinedChars: combined, threshold: EDIT_MIN_CHARS },
157
+ }).catch(() => {});
158
+ }).catch(() => {});
159
+ process.stdout.write(JSON.stringify(buildPassThrough()));
160
+ await exit(0, "ok", tool);
161
+ }
162
+ }
163
+
164
+ // Legacy back-compat: `ASHLR_ENFORCE=1` → exit-code-based block on stderr.
165
+ if (!enforcementDisabled()) {
166
+ process.stderr.write(
167
+ `ashlr: refusing full ${target.verb} on large file ${payload!.file_path} (${size} bytes). Call ${target.short} with diff-format to save ~80% tokens. Set ASHLR_NO_ENFORCE=1 to disable this guard.\n`,
168
+ );
169
+ await exit(2, "block", tool);
170
+ }
171
+
172
+ // v1.18: default redirect mode. Fall back to nudge for paths outside cwd —
173
+ // never block on files the user didn't explicitly bring into scope.
174
+ if (!isInsideCwd(payload!.file_path)) {
175
+ const nudge = buildNudgeContext(payload!.tool_name, { file_path: payload!.file_path });
176
+ process.stdout.write(JSON.stringify(nudge ?? buildPassThrough()));
177
+ await exit(0, "ok", tool);
178
+ }
179
+
180
+ // JSON.stringify so Windows path backslashes get escaped — naive template
181
+ // interpolation produced invalid JSON like `"path": "D:\a\..."` which
182
+ // downstream parsers (and the v1.22 nudge tests) couldn't decode.
183
+ const argsJson = payload!.tool_name === "MultiEdit"
184
+ ? JSON.stringify({ edits: [{ path: payload!.file_path, search: "...", replace: "..." }] })
185
+ : JSON.stringify({ path: payload!.file_path, search: "...", replace: "...", strict: true });
186
+ const why = payload!.tool_name === "MultiEdit"
187
+ ? `native MultiEdit echoes the full file; ${target.short} applies all edits atomically and returns one consolidated diff summary (~80% token savings).`
188
+ : `native ${target.verb} on ${payload!.file_path} (${size} bytes) echoes the full file; ${target.short} applies a strict search/replace and returns only a compact diff summary (~80% token savings).`;
189
+ // Track G: record block for posttooluse-correlate (best-effort, never throws).
190
+ recordBlock({ ts: Date.now(), toolName: payload!.tool_name, filePath: payload!.file_path });
191
+ process.stdout.write(JSON.stringify(buildToolRedirectBlock({
192
+ mcpToolName: target.mcp,
193
+ argsJson,
194
+ why,
195
+ savingsPct: 80,
196
+ })));
197
+ await exit(0, "block", tool);
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-glob.ts — PreToolUse hook for the Glob tool.
4
+ *
5
+ * Glob has no direct ashlr__* equivalent, so this hook never blocks.
6
+ * Its purpose is audit-trail coverage: every Glob invocation is logged to
7
+ * hook-timings.jsonl so `/ashlr-hook-timings` shows a complete picture of
8
+ * all built-in tool activity, not just Read/Grep/Edit.
9
+ *
10
+ * In nudge mode we emit a soft `additionalContext` reminding the agent that
11
+ * ashlr__grep can serve many of the same discovery use-cases with genome-
12
+ * aware ranking. In redirect and off modes we pass through silently.
13
+ */
14
+
15
+ import {
16
+ buildPassThrough,
17
+ flushHookTimings,
18
+ getHookModeFor,
19
+ parsePayload,
20
+ readStdin,
21
+ recordHookTiming,
22
+ } from "./pretooluse-common";
23
+ import { installHookTimeout } from "./pretooluse-common";
24
+
25
+ if (import.meta.main) installHookTimeout("pretooluse-glob");
26
+
27
+ const hookStartedAt = Date.now();
28
+
29
+ async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
30
+ recordHookTiming({ hook: "pretooluse-glob", tool, durationMs: Date.now() - hookStartedAt, outcome });
31
+ await flushHookTimings();
32
+ process.exit(code);
33
+ }
34
+
35
+ const raw = await readStdin();
36
+ const payload = parsePayload(raw);
37
+ if (!payload) await exit(0, "ok");
38
+
39
+ const tool = payload!.tool_name || undefined;
40
+ if (payload!.tool_name !== "Glob") await exit(0, "ok", tool);
41
+
42
+ const mode = getHookModeFor("glob");
43
+ if (mode === "nudge") {
44
+ // Soft suggestion: ashlr__grep can handle many Glob-like discovery queries
45
+ // with genome-aware ranking when a genome exists.
46
+ let pattern = "*";
47
+ try {
48
+ const parsed = JSON.parse(raw) as { tool_input?: { pattern?: string } };
49
+ if (typeof parsed?.tool_input?.pattern === "string") pattern = parsed.tool_input.pattern;
50
+ } catch {
51
+ /* ignore */
52
+ }
53
+ process.stdout.write(
54
+ JSON.stringify({
55
+ hookSpecificOutput: {
56
+ hookEventName: "PreToolUse",
57
+ additionalContext:
58
+ `[ashlr] For file discovery with ranking, consider \`ashlr__grep\` — ` +
59
+ `when .ashlrcode/genome/ exists it returns the most relevant sections ` +
60
+ `instead of a flat glob list. Pattern: ${JSON.stringify(pattern)}.`,
61
+ },
62
+ }),
63
+ );
64
+ await exit(0, "ok", tool);
65
+ }
66
+
67
+ // redirect or off: pass through silently (Glob has no ashlr equivalent to
68
+ // block toward; audit trail is captured via the timing record above).
69
+ process.stdout.write(JSON.stringify(buildPassThrough()));
70
+ await exit(0, "ok", tool);
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env bash
2
+ # ashlr-plugin PreToolUse:Grep enforcement hook.
3
+ #
4
+ # Blocks the built-in Grep tool and redirects the agent to ashlr__grep, which
5
+ # routes through genome-aware RAG (when .ashlrcode/genome/ exists) or a
6
+ # truncated ripgrep fallback — saves tokens on every project-level search.
7
+ #
8
+ # Contract (Claude Code PreToolUse):
9
+ # stdin → JSON { tool_name, tool_input: { pattern, path?, ... }, ... }
10
+ # exit 0 → allow the built-in call
11
+ # exit 2 → block; stderr is shown to the agent as a tool error
12
+ #
13
+ # Exceptions (pass-through):
14
+ # - $ASHLR_NO_ENFORCE=1 (global escape hatch)
15
+ # - tool_input.bypassSummary === true (explicit override)
16
+ # - Search scoped entirely inside the plugin's own source tree
17
+ #
18
+ # Note: We always enforce on project-level Grep. ashlr__grep has a truncated
19
+ # rg fallback when no genome exists, so there is no "small project" exemption
20
+ # — raw rg output streams unbounded matches and is always wasteful relative
21
+ # to ashlr__grep.
22
+
23
+ set -u
24
+
25
+ # Enforcement off by default; see pretooluse-read.sh for the full rationale.
26
+ # Day-to-day nudging/redirecting is handled by the .ts replacement
27
+ # (pretooluse-grep.ts); this .sh stays as a fallback for bun-less shells.
28
+ if [ "${ASHLR_ENFORCE:-0}" != "1" ]; then
29
+ exit 0
30
+ fi
31
+ if [ "${ASHLR_NO_ENFORCE:-0}" = "1" ]; then
32
+ exit 0
33
+ fi
34
+
35
+ STDIN_JSON=$(cat)
36
+ if [ -z "${STDIN_JSON// }" ]; then
37
+ exit 0
38
+ fi
39
+
40
+ PARSED=$(printf '%s' "$STDIN_JSON" | bun -e '
41
+ let raw = "";
42
+ process.stdin.on("data", (c) => (raw += c));
43
+ process.stdin.on("end", () => {
44
+ try {
45
+ const p = JSON.parse(raw);
46
+ const name = p?.tool_name ?? "";
47
+ const input = p?.tool_input ?? {};
48
+ const pattern = typeof input.pattern === "string" ? input.pattern : "";
49
+ const path = typeof input.path === "string" ? input.path : "";
50
+ const bypass = input.bypassSummary === true ? "1" : "0";
51
+ // Escape tabs/newlines in pattern so the shell split stays clean.
52
+ const safe = pattern.replace(/[\t\n\r]/g, " ");
53
+ process.stdout.write(`${name}\t${safe}\t${path}\t${bypass}`);
54
+ } catch {
55
+ process.stdout.write("\t\t\t0");
56
+ }
57
+ });
58
+ ' 2>/dev/null)
59
+
60
+ IFS=$'\t' read -r TOOL_NAME PATTERN SEARCH_PATH BYPASS <<< "$PARSED"
61
+
62
+ if [ "$TOOL_NAME" != "Grep" ]; then
63
+ exit 0
64
+ fi
65
+
66
+ if [ "${BYPASS:-0}" = "1" ]; then
67
+ exit 0
68
+ fi
69
+
70
+ # Exempt searches confined to the plugin's own tree.
71
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
72
+ if [ -n "${SEARCH_PATH:-}" ]; then
73
+ case "$SEARCH_PATH" in
74
+ "$PLUGIN_ROOT"|"$PLUGIN_ROOT"/*) exit 0 ;;
75
+ esac
76
+ fi
77
+
78
+ # Escape double-quotes and backslashes for the redirect message.
79
+ SAFE_PATTERN="${PATTERN//\\/\\\\}"
80
+ SAFE_PATTERN="${SAFE_PATTERN//\"/\\\"}"
81
+
82
+ cat >&2 <<EOF
83
+ ashlr: routing Grep through ashlr__grep for genome-aware retrieval (saves tokens when genome exists, truncates otherwise). Call ashlr__grep with pattern='${SAFE_PATTERN}'. Set ASHLR_NO_ENFORCE=1 to disable this guard.
84
+ EOF
85
+ exit 2