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,205 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * posttooluse-native-nudge.ts — PostToolUse hook: missed-save nudge.
4
+ *
5
+ * Fires AFTER a native Read / Grep / Edit / Write / MultiEdit call completes.
6
+ * When the tool was called on a file inside cwd (and not inside the plugin
7
+ * tree) that COULD have been redirected by ashlr but wasn't (either because
8
+ * ASHLR_HOOK_MODE=nudge or because the cwd-clamp let it through), this hook
9
+ * emits a `additionalContext` nudge into the next assistant turn.
10
+ *
11
+ * Two escalation levels:
12
+ * 1. Basic missed-save nudge (first time, throttled to 1/min):
13
+ * [ashlr nudge] You called native Read on big_file.ts (8.2KB).
14
+ * ashlr__read would have returned ~1.6KB. Try
15
+ * mcp__plugin_ashlr_ashlr__ashlr__read next time.
16
+ *
17
+ * 2. Repeat-offender escalation (3+ calls of same tool in 10 min):
18
+ * [ashlr nudge] You've called native Grep 3 times in the last
19
+ * 10 minutes. ashlr__grep would have saved ~80% on each. Set
20
+ * ASHLR_HOOK_MODE=redirect to enforce the swap automatically:
21
+ * `bun ${CLAUDE_PLUGIN_ROOT}/scripts/set-hook-mode.ts redirect`.
22
+ *
23
+ * Constraints:
24
+ * - Only fires in ASHLR_HOOK_MODE=nudge. In redirect mode the PreToolUse
25
+ * hook already blocked and explained — double-talk is annoying.
26
+ * - Only fires for in-cwd, non-plugin-tree files (mirrors PreToolUse guard).
27
+ * - Throttled to ≤1 nudge per 60 seconds (see _nudge-throttle.ts).
28
+ * - Kill switch: ASHLR_SESSION_LOG=0.
29
+ * - Best-effort: any error → exit 0, no nudge.
30
+ *
31
+ * PostToolUse hook contract:
32
+ * stdin → JSON { tool_name, tool_input: { ... }, tool_response: { ... } }
33
+ * stdout → JSON { hookSpecificOutput: { hookEventName: "PostToolUse",
34
+ * additionalContext: "..." } } (nudge)
35
+ * OR empty / "{}" (no nudge)
36
+ * exit 0 always (never block post-tool execution)
37
+ */
38
+
39
+ import { basename } from "path";
40
+ import {
41
+ flushHookTimings,
42
+ fileSize,
43
+ getHookMode,
44
+ isInsideCwd,
45
+ isInsidePluginRoot,
46
+ nudgeVariantIndex,
47
+ parsePayload,
48
+ pluginRootFrom,
49
+ readStdin,
50
+ recordHookTiming,
51
+ } from "./pretooluse-common";
52
+ import { recordNativeCall } from "./_nudge-throttle";
53
+
54
+ const hookStartedAt = Date.now();
55
+
56
+ // Kill switch
57
+ if (process.env.ASHLR_SESSION_LOG === "0") {
58
+ process.stdout.write("{}");
59
+ process.exit(0);
60
+ }
61
+
62
+ async function exit(code: number, outcome: "ok" | "error" = "ok"): Promise<never> {
63
+ recordHookTiming({
64
+ hook: "posttooluse-native-nudge",
65
+ durationMs: Date.now() - hookStartedAt,
66
+ outcome,
67
+ });
68
+ await flushHookTimings();
69
+ process.exit(code);
70
+ }
71
+
72
+ /** No-nudge pass-through — write empty JSON and exit 0. */
73
+ async function passThrough(): Promise<never> {
74
+ process.stdout.write("{}");
75
+ await exit(0);
76
+ // unreachable; satisfies `never` return type
77
+ throw new Error("unreachable");
78
+ }
79
+
80
+ // Tool → ashlr equivalent mapping
81
+ const NATIVE_TO_ASHLR: Record<string, { ashlrTool: string; mcpTool: string; savingsPct: number }> = {
82
+ Read: { ashlrTool: "ashlr__read", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__read", savingsPct: 80 },
83
+ Grep: { ashlrTool: "ashlr__grep", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__grep", savingsPct: 80 },
84
+ Edit: { ashlrTool: "ashlr__edit", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__edit", savingsPct: 60 },
85
+ Write: { ashlrTool: "ashlr__edit", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__edit", savingsPct: 60 },
86
+ MultiEdit: { ashlrTool: "ashlr__multi_edit", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__multi_edit", savingsPct: 60 },
87
+ };
88
+
89
+ const READ_THRESHOLD = 2048; // bytes — mirrors pretooluse-read.ts
90
+
91
+ // Parse stdin
92
+ const raw = await readStdin();
93
+ let payload: Record<string, unknown> = {};
94
+ try {
95
+ payload = JSON.parse(raw) as Record<string, unknown>;
96
+ } catch {
97
+ await passThrough();
98
+ }
99
+
100
+ const toolName = payload.tool_name as string | undefined;
101
+ if (!toolName) await passThrough();
102
+
103
+ const equiv = NATIVE_TO_ASHLR[toolName as string];
104
+ if (!equiv) await passThrough();
105
+
106
+ // Only fire in nudge mode — redirect mode already blocked + explained.
107
+ if (getHookMode() !== "nudge") await passThrough();
108
+
109
+ // Extract file path from tool_input
110
+ const toolInput = (payload.tool_input ?? {}) as Record<string, unknown>;
111
+ const filePath = (toolInput.file_path as string | undefined)
112
+ ?? (toolInput.path as string | undefined)
113
+ ?? "";
114
+
115
+ // For Read: only nudge on files above the threshold (tiny reads have no savings).
116
+ if (toolName === "Read") {
117
+ if (!filePath) await passThrough();
118
+ const size = fileSize(filePath);
119
+ if (size === null || size <= READ_THRESHOLD) await passThrough();
120
+ }
121
+
122
+ // For Grep: always try (no size guard needed).
123
+ // For Edit/Write/MultiEdit: only nudge when filePath is present.
124
+ if ((toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") && !filePath) {
125
+ await passThrough();
126
+ }
127
+
128
+ // Safety: only nudge for in-cwd, non-plugin-tree paths.
129
+ const pluginRoot = pluginRootFrom(import.meta.url);
130
+ if (filePath) {
131
+ if (isInsidePluginRoot(filePath, pluginRoot)) await passThrough();
132
+ if (!isInsideCwd(filePath)) await passThrough();
133
+ }
134
+
135
+ // Throttle check + repeat-offender detection.
136
+ const { emitNudge, emitEscalation, recentCallCount } = recordNativeCall(toolName as string);
137
+
138
+ if (!emitNudge && !emitEscalation) await passThrough();
139
+
140
+ // Build the nudge message.
141
+ let nudgeText: string;
142
+ const pluginRoot2 = process.env.CLAUDE_PLUGIN_ROOT ?? pluginRoot;
143
+
144
+ if (emitEscalation) {
145
+ // Escalation: 3+ same-tool calls in 10 min — give actionable set-hook-mode instruction.
146
+ const escalationVariants = [
147
+ `[ashlr nudge] ${recentCallCount}x native ${toolName} in 10 min — ${equiv.ashlrTool} saves ~${equiv.savingsPct}% each. ` +
148
+ `Enforce: \`bun ${pluginRoot2}/scripts/set-hook-mode.ts redirect\`.`,
149
+ `[ashlr nudge] You've used native ${toolName} ${recentCallCount} times (last 10 min). ` +
150
+ `${equiv.mcpTool} would save ~${equiv.savingsPct}% per call. ` +
151
+ `Auto-redirect: \`bun ${pluginRoot2}/scripts/set-hook-mode.ts redirect\`.`,
152
+ `[ashlr nudge] ${recentCallCount} native ${toolName} calls = missed savings. ` +
153
+ `Set ASHLR_HOOK_MODE=redirect or run \`bun ${pluginRoot2}/scripts/set-hook-mode.ts redirect\` once.`,
154
+ ];
155
+ nudgeText = escalationVariants[nudgeVariantIndex(`${toolName}:${recentCallCount}`, escalationVariants.length)];
156
+ } else {
157
+ // Basic missed-save nudge — include exact size + MCP tool name to call.
158
+ if (toolName === "Read" && filePath) {
159
+ const size = fileSize(filePath);
160
+ const sizeKb = size !== null ? (size / 1024).toFixed(1) + "KB" : "?KB";
161
+ const compactKb = size !== null
162
+ ? ((size * (1 - equiv.savingsPct / 100)) / 1024).toFixed(1) + "KB"
163
+ : "~20%";
164
+ const fname = basename(filePath) || filePath;
165
+ const variants = [
166
+ `[ashlr nudge] ${fname} (${sizeKb}) — ${equiv.mcpTool} returns ~${compactKb} instead. Call it next time.`,
167
+ `[ashlr nudge] Native Read on ${fname} (${sizeKb}) costs ${equiv.savingsPct}% extra tokens. ` +
168
+ `Use ${equiv.mcpTool} { "path": "${filePath}" }.`,
169
+ `[ashlr nudge] ${equiv.ashlrTool} on ${fname}: ${sizeKb} → ~${compactKb}. ` +
170
+ `${equiv.mcpTool} { "path": "${filePath}" }.`,
171
+ ];
172
+ nudgeText = variants[nudgeVariantIndex(filePath, variants.length)];
173
+ } else if (toolName === "Grep") {
174
+ const pattern = (toolInput.pattern as string | undefined) ?? "<pattern>";
175
+ const variants = [
176
+ `[ashlr nudge] ${equiv.mcpTool} saves ~${equiv.savingsPct}% on Grep (genome-aware or compressed rg). ` +
177
+ `{ "pattern": ${JSON.stringify(pattern)} }.`,
178
+ `[ashlr nudge] Native Grep → raw output. ${equiv.ashlrTool} filters to relevant chunks (~${equiv.savingsPct}% smaller). ` +
179
+ `${equiv.mcpTool} { "pattern": ${JSON.stringify(pattern)} }.`,
180
+ `[ashlr nudge] Use ${equiv.mcpTool} instead of Grep — genome routing or truncated ripgrep, ~${equiv.savingsPct}% savings.`,
181
+ ];
182
+ nudgeText = variants[nudgeVariantIndex(pattern, variants.length)];
183
+ } else {
184
+ // Edit / Write / MultiEdit
185
+ const fname = filePath ? (basename(filePath) || filePath) : "<file>";
186
+ const variants = [
187
+ `[ashlr nudge] ${equiv.mcpTool} on ${fname} returns only a diff (~${equiv.savingsPct}% less than native ${toolName}).`,
188
+ `[ashlr nudge] Native ${toolName} echoes the full file; ${equiv.ashlrTool} returns compact diff only. ` +
189
+ `Try ${equiv.mcpTool} { "path": "${filePath}" }.`,
190
+ `[ashlr nudge] ${fname} — ${equiv.ashlrTool} saves ~${equiv.savingsPct}% by returning a diff summary. ` +
191
+ `${equiv.mcpTool} { "path": "${filePath}", "search": ..., "replace": ... }.`,
192
+ ];
193
+ nudgeText = variants[nudgeVariantIndex(filePath || fname, variants.length)];
194
+ }
195
+ }
196
+
197
+ const output = {
198
+ hookSpecificOutput: {
199
+ hookEventName: "PostToolUse",
200
+ additionalContext: nudgeText,
201
+ },
202
+ };
203
+
204
+ process.stdout.write(JSON.stringify(output));
205
+ await exit(0);
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * posttooluse-stale-result.ts — Track ashlr tool results for multi-turn staleness.
4
+ *
5
+ * PostToolUse hook. Records every ashlr__read / ashlr__grep / Read / Grep result
6
+ * into the per-session history JSONL so the freshness tracker can later identify
7
+ * stale accumulation.
8
+ *
9
+ * Also fires the once-per-session adaptive nudge when stale bytes exceed 50 KB.
10
+ *
11
+ * Design:
12
+ * - Best-effort only — never throws, always exits 0.
13
+ * - Kill switch: ASHLR_SESSION_LOG=0 disables everything.
14
+ * - Only tracks read-class tools (Read, Grep, ashlr__read, ashlr__grep).
15
+ * Edit/Write results don't accumulate stale read content.
16
+ *
17
+ * Hook contract (PostToolUse):
18
+ * stdin → { tool_name, tool_input?, tool_result? }
19
+ * stdout → { hookSpecificOutput: { hookEventName: "PostToolUse",
20
+ * additionalContext?: string } }
21
+ */
22
+
23
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
24
+ import { homedir } from "os";
25
+ import { dirname, join } from "path";
26
+
27
+ import {
28
+ recordResult,
29
+ readHistory,
30
+ resolveSessionId,
31
+ staleByteTotal,
32
+ STALE_BYTES_NUDGE_THRESHOLD,
33
+ } from "../servers/_history-tracker";
34
+ import { logPreCompactionNudgeEvent } from "../servers/_telemetry";
35
+ // logMultiTurnStaleEvent removed in v1.30 (1.5) — multi_turn_stale_estimate was never populated.
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Tools to track
39
+ // ---------------------------------------------------------------------------
40
+
41
+ const TRACKED_TOOLS = new Set([
42
+ "Read",
43
+ "Grep",
44
+ "mcp__ashlr-efficiency__ashlr__read",
45
+ "mcp__ashlr-efficiency__ashlr__grep",
46
+ "mcp__plugin_ashlr_ashlr__ashlr__read",
47
+ "mcp__plugin_ashlr_ashlr__ashlr__grep",
48
+ "ashlr__read",
49
+ "ashlr__grep",
50
+ ]);
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Pre-compaction nudge constants
54
+ // ---------------------------------------------------------------------------
55
+
56
+ /** Token estimate heuristic: 4 bytes per token. */
57
+ const BYTES_PER_TOKEN = 4;
58
+
59
+ /** Fire the pre-compaction nudge when estimated tokens exceed this. */
60
+ const PRECOMPACT_TOKEN_THRESHOLD = 65_000;
61
+
62
+ /** Assumed auto-compact ceiling (used for percentage display). */
63
+ const AUTOCOMPACT_TOKEN_CEILING = 80_000;
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Pre-compaction nudge state (once-per-session file flag)
67
+ // ---------------------------------------------------------------------------
68
+
69
+ function precompactNudgePath(homeDir: string, sessionId: string): string {
70
+ return join(homeDir, ".ashlr", "precompact-fired", sessionId);
71
+ }
72
+
73
+ function hasPrecompactNudgeFired(sessionId: string, homeDir: string): boolean {
74
+ try {
75
+ return existsSync(precompactNudgePath(homeDir, sessionId));
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ function markPrecompactNudgeFired(sessionId: string, homeDir: string): void {
82
+ try {
83
+ const p = precompactNudgePath(homeDir, sessionId);
84
+ mkdirSync(dirname(p), { recursive: true });
85
+ writeFileSync(p, String(Date.now()), "utf-8");
86
+ } catch {
87
+ // Best-effort.
88
+ }
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Stale nudge state
93
+ // ---------------------------------------------------------------------------
94
+
95
+ function staleNudgePath(homeDir: string): string {
96
+ return join(homeDir, ".ashlr", "stale-nudge-state.json");
97
+ }
98
+
99
+ interface StaleNudgeState {
100
+ sessionId: string;
101
+ firedAt: number;
102
+ }
103
+
104
+ function hasNudgeFiredThisSession(sessionId: string, homeDir: string): boolean {
105
+ try {
106
+ const p = staleNudgePath(homeDir);
107
+ if (!existsSync(p)) return false;
108
+ const raw = JSON.parse(readFileSync(p, "utf-8")) as Partial<StaleNudgeState>;
109
+ return raw.sessionId === sessionId;
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+
115
+ function markNudgeFired(sessionId: string, homeDir: string, now: number): void {
116
+ try {
117
+ const p = staleNudgePath(homeDir);
118
+ mkdirSync(dirname(p), { recursive: true });
119
+ const state: StaleNudgeState = { sessionId, firedAt: now };
120
+ writeFileSync(p, JSON.stringify(state), "utf-8");
121
+ } catch {
122
+ // Best-effort.
123
+ }
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Result content extraction
128
+ // ---------------------------------------------------------------------------
129
+
130
+ function extractContent(payload: Record<string, unknown>): string {
131
+ const result = payload.tool_result ?? payload.tool_response ?? payload.tool_output;
132
+ if (result == null) return "";
133
+ if (typeof result === "string") return result;
134
+ try { return JSON.stringify(result); } catch { return ""; }
135
+ }
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Hook output type
139
+ // ---------------------------------------------------------------------------
140
+
141
+ interface HookOutput {
142
+ hookSpecificOutput: {
143
+ hookEventName: "PostToolUse";
144
+ additionalContext?: string;
145
+ };
146
+ }
147
+
148
+ function passThrough(): HookOutput {
149
+ return { hookSpecificOutput: { hookEventName: "PostToolUse" } };
150
+ }
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // Main decision function (exported for tests)
154
+ // ---------------------------------------------------------------------------
155
+
156
+ export interface ProcessOpts {
157
+ home?: string;
158
+ sessionId?: string;
159
+ now?: number;
160
+ }
161
+
162
+ export function decide(
163
+ payload: Record<string, unknown>,
164
+ opts: ProcessOpts = {},
165
+ ): HookOutput {
166
+ if (process.env.ASHLR_SESSION_LOG === "0") return passThrough();
167
+
168
+ const toolName = payload?.tool_name;
169
+ if (typeof toolName !== "string" || !TRACKED_TOOLS.has(toolName)) {
170
+ return passThrough();
171
+ }
172
+
173
+ const homeDir =
174
+ opts.home ??
175
+ (process.env.ASHLR_HOME_OVERRIDE?.trim() || process.env.HOME || homedir());
176
+ const sessionId = opts.sessionId ?? resolveSessionId();
177
+ const now = opts.now ?? Date.now();
178
+
179
+ // Record this result in the history JSONL.
180
+ const content = extractContent(payload);
181
+ recordResult(toolName, content, sessionId, homeDir, now);
182
+
183
+ // -------------------------------------------------------------------------
184
+ // 2B — Pre-compaction nudge: warn once when cumulative bytes approach
185
+ // the auto-compact ceiling (~80K tokens).
186
+ // -------------------------------------------------------------------------
187
+ if (!hasPrecompactNudgeFired(sessionId, homeDir)) {
188
+ try {
189
+ const allEntries = readHistory(sessionId, homeDir);
190
+ const cumulativeBytes = allEntries.reduce((acc, e) => acc + e.sizeBytes, 0);
191
+ const estimatedTokens = Math.floor(cumulativeBytes / BYTES_PER_TOKEN);
192
+ if (estimatedTokens > PRECOMPACT_TOKEN_THRESHOLD) {
193
+ const pct = Math.min(100, Math.round((estimatedTokens / AUTOCOMPACT_TOKEN_CEILING) * 100));
194
+ markPrecompactNudgeFired(sessionId, homeDir);
195
+ try {
196
+ logPreCompactionNudgeEvent({ cumulativeBytes, estimatedTokens, pct });
197
+ } catch {
198
+ // Telemetry never blocks.
199
+ }
200
+ const precompactNudge =
201
+ `[ashlr] context approaching auto-compact (~${pct}% of 80K). ` +
202
+ `Run /ashlr-compact now to dedupe stale results before lag hits.`;
203
+ return {
204
+ hookSpecificOutput: {
205
+ hookEventName: "PostToolUse",
206
+ additionalContext: precompactNudge,
207
+ },
208
+ };
209
+ }
210
+ } catch {
211
+ // Best-effort — never block tool call.
212
+ }
213
+ }
214
+
215
+ // -------------------------------------------------------------------------
216
+ // Stale-result nudge (original behaviour)
217
+ // -------------------------------------------------------------------------
218
+
219
+ // Check if stale bytes exceed nudge threshold.
220
+ if (hasNudgeFiredThisSession(sessionId, homeDir)) return passThrough();
221
+
222
+ const { staleBytes, staleResults, sessionTurnCount } = staleByteTotal(sessionId, homeDir);
223
+
224
+ if (staleBytes < STALE_BYTES_NUDGE_THRESHOLD) return passThrough();
225
+
226
+ // Fire the once-per-session adaptive nudge.
227
+ markNudgeFired(sessionId, homeDir, now);
228
+
229
+ const staleKb = Math.round(staleBytes / 1024);
230
+ const nudge =
231
+ `[ashlr] ~${staleKb} KB of stale tool output is accumulating in this session ` +
232
+ `(${staleResults} result${staleResults === 1 ? "" : "s"} from 5+ turns ago). ` +
233
+ `Run \`/ashlr-compact\` to surface a recompression plan.`;
234
+
235
+ return {
236
+ hookSpecificOutput: {
237
+ hookEventName: "PostToolUse",
238
+ additionalContext: nudge,
239
+ },
240
+ };
241
+ }
242
+
243
+ // ---------------------------------------------------------------------------
244
+ // Stdin reading
245
+ // ---------------------------------------------------------------------------
246
+
247
+ async function readStdin(): Promise<string> {
248
+ const chunks: Buffer[] = [];
249
+ for await (const chunk of process.stdin as AsyncIterable<Buffer | string>) {
250
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
251
+ }
252
+ return Buffer.concat(chunks).toString("utf-8");
253
+ }
254
+
255
+ // ---------------------------------------------------------------------------
256
+ // Entry point
257
+ // ---------------------------------------------------------------------------
258
+
259
+ async function main(): Promise<void> {
260
+ let payload: Record<string, unknown> = {};
261
+ try {
262
+ const raw = await readStdin();
263
+ if (raw.trim()) payload = JSON.parse(raw) as Record<string, unknown>;
264
+ } catch {
265
+ process.stdout.write(JSON.stringify(passThrough()));
266
+ return;
267
+ }
268
+ try {
269
+ process.stdout.write(JSON.stringify(decide(payload)));
270
+ } catch {
271
+ process.stdout.write(JSON.stringify(passThrough()));
272
+ }
273
+ }
274
+
275
+ if (import.meta.main) {
276
+ await main();
277
+ }
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * pretooluse-bash.ts — Nudges the agent toward ashlr__bash for shell commands
4
+ * that produce verbose output.
5
+ *
6
+ * Unlike pretooluse-{read,grep,edit}, this hook NEVER blocks regardless of
7
+ * ASHLR_HOOK_MODE. Bash has no 1:1 equivalent for arbitrary commands —
8
+ * forcing every shell call through MCP would strand the agent on commands
9
+ * that aren't in the summarizer registry, and changes how output renders to
10
+ * the user (MCP tool result vs. inline shell output). Nudge-only keeps the
11
+ * UX intact while letting the model voluntarily pick ashlr__bash for the
12
+ * commands where compression actually pays off.
13
+ *
14
+ * The nudge fires only when `findSummarizer(command)` returns non-null OR
15
+ * `isLargeDiffCommand(command)` returns true — i.e., commands where
16
+ * ashlr__bash would actually run a structured summarizer or LLM-compress the
17
+ * stdout. Quiet commands (echo, pwd, mv, rm) pass through silently.
18
+ *
19
+ * `ASHLR_HOOK_MODE=off` (or the legacy `~/.ashlr/settings.json
20
+ * { toolRedirect: false }` killswitch) disables the nudge entirely.
21
+ */
22
+
23
+ import {
24
+ buildNudgeContext,
25
+ buildPassThrough,
26
+ flushHookTimings,
27
+ getHookModeFor,
28
+ parsePayload,
29
+ readStdin,
30
+ recordHookTiming,
31
+ } from "./pretooluse-common";
32
+ import { installHookTimeout } from "./pretooluse-common";
33
+
34
+ if (import.meta.main) installHookTimeout("pretooluse-bash");
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-bash", 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
+ const handledTools = new Set(["Bash", "exec_command", "functions.exec_command"]);
50
+ if (!handledTools.has(payload!.tool_name)) await exit(0, "ok", tool);
51
+ if (!payload!.command) await exit(0, "ok", tool);
52
+
53
+ const mode = getHookModeFor("bash");
54
+ if (mode === "off") {
55
+ process.stdout.write(JSON.stringify(buildPassThrough()));
56
+ await exit(0, "ok", tool);
57
+ }
58
+
59
+ // buildNudgeContext("Bash", ...) returns null when the command isn't in the
60
+ // summarizer registry — passing through silently in that case is intentional.
61
+ const nudge = buildNudgeContext("Bash", { command: payload!.command });
62
+ process.stdout.write(JSON.stringify(nudge ?? buildPassThrough()));
63
+ await exit(0, "ok", tool);