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,96 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-grep.ts — Cross-platform replacement for pretooluse-grep.sh.
4
+ *
5
+ * v1.18: by default, this hook BLOCKS the native Grep tool and routes the
6
+ * agent to ashlr__grep (genome-aware RAG or truncated rg fallback). Set
7
+ * `ASHLR_HOOK_MODE=nudge` to downgrade to a soft `additionalContext`
8
+ * suggestion (the old v1.17 tool-redirect.ts behavior, absorbed into this
9
+ * hook after tool-redirect.ts was retired). Set `ASHLR_HOOK_MODE=off` — or
10
+ * `~/.ashlr/settings.json { toolRedirect: false }` — for total pass-through.
11
+ *
12
+ * Legacy: `ASHLR_ENFORCE=1` continues to use the exit-2 + stderr protocol
13
+ * for back-compat with existing harness configs and the hook-timings tests.
14
+ */
15
+
16
+ import {
17
+ buildNudgeContext,
18
+ buildPassThrough,
19
+ buildToolRedirectBlock,
20
+ enforcementDisabled,
21
+ flushHookTimings,
22
+ getHookModeFor,
23
+ isInsideCwd,
24
+ isInsidePluginRoot,
25
+ parsePayload,
26
+ pluginRootFrom,
27
+ readStdin,
28
+ recordHookTiming,
29
+ } from "./pretooluse-common";
30
+ import { installHookTimeout } from "./pretooluse-common";
31
+
32
+ if (import.meta.main) installHookTimeout("pretooluse-grep");
33
+ import { recordBlock } from "./_recent-blocks";
34
+
35
+ const hookStartedAt = Date.now();
36
+
37
+ async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
38
+ recordHookTiming({ hook: "pretooluse-grep", tool, durationMs: Date.now() - hookStartedAt, outcome });
39
+ await flushHookTimings();
40
+ process.exit(code);
41
+ }
42
+
43
+ const raw = await readStdin();
44
+ const payload = parsePayload(raw);
45
+ if (!payload) await exit(0, "ok");
46
+
47
+ const tool = payload!.tool_name || undefined;
48
+ if (payload!.tool_name !== "Grep") await exit(0, "ok", tool);
49
+ if (payload!.bypass) await exit(0, "bypass", tool);
50
+
51
+ const pluginRoot = pluginRootFrom(import.meta.url);
52
+ // Never redirect a Grep that's explicitly scoped inside the plugin tree —
53
+ // agents editing the plugin itself need direct access to rg behavior.
54
+ if (payload!.search_path && isInsidePluginRoot(payload!.search_path, pluginRoot)) {
55
+ await exit(0, "ok", tool);
56
+ }
57
+
58
+ // Legacy back-compat: `ASHLR_ENFORCE=1` → exit-code-based block on stderr.
59
+ if (!enforcementDisabled()) {
60
+ const safePattern = payload!.pattern.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
61
+ process.stderr.write(
62
+ `ashlr: routing Grep through ashlr__grep for genome-aware retrieval (saves tokens when genome exists, truncates otherwise). Call ashlr__grep with pattern="${safePattern}". Set ASHLR_NO_ENFORCE=1 to disable this guard.\n`,
63
+ );
64
+ await exit(2, "block", tool);
65
+ }
66
+
67
+ // v1.18: default redirect mode. If the caller supplied a search_path that
68
+ // lies outside cwd, fall back to nudge — never block on paths the user
69
+ // didn't explicitly bring into scope. Grep without a path implicitly runs
70
+ // in cwd, which is fine to redirect.
71
+ const mode = getHookModeFor("grep");
72
+ const outOfScope = !!payload!.search_path && !isInsideCwd(payload!.search_path);
73
+ if (mode === "off") {
74
+ process.stdout.write(JSON.stringify(buildPassThrough()));
75
+ await exit(0, "ok", tool);
76
+ }
77
+ if (mode === "nudge" || outOfScope) {
78
+ const nudge = buildNudgeContext("Grep", { pattern: payload!.pattern });
79
+ process.stdout.write(JSON.stringify(nudge ?? buildPassThrough()));
80
+ await exit(0, "ok", tool);
81
+ }
82
+
83
+ // Track G: record block for posttooluse-correlate (best-effort, never throws).
84
+ recordBlock({ ts: Date.now(), toolName: "Grep", pattern: payload!.pattern });
85
+ // JSON.stringify so Windows path backslashes (search_path) and any
86
+ // special characters in the pattern are escaped properly.
87
+ const argsJson = payload!.search_path
88
+ ? JSON.stringify({ pattern: payload!.pattern, path: payload!.search_path })
89
+ : JSON.stringify({ pattern: payload!.pattern });
90
+ process.stdout.write(JSON.stringify(buildToolRedirectBlock({
91
+ mcpToolName: "mcp__plugin_ashlr_ashlr__ashlr__grep",
92
+ argsJson,
93
+ why: "native Grep returns ~10× more bytes; ashlr__grep is genome-aware and uses LLM summarization for large result sets.",
94
+ savingsPct: 80,
95
+ })));
96
+ await exit(0, "block", tool);
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-notebookedit.ts — PreToolUse hook for the NotebookEdit tool.
4
+ *
5
+ * v1.22 (Track C): promoted from audit-only to redirect. The redirect target
6
+ * `ashlr__notebook_edit` now exists and returns a compressed cell-window
7
+ * response instead of echoing the full notebook state.
8
+ *
9
+ * Redirect applies only when:
10
+ * - hook mode is "redirect" (default)
11
+ * - the notebook path is inside cwd (same safety rule as pretooluse-edit)
12
+ *
13
+ * Pass-through when:
14
+ * - hook mode is "nudge" or "off"
15
+ * - path is outside cwd (can't redirect safely)
16
+ * - NotebookEdit is called on a non-existent file (edge case, let Claude Code handle it)
17
+ */
18
+
19
+ import {
20
+ buildNudgeContext,
21
+ buildPassThrough,
22
+ buildRedirectBlock,
23
+ flushHookTimings,
24
+ getHookModeFor,
25
+ isInsideCwd,
26
+ isInsidePluginRoot,
27
+ parsePayload,
28
+ pluginRootFrom,
29
+ readStdin,
30
+ recordHookTiming,
31
+ } from "./pretooluse-common";
32
+ import { installHookTimeout } from "./pretooluse-common";
33
+
34
+ if (import.meta.main) installHookTimeout("pretooluse-notebookedit");
35
+
36
+ const hookStartedAt = Date.now();
37
+
38
+ async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
39
+ recordHookTiming({ hook: "pretooluse-notebookedit", tool, durationMs: Date.now() - hookStartedAt, outcome });
40
+ await flushHookTimings();
41
+ process.exit(code);
42
+ }
43
+
44
+ const raw = await readStdin();
45
+ const payload = parsePayload(raw);
46
+ if (!payload) await exit(0, "ok");
47
+
48
+ const tool = payload!.tool_name || undefined;
49
+ if (payload!.tool_name !== "NotebookEdit") await exit(0, "ok", tool);
50
+ if (payload!.bypass) await exit(0, "bypass", tool);
51
+
52
+ // NotebookEdit uses `notebook_path` in its input — check both field names for
53
+ // robustness since the harness payload shape may vary across Claude Code versions.
54
+ const rawInput = (() => {
55
+ try {
56
+ const p = JSON.parse(raw);
57
+ return p?.tool_input ?? {};
58
+ } catch {
59
+ return {};
60
+ }
61
+ })();
62
+ const notebookPath: string =
63
+ typeof rawInput.notebook_path === "string"
64
+ ? rawInput.notebook_path
65
+ : typeof rawInput.file_path === "string"
66
+ ? rawInput.file_path
67
+ : "";
68
+
69
+ const pluginRoot = pluginRootFrom(import.meta.url);
70
+ if (isInsidePluginRoot(notebookPath, pluginRoot)) await exit(0, "ok", tool);
71
+
72
+ // NotebookEdit redirect target `ashlr__notebook_edit` exists as of v1.22. In
73
+ // redirect mode we block and route. In nudge mode we hint at the canonical
74
+ // MCP name. In off mode we pass through silently.
75
+ const mode = getHookModeFor("notebookedit");
76
+ if (mode === "off") {
77
+ process.stdout.write(JSON.stringify(buildPassThrough()));
78
+ await exit(0, "ok", tool);
79
+ }
80
+
81
+ if (mode === "nudge") {
82
+ process.stdout.write(
83
+ JSON.stringify({
84
+ hookSpecificOutput: {
85
+ hookEventName: "PreToolUse",
86
+ additionalContext:
87
+ `[ashlr] For notebook cell edits, prefer mcp__plugin_ashlr_ashlr__ashlr__notebook_edit — ` +
88
+ `it applies the edit and returns only the edited cell and its immediate neighbors ` +
89
+ `instead of echoing the full notebook state. ` +
90
+ `Call shape: { "notebookPath": "<path>", "cellIndex": <n>, "newSource": "..." }.`,
91
+ },
92
+ }),
93
+ );
94
+ await exit(0, "ok", tool);
95
+ }
96
+
97
+ // redirect mode: block and route to ashlr__notebook_edit.
98
+ if (!notebookPath || !isInsideCwd(notebookPath)) {
99
+ // Outside cwd or path unknown — nudge only, don't block.
100
+ process.stdout.write(
101
+ JSON.stringify({
102
+ hookSpecificOutput: {
103
+ hookEventName: "PreToolUse",
104
+ additionalContext:
105
+ `[ashlr] For notebook cell edits, prefer \`ashlr__notebook_edit\` — ` +
106
+ `it returns only a cell-window summary instead of the full notebook.`,
107
+ },
108
+ }),
109
+ );
110
+ await exit(0, "ok", tool);
111
+ }
112
+
113
+ const mcp = "mcp__plugin_ashlr_ashlr__ashlr__notebook_edit";
114
+ const reason =
115
+ `[ashlr] To bypass: set ASHLR_HOOK_MODE=nudge in ~/.ashlr/config.json. ` +
116
+ `Current rule: blocking built-in NotebookEdit on ${notebookPath || "<notebook>"} — ` +
117
+ `call ${mcp} instead, which applies the cell edit and returns only the edited cell ` +
118
+ `plus one cell above and below (full notebook never echoed). ` +
119
+ `Equivalent call: { "notebookPath": "${notebookPath}", "cellIndex": <n>, "newSource": "..." }.`;
120
+ process.stdout.write(JSON.stringify(buildRedirectBlock(reason)));
121
+ await exit(0, "block", tool);
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env bash
2
+ # ashlr-plugin PreToolUse:Read enforcement hook.
3
+ #
4
+ # Blocks the built-in Read tool on files larger than 2KB and redirects the
5
+ # agent to ashlr__read, which returns a snipCompact-truncated view (head +
6
+ # tail, elided middle) saving ~60-95% of tokens on large files.
7
+ #
8
+ # Contract (Claude Code PreToolUse):
9
+ # stdin → JSON { tool_name, tool_input: { file_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
+ # - File < 2 KB (no meaningful savings)
15
+ # - File does not exist or cannot be stat'd (let built-in produce the real error)
16
+ # - Path is inside this plugin's own source tree (dogfooding / self-edits)
17
+ # - $ASHLR_NO_ENFORCE=1 (global escape hatch)
18
+ # - tool_input.bypassSummary === true (explicit override by agent)
19
+ #
20
+ # Design notes:
21
+ # - Uses `bun -e` for JSON parsing (bun is already a plugin dependency).
22
+ # - Single stat call, no other subprocess work, typical runtime <30ms.
23
+
24
+ set -u
25
+
26
+ THRESHOLD=2048
27
+
28
+ # Enforcement is OFF by default. The hard-block via `exit 2` was too
29
+ # aggressive — it interrupted the user in `bypassPermissions` mode when all
30
+ # they wanted was a silent suggestion. The .ts replacement (pretooluse-read.ts)
31
+ # now covers both the redirect (JSON deny) and nudge (additionalContext)
32
+ # paths; this .sh is kept only for environments that can't run bun hooks.
33
+ #
34
+ # To re-enable hard enforcement (e.g. on CI where you want zero tolerance
35
+ # for full-file reads), set ASHLR_ENFORCE=1. `ASHLR_NO_ENFORCE=1` remains
36
+ # honored for backwards compatibility with anyone scripting against the
37
+ # old flag name.
38
+ if [ "${ASHLR_ENFORCE:-0}" != "1" ]; then
39
+ exit 0
40
+ fi
41
+ if [ "${ASHLR_NO_ENFORCE:-0}" = "1" ]; then
42
+ exit 0
43
+ fi
44
+
45
+ # Slurp stdin JSON; malformed or empty → pass-through.
46
+ STDIN_JSON=$(cat)
47
+ if [ -z "${STDIN_JSON// }" ]; then
48
+ exit 0
49
+ fi
50
+
51
+ # Parse tool_name, file_path, bypassSummary via bun. Output is tab-separated:
52
+ # <tool_name>\t<file_path>\t<bypassSummary>
53
+ PARSED=$(printf '%s' "$STDIN_JSON" | bun -e '
54
+ let raw = "";
55
+ process.stdin.on("data", (c) => (raw += c));
56
+ process.stdin.on("end", () => {
57
+ try {
58
+ const p = JSON.parse(raw);
59
+ const name = p?.tool_name ?? "";
60
+ const input = p?.tool_input ?? {};
61
+ const fp = typeof input.file_path === "string" ? input.file_path : "";
62
+ const bypass = input.bypassSummary === true ? "1" : "0";
63
+ process.stdout.write(`${name}\t${fp}\t${bypass}`);
64
+ } catch {
65
+ process.stdout.write("\t\t0");
66
+ }
67
+ });
68
+ ' 2>/dev/null)
69
+
70
+ TOOL_NAME="${PARSED%%$'\t'*}"
71
+ REST="${PARSED#*$'\t'}"
72
+ FILE_PATH="${REST%%$'\t'*}"
73
+ BYPASS="${REST##*$'\t'}"
74
+
75
+ # Only enforce on Read. Any other tool name → pass-through (defensive).
76
+ if [ "$TOOL_NAME" != "Read" ]; then
77
+ exit 0
78
+ fi
79
+
80
+ # Missing or empty path → let built-in handle it.
81
+ if [ -z "$FILE_PATH" ]; then
82
+ exit 0
83
+ fi
84
+
85
+ # Explicit bypass by caller.
86
+ if [ "$BYPASS" = "1" ]; then
87
+ exit 0
88
+ fi
89
+
90
+ # Plugin's own source tree is exempt (users editing the plugin itself).
91
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
92
+ case "$FILE_PATH" in
93
+ "$PLUGIN_ROOT"/*) exit 0 ;;
94
+ esac
95
+
96
+ # File must exist and be a regular file to enforce; else pass-through.
97
+ if [ ! -f "$FILE_PATH" ]; then
98
+ exit 0
99
+ fi
100
+
101
+ # Portable stat: macOS uses -f%z, Linux uses -c%s.
102
+ SIZE=$(stat -f%z "$FILE_PATH" 2>/dev/null || stat -c%s "$FILE_PATH" 2>/dev/null || echo 0)
103
+
104
+ if [ -z "$SIZE" ] || [ "$SIZE" -le "$THRESHOLD" ] 2>/dev/null; then
105
+ exit 0
106
+ fi
107
+
108
+ # Rough token-savings estimate: snipCompact keeps ~head+tail (~1KB), so savings
109
+ # are approximately (SIZE - 1024) / 4 tokens (4 bytes/token avg).
110
+ SAVED_BYTES=$(( SIZE - 1024 ))
111
+ SAVED_TOKENS=$(( SAVED_BYTES / 4 ))
112
+ if [ "$SAVED_TOKENS" -lt 0 ]; then SAVED_TOKENS=0; fi
113
+
114
+ # Block and redirect.
115
+ cat >&2 <<EOF
116
+ ashlr: refusing full Read of $FILE_PATH ($SIZE bytes). Call ashlr__read instead for snipCompact truncation — saves ~${SAVED_TOKENS} tokens. Pass bypassSummary: true on ashlr__read if you truly need the raw file. Set ASHLR_NO_ENFORCE=1 to disable this guard.
117
+ EOF
118
+ exit 2
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-read.ts — Cross-platform replacement for pretooluse-read.sh.
4
+ *
5
+ * v1.18: by default, this hook BLOCKS the native Read tool for files larger
6
+ * than the snipCompact threshold and routes the agent to ashlr__read instead
7
+ * ("redirect" mode). Set `ASHLR_HOOK_MODE=nudge` to fall back to a soft
8
+ * `additionalContext` suggestion (the old v1.17 tool-redirect.ts behavior,
9
+ * since absorbed into this hook after tool-redirect.ts was retired). Set
10
+ * `ASHLR_HOOK_MODE=off` — or `~/.ashlr/settings.json { toolRedirect: false }`
11
+ * — for total pass-through with no nudge at all.
12
+ *
13
+ * Contract (Claude Code PreToolUse):
14
+ * stdin -> JSON { tool_name, tool_input: { file_path, ... }, ... }
15
+ * stdout -> JSON { hookSpecificOutput: {
16
+ * hookEventName: "PreToolUse",
17
+ * permissionDecision: "deny",
18
+ * permissionDecisionReason: "..."
19
+ * } } (redirect mode, blocks)
20
+ * stdout -> JSON { hookSpecificOutput: {
21
+ * hookEventName: "PreToolUse",
22
+ * additionalContext: "..."
23
+ * } } (nudge mode)
24
+ * stdout -> JSON { hookSpecificOutput: { hookEventName: "PreToolUse" } }
25
+ * (off mode / pass-through)
26
+ * exit 0 -> allow / proceed (with stdout JSON above, if any)
27
+ * exit 2 -> legacy block (stderr is shown as a tool error). Only taken
28
+ * when `ASHLR_ENFORCE=1` is set — preserved for back-compat with
29
+ * harness configurations and the hook-timings test suite.
30
+ *
31
+ * Safety: we never block on files outside `cwd` or inside CLAUDE_PLUGIN_ROOT.
32
+ * Those fall through to a silent pass-through even in redirect mode, so the
33
+ * agent can edit/read plugin internals and other-project files without being
34
+ * rerouted into ashlr__*.
35
+ */
36
+
37
+ import {
38
+ buildNudgeContext,
39
+ buildPassThrough,
40
+ buildToolRedirectBlock,
41
+ enforcementDisabled,
42
+ fileSize,
43
+ flushHookTimings,
44
+ getHookModeFor,
45
+ isInsideCwd,
46
+ isInsidePluginRoot,
47
+ parsePayload,
48
+ pluginRootFrom,
49
+ readStdin,
50
+ recordHookTiming,
51
+ } from "./pretooluse-common";
52
+ import { installHookTimeout } from "./pretooluse-common";
53
+
54
+ if (import.meta.main) installHookTimeout("pretooluse-read");
55
+ import { recordBlock } from "./_recent-blocks";
56
+
57
+ const THRESHOLD = 2048;
58
+ const hookStartedAt = Date.now();
59
+
60
+ async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
61
+ recordHookTiming({ hook: "pretooluse-read", tool, durationMs: Date.now() - hookStartedAt, outcome });
62
+ await flushHookTimings();
63
+ process.exit(code);
64
+ }
65
+
66
+ const raw = await readStdin();
67
+ const payload = parsePayload(raw);
68
+ if (!payload) await exit(0, "ok");
69
+
70
+ const tool = payload!.tool_name || undefined;
71
+ if (payload!.tool_name !== "Read") await exit(0, "ok", tool);
72
+ if (!payload!.file_path) await exit(0, "ok", tool);
73
+ if (payload!.bypass) await exit(0, "bypass", tool);
74
+
75
+ const pluginRoot = pluginRootFrom(import.meta.url);
76
+ // Never redirect reads inside the plugin's own tree — agents editing the
77
+ // ashlr-plugin itself need direct access without being rerouted.
78
+ if (isInsidePluginRoot(payload!.file_path, pluginRoot)) await exit(0, "ok", tool);
79
+
80
+ const size = fileSize(payload!.file_path);
81
+ if (size === null) await exit(0, "ok", tool);
82
+ if (size! <= THRESHOLD) await exit(0, "ok", tool);
83
+
84
+ // Legacy back-compat: `ASHLR_ENFORCE=1` → exit-code-based block on stderr.
85
+ if (!enforcementDisabled()) {
86
+ const savedTokens = Math.max(0, Math.floor((size! - 1024) / 4));
87
+ process.stderr.write(
88
+ `ashlr: refusing full Read of ${payload!.file_path} (${size} bytes). Call ashlr__read instead for snipCompact truncation — saves ~${savedTokens} tokens. Pass bypassSummary: true on ashlr__read if you truly need the raw file. Set ASHLR_NO_ENFORCE=1 to disable this guard.\n`,
89
+ );
90
+ await exit(2, "block", tool);
91
+ }
92
+
93
+ // v1.18: default redirect mode. Fall back to nudge when the file lies
94
+ // outside cwd (e.g. /tmp, unrelated repos) — we only block paths the user
95
+ // has actually brought into scope.
96
+ const mode = getHookModeFor("read");
97
+ if (mode === "off") {
98
+ process.stdout.write(JSON.stringify(buildPassThrough()));
99
+ await exit(0, "ok", tool);
100
+ }
101
+ if (mode === "nudge" || !isInsideCwd(payload!.file_path)) {
102
+ const nudge = buildNudgeContext("Read", { file_path: payload!.file_path });
103
+ process.stdout.write(JSON.stringify(nudge ?? buildPassThrough()));
104
+ await exit(0, "ok", tool);
105
+ }
106
+
107
+ const savedTokens = Math.max(0, Math.floor((size! - 1024) / 4));
108
+ const savingsPct = Math.min(90, Math.round((savedTokens / Math.max(1, size! / 4)) * 100));
109
+ // Track G: record block for posttooluse-correlate (best-effort, never throws).
110
+ recordBlock({ ts: Date.now(), toolName: "Read", filePath: payload!.file_path });
111
+ process.stdout.write(JSON.stringify(buildToolRedirectBlock({
112
+ mcpToolName: "mcp__plugin_ashlr_ashlr__ashlr__read",
113
+ // JSON.stringify so Windows path backslashes are escaped properly.
114
+ argsJson: JSON.stringify({ path: payload!.file_path }),
115
+ why: `native Read returns all ${size} bytes; ashlr__read returns snipCompact-truncated view (head + tail, elided middle), saving ~${savedTokens} tokens. Pass bypassSummary:true to get the full file.`,
116
+ savingsPct,
117
+ })));
118
+ await exit(0, "block", tool);
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-task.ts — PreToolUse hook for TaskList and TaskGet tools.
4
+ *
5
+ * In redirect mode (default) this hook BLOCKS the native TaskList/TaskGet
6
+ * calls and routes the agent to ashlr__task_list / ashlr__task_get, which
7
+ * filter, paginate, and truncate task payloads to save tokens.
8
+ *
9
+ * TaskCreate and TaskUpdate are NOT intercepted — they're tiny inputs.
10
+ *
11
+ * Escape valves (in order of granularity):
12
+ * - `ASHLR_TASK_PASSTHROUGH=1` env — pass through TaskList/TaskGet for this
13
+ * invocation only. Use from skills like `/deep-work` that orchestrate
14
+ * many subagents and need the raw, full task list.
15
+ * - `~/.ashlr/settings.json { "hookModes": { "task": "nudge" } }` — soft
16
+ * suggestion instead of block, permanently.
17
+ * - `ASHLR_HOOK_MODE=nudge` env — same as above but global (all hooks).
18
+ * - `ASHLR_HOOK_MODE=off` (or `settings.json { toolRedirect: false }`) —
19
+ * total pass-through, all hooks.
20
+ */
21
+
22
+ import {
23
+ buildPassThrough,
24
+ buildRedirectBlock,
25
+ flushHookTimings,
26
+ getHookModeFor,
27
+ installHookTimeout,
28
+ parsePayload,
29
+ readStdin,
30
+ recordHookTiming,
31
+ } from "./pretooluse-common";
32
+
33
+ if (import.meta.main) installHookTimeout("pretooluse-task");
34
+
35
+ const hookStartedAt = Date.now();
36
+
37
+ async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
38
+ recordHookTiming({ hook: "pretooluse-task", tool, durationMs: Date.now() - hookStartedAt, outcome });
39
+ await flushHookTimings();
40
+ process.exit(code);
41
+ }
42
+
43
+ const raw = await readStdin();
44
+ const payload = parsePayload(raw);
45
+ if (!payload) await exit(0, "ok");
46
+
47
+ const toolName = payload!.tool_name;
48
+ const tool = toolName || undefined;
49
+
50
+ if (toolName !== "TaskList" && toolName !== "TaskGet") await exit(0, "ok", tool);
51
+ if (payload!.bypass) await exit(0, "bypass", tool);
52
+
53
+ // Per-invocation escape valve. Skills that legitimately need the full task
54
+ // list (e.g. multi-agent orchestrators like /deep-work) can set this in their
55
+ // prelude before issuing TaskList/TaskGet.
56
+ if (process.env.ASHLR_TASK_PASSTHROUGH === "1") {
57
+ process.stdout.write(JSON.stringify(buildPassThrough()));
58
+ await exit(0, "bypass", tool);
59
+ }
60
+
61
+ const mode = getHookModeFor("task");
62
+ if (mode === "off") {
63
+ process.stdout.write(JSON.stringify(buildPassThrough()));
64
+ await exit(0, "ok", tool);
65
+ }
66
+
67
+ // Parse tool input for a more helpful redirect message.
68
+ let taskId = "<taskId>";
69
+ let status = "<status>";
70
+ try {
71
+ const parsed = JSON.parse(raw) as { tool_input?: { task_id?: string; taskId?: string; status?: string } };
72
+ const input = parsed?.tool_input ?? {};
73
+ if (typeof input.task_id === "string") taskId = input.task_id;
74
+ if (typeof input.taskId === "string") taskId = input.taskId;
75
+ if (typeof input.status === "string") status = input.status;
76
+ } catch {
77
+ /* ignore */
78
+ }
79
+
80
+ if (mode === "nudge") {
81
+ const ashlrTool = toolName === "TaskList" ? "ashlr__task_list" : "ashlr__task_get";
82
+ const exampleCall =
83
+ toolName === "TaskList"
84
+ ? `{ "status": ${JSON.stringify(status !== "<status>" ? status : undefined)} }`
85
+ : `{ "taskId": ${JSON.stringify(taskId)} }`;
86
+ process.stdout.write(
87
+ JSON.stringify({
88
+ hookSpecificOutput: {
89
+ hookEventName: "PreToolUse",
90
+ additionalContext:
91
+ `[ashlr] Prefer the MCP tool \`${ashlrTool}\` over the built-in ${toolName}. ` +
92
+ (toolName === "TaskList"
93
+ ? "It filters by status/owner, limits to 30 rows by default, and returns a compact column view (taskId, status, subject, ageMin). "
94
+ : "It snipCompacts descriptions > 2KB and returns a compact structured view. ") +
95
+ `Call it with ${exampleCall}.`,
96
+ },
97
+ }),
98
+ );
99
+ await exit(0, "ok", tool);
100
+ }
101
+
102
+ // Default: redirect mode.
103
+ const escapeValves =
104
+ `Escape valves: ASHLR_TASK_PASSTHROUGH=1 (one-shot pass-through, ` +
105
+ `intended for skills like /deep-work that need the full list); ` +
106
+ `~/.ashlr/settings.json { "hookModes": { "task": "nudge" } } (soft hint ` +
107
+ `instead of block); ASHLR_HOOK_MODE=nudge (global soft hint).`;
108
+
109
+ let reason: string;
110
+ if (toolName === "TaskList") {
111
+ const safeStatus = status.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
112
+ reason =
113
+ `[ashlr] Blocking the built-in TaskList. Call ` +
114
+ `mcp__plugin_ashlr_ashlr__ashlr__task_list instead — it filters by status/owner, ` +
115
+ `limits to 30 rows by default, and returns a compact column view (taskId, status, ` +
116
+ `subject, ageMin) saving 50-80% tokens on large task lists. ` +
117
+ `Equivalent call: { "status": "${safeStatus}" }. ` +
118
+ escapeValves;
119
+ } else {
120
+ const safeTaskId = taskId.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
121
+ reason =
122
+ `[ashlr] Blocking the built-in TaskGet. Call ` +
123
+ `mcp__plugin_ashlr_ashlr__ashlr__task_get instead — it snipCompacts descriptions ` +
124
+ `> 2KB (head + tail with elision marker) to save tokens on tasks with long bodies. ` +
125
+ `Equivalent call: { "taskId": "${safeTaskId}" }. ` +
126
+ escapeValves;
127
+ }
128
+
129
+ process.stdout.write(JSON.stringify(buildRedirectBlock(reason)));
130
+ await exit(0, "block", tool);
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-webfetch.ts — PreToolUse hook for the WebFetch tool.
4
+ *
5
+ * WebFetch has no ashlr__* equivalent. This hook provides audit-trail
6
+ * coverage so every WebFetch call appears in hook-timings.jsonl. In nudge
7
+ * mode it reminds the agent that ashlr__webfetch compresses large HTML
8
+ * responses, returning only the main content with the boilerplate stripped.
9
+ *
10
+ * Never blocks in any mode — WebFetch is a read-only network tool and the
11
+ * agent must always be able to fetch documentation or API responses.
12
+ */
13
+
14
+ import {
15
+ buildPassThrough,
16
+ flushHookTimings,
17
+ getHookModeFor,
18
+ parsePayload,
19
+ readStdin,
20
+ recordHookTiming,
21
+ } from "./pretooluse-common";
22
+ import { installHookTimeout } from "./pretooluse-common";
23
+
24
+ if (import.meta.main) installHookTimeout("pretooluse-webfetch");
25
+
26
+ const hookStartedAt = Date.now();
27
+
28
+ async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
29
+ recordHookTiming({ hook: "pretooluse-webfetch", tool, durationMs: Date.now() - hookStartedAt, outcome });
30
+ await flushHookTimings();
31
+ process.exit(code);
32
+ }
33
+
34
+ const raw = await readStdin();
35
+ const payload = parsePayload(raw);
36
+ if (!payload) await exit(0, "ok");
37
+
38
+ const tool = payload!.tool_name || undefined;
39
+ if (payload!.tool_name !== "WebFetch") await exit(0, "ok", tool);
40
+
41
+ const mode = getHookModeFor("webfetch");
42
+ if (mode === "nudge") {
43
+ let url = "<url>";
44
+ try {
45
+ const parsed = JSON.parse(raw) as { tool_input?: { url?: string } };
46
+ if (typeof parsed?.tool_input?.url === "string") url = parsed.tool_input.url;
47
+ } catch {
48
+ /* ignore */
49
+ }
50
+ process.stdout.write(
51
+ JSON.stringify({
52
+ hookSpecificOutput: {
53
+ hookEventName: "PreToolUse",
54
+ additionalContext:
55
+ `[ashlr] Prefer the MCP tool \`ashlr__webfetch\` over the built-in WebFetch. ` +
56
+ `It strips HTML boilerplate and returns only the main content, saving tokens ` +
57
+ `on documentation pages and API references. ` +
58
+ `Call it with { "url": ${JSON.stringify(url)} }.`,
59
+ },
60
+ }),
61
+ );
62
+ await exit(0, "ok", tool);
63
+ }
64
+
65
+ process.stdout.write(JSON.stringify(buildPassThrough()));
66
+ await exit(0, "ok", tool);