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,127 @@
1
+ /**
2
+ * _nudge-throttle.ts — Per-tool nudge throttle for posttooluse-native-nudge.
3
+ *
4
+ * Contract: at most 1 missed-save nudge per minute (across all tools) and
5
+ * at most 1 repeat-offender escalation per minute. State is persisted to
6
+ * ~/.ashlr/nudge-throttle.json so the limit crosses hook subprocess
7
+ * boundaries (each PostToolUse invocation is a fresh process).
8
+ *
9
+ * Design:
10
+ * - Best-effort only. Any I/O error → silent continue (fail open).
11
+ * - Kill switch: ASHLR_SESSION_LOG=0 disables nudges.
12
+ * - File: ~/.ashlr/nudge-throttle.json — written synchronously so it
13
+ * completes before process.exit().
14
+ */
15
+
16
+ import {
17
+ existsSync,
18
+ mkdirSync,
19
+ readFileSync,
20
+ writeFileSync,
21
+ } from "fs";
22
+ import { homedir } from "os";
23
+ import { dirname, join } from "path";
24
+
25
+ export const THROTTLE_MS = 60_000; // 1 nudge per minute
26
+ export const REPEAT_WINDOW_MS = 10 * 60_000; // 10-minute repeat-offender window
27
+ export const REPEAT_THRESHOLD = 3; // calls before escalation
28
+
29
+ export interface ThrottleState {
30
+ /** Epoch ms of the last nudge emitted (any tool). */
31
+ lastNudgeAt: number;
32
+ /** Epoch ms of the last repeat-offender escalation. */
33
+ lastEscalationAt: number;
34
+ /**
35
+ * Per-tool call timestamps within the repeat window.
36
+ * Only tracks native calls that COULD have been redirected.
37
+ */
38
+ recentCalls: Record<string, number[]>; // toolName → array of epoch ms
39
+ }
40
+
41
+ function throttlePath(home: string = process.env.HOME ?? homedir()): string {
42
+ return join(home, ".ashlr", "nudge-throttle.json");
43
+ }
44
+
45
+ function readState(home?: string): ThrottleState {
46
+ try {
47
+ const p = throttlePath(home);
48
+ if (!existsSync(p)) {
49
+ return { lastNudgeAt: 0, lastEscalationAt: 0, recentCalls: {} };
50
+ }
51
+ const raw = JSON.parse(readFileSync(p, "utf-8")) as Partial<ThrottleState>;
52
+ return {
53
+ lastNudgeAt: typeof raw.lastNudgeAt === "number" ? raw.lastNudgeAt : 0,
54
+ lastEscalationAt: typeof raw.lastEscalationAt === "number" ? raw.lastEscalationAt : 0,
55
+ recentCalls: (raw.recentCalls && typeof raw.recentCalls === "object")
56
+ ? raw.recentCalls as Record<string, number[]>
57
+ : {},
58
+ };
59
+ } catch {
60
+ return { lastNudgeAt: 0, lastEscalationAt: 0, recentCalls: {} };
61
+ }
62
+ }
63
+
64
+ function writeState(state: ThrottleState, home?: string): void {
65
+ try {
66
+ const p = throttlePath(home);
67
+ mkdirSync(dirname(p), { recursive: true });
68
+ writeFileSync(p, JSON.stringify(state), "utf-8");
69
+ } catch {
70
+ // Best-effort.
71
+ }
72
+ }
73
+
74
+ export interface NudgeDecision {
75
+ /** Whether to emit a basic missed-save nudge. */
76
+ emitNudge: boolean;
77
+ /** Whether to escalate to a repeat-offender message instead. */
78
+ emitEscalation: boolean;
79
+ /** How many recent native calls exist for this tool (for escalation message). */
80
+ recentCallCount: number;
81
+ }
82
+
83
+ /**
84
+ * Record a native tool call and decide whether to emit a nudge.
85
+ *
86
+ * @param toolName - e.g. "Read", "Grep", "Edit"
87
+ * @param now - injectable for testing (default: Date.now())
88
+ * @param home - injectable for testing
89
+ */
90
+ export function recordNativeCall(
91
+ toolName: string,
92
+ now: number = Date.now(),
93
+ home?: string,
94
+ ): NudgeDecision {
95
+ if (process.env.ASHLR_SESSION_LOG === "0") {
96
+ return { emitNudge: false, emitEscalation: false, recentCallCount: 0 };
97
+ }
98
+
99
+ const state = readState(home);
100
+
101
+ // Prune old entries from recentCalls for this tool.
102
+ const cutoff = now - REPEAT_WINDOW_MS;
103
+ const prev = (state.recentCalls[toolName] ?? []).filter((t) => t >= cutoff);
104
+ prev.push(now);
105
+ state.recentCalls[toolName] = prev;
106
+
107
+ const recentCallCount = prev.length;
108
+ const timeSinceLastNudge = now - state.lastNudgeAt;
109
+ const timeSinceLastEscalation = now - state.lastEscalationAt;
110
+
111
+ let emitNudge = false;
112
+ let emitEscalation = false;
113
+
114
+ if (recentCallCount >= REPEAT_THRESHOLD && timeSinceLastEscalation >= THROTTLE_MS) {
115
+ // Repeat offender — escalate.
116
+ emitEscalation = true;
117
+ state.lastEscalationAt = now;
118
+ state.lastNudgeAt = now; // also resets nudge throttle
119
+ } else if (timeSinceLastNudge >= THROTTLE_MS) {
120
+ // Simple missed-save nudge.
121
+ emitNudge = true;
122
+ state.lastNudgeAt = now;
123
+ }
124
+
125
+ writeState(state, home);
126
+ return { emitNudge, emitEscalation, recentCallCount };
127
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * _recent-blocks.ts — Lightweight append log for PreToolUse redirect events.
3
+ *
4
+ * When a pretooluse-*.ts hook blocks a native tool call (redirect mode), it
5
+ * appends a small record here so the posttooluse-correlate.ts hook can match
6
+ * a subsequent ashlr__* tool call and emit `tool_called_after_block`.
7
+ *
8
+ * Design constraints:
9
+ * - Best-effort only. Failure → silent continue. Never block the hook path.
10
+ * - File: ~/.ashlr/recent-blocks.jsonl. Capped at ≤200 entries.
11
+ * - recordBlock() is APPEND-ONLY — appendFileSync is POSIX-atomic for
12
+ * writes smaller than PIPE_BUF (~512B–4KB). Each JSONL line is ≤200B,
13
+ * well inside the threshold, so concurrent hook subprocesses cannot
14
+ * interleave bytes. Last-write-wins data loss is eliminated.
15
+ * Windows note: NT file writes are also atomic for small sequential
16
+ * writes below the page size (4KB). Our lines are ~200B, so the same
17
+ * guarantee holds.
18
+ * - Truncation (MAX_ENTRIES cap) is deferred to readRecentBlocks() — a
19
+ * lazy pruneOldBlocks() that rewrites via atomic rename when the file
20
+ * grows past MAX_ENTRIES * 1.5. No write contention on the hot path.
21
+ * - Synchronous write (hooks are subprocesses; async write + process.exit
22
+ * races unless we use appendFileSync or drain a promise before exit).
23
+ * - Kill switch: ASHLR_SESSION_LOG=0 disables all telemetry including this.
24
+ */
25
+
26
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
27
+ import { homedir } from "os";
28
+ import { dirname, join } from "path";
29
+
30
+ export interface RecentBlock {
31
+ ts: number; // Date.now() — epoch ms for fast comparison
32
+ toolName: string; // native tool that was blocked, e.g. "Grep"
33
+ pattern?: string; // grep pattern / file path hint — for correlation
34
+ filePath?: string; // for Read / Edit blocks
35
+ }
36
+
37
+ const MAX_ENTRIES = 200;
38
+
39
+ function blocksPath(home: string = process.env.HOME ?? homedir()): string {
40
+ return join(home, ".ashlr", "recent-blocks.jsonl");
41
+ }
42
+
43
+ /**
44
+ * Append one block record. Never throws — all errors are swallowed silently.
45
+ * Must be synchronous so it completes before the hook calls process.exit().
46
+ *
47
+ * Race safety: uses appendFileSync which is atomic for writes < PIPE_BUF
48
+ * (~512B–4KB on POSIX; ~4KB on Windows). Our JSONL lines are ≤200B.
49
+ * Concurrent hook subprocesses can all call this simultaneously without
50
+ * losing records — each append is an independent atomic kernel operation.
51
+ */
52
+ export function recordBlock(block: RecentBlock): void {
53
+ if (process.env.ASHLR_SESSION_LOG === "0") return;
54
+ try {
55
+ const path = blocksPath();
56
+ mkdirSync(dirname(path), { recursive: true });
57
+ appendFileSync(path, JSON.stringify(block) + "\n", "utf-8");
58
+ } catch {
59
+ // Silently swallow — telemetry must never break hook critical path.
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Read the recent blocks log, skip malformed lines, and lazily prune if the
65
+ * file has grown past MAX_ENTRIES * 1.5. Returns an empty array on any error.
66
+ * Used by posttooluse-correlate.ts to find matches.
67
+ *
68
+ * Pruning uses an atomic rename (write → .tmp, rename) so concurrent readers
69
+ * never observe a partial file.
70
+ */
71
+ export function readRecentBlocks(home: string = process.env.HOME ?? homedir()): RecentBlock[] {
72
+ try {
73
+ const path = blocksPath(home);
74
+ if (!existsSync(path)) return [];
75
+ const lines = readFileSync(path, "utf-8").split("\n").filter((l) => l.trim());
76
+ const records: RecentBlock[] = [];
77
+ for (const line of lines) {
78
+ try {
79
+ records.push(JSON.parse(line) as RecentBlock);
80
+ } catch {
81
+ // Skip malformed lines — guards against partial writes during crashes.
82
+ }
83
+ }
84
+ // Lazy prune: rewrite via atomic rename when file exceeds 1.5× cap.
85
+ // Caller-side: always slice to last MAX_ENTRIES so the public contract
86
+ // (≤200 blocks) holds even when the file is in the 200–300 sweet spot
87
+ // where lazy rewrite hasn't kicked in yet.
88
+ if (records.length > MAX_ENTRIES * 1.5) {
89
+ const pruned = records.slice(-MAX_ENTRIES);
90
+ _atomicRewrite(path, pruned);
91
+ return pruned;
92
+ }
93
+ return records.length > MAX_ENTRIES ? records.slice(-MAX_ENTRIES) : records;
94
+ } catch {
95
+ return [];
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Remove block records older than `windowMs` ms from the file.
101
+ * Called by posttooluse-correlate.ts after matching so the file stays trim.
102
+ * Best-effort — never throws.
103
+ */
104
+ export function pruneOldBlocks(windowMs: number, home: string = process.env.HOME ?? homedir()): void {
105
+ try {
106
+ const path = blocksPath(home);
107
+ if (!existsSync(path)) return;
108
+ const cutoff = Date.now() - windowMs;
109
+ const records = readRecentBlocks(home).filter((r) => r.ts >= cutoff);
110
+ _atomicRewrite(path, records);
111
+ } catch {
112
+ // Best-effort.
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Write `records` to `path` atomically by writing to a `.tmp` sibling first,
118
+ * then renaming. Rename is atomic on POSIX and atomic on NTFS (same volume).
119
+ */
120
+ function _atomicRewrite(path: string, records: RecentBlock[]): void {
121
+ const tmp = path + ".tmp";
122
+ const content = records.length === 0
123
+ ? ""
124
+ : records.map((r) => JSON.stringify(r)).join("\n") + "\n";
125
+ writeFileSync(tmp, content, "utf-8");
126
+ renameSync(tmp, path);
127
+ }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bun
2
+ // Make this a proper module so top-level await is allowed.
3
+ export {};
4
+
5
+ /**
6
+ * audit-upload.ts — PostToolUse hook.
7
+ *
8
+ * Fires after any non-read tool call. If ASHLR_PRO_TOKEN is set and the user's
9
+ * tier includes audit logging, POSTs the event to /audit/event on the ashlr
10
+ * backend. Fire-and-forget with a 3s timeout — failure never blocks the tool.
11
+ *
12
+ * Registered in hooks.json:
13
+ * PostToolUse matcher: Edit|MultiEdit|Write|Bash|mcp__plugin_ashlr_ashlr__ashlr__edit|mcp__plugin_ashlr_ashlr__ashlr__multi_edit|mcp__plugin_ashlr_ashlr__ashlr__edit_structural
14
+ */
15
+
16
+ const token = process.env["ASHLR_PRO_TOKEN"];
17
+ const baseUrl = process.env["ASHLR_API_URL"] ?? "https://api.ashlr.ai";
18
+
19
+ if (!token) {
20
+ // No pro token — silently exit
21
+ process.exit(0);
22
+ }
23
+
24
+ // Claude Code passes the hook payload on stdin as JSON
25
+ let payload: Record<string, unknown> = {};
26
+ try {
27
+ const raw = await Bun.stdin.text();
28
+ if (raw.trim()) {
29
+ payload = JSON.parse(raw) as Record<string, unknown>;
30
+ }
31
+ } catch (e) {
32
+ // Malformed stdin — exit cleanly, but leave a stderr trace.
33
+ process.stderr.write("[ashlr-audit-upload] stdin parse failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
34
+ process.exit(0);
35
+ }
36
+
37
+ const tool = (payload["tool_name"] as string | undefined) ?? "unknown";
38
+ const toolInput = (payload["tool_input"] as Record<string, unknown> | undefined) ?? {};
39
+ const cwd = (payload["cwd"] as string | undefined) ?? "";
40
+
41
+ // Best-effort git commit from env or cwd
42
+ let gitCommit = process.env["GIT_COMMIT"] ?? "";
43
+ if (!gitCommit && cwd) {
44
+ try {
45
+ const proc = Bun.spawnSync(["git", "-C", cwd, "rev-parse", "--short", "HEAD"], {
46
+ stdout: "pipe",
47
+ stderr: "pipe",
48
+ });
49
+ gitCommit = proc.stdout.toString().trim();
50
+ } catch (e) {
51
+ // Not in a git repo — ignore, but leave a stderr trace.
52
+ process.stderr.write("[ashlr-audit-upload] git rev-parse failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
53
+ }
54
+ }
55
+
56
+ const body = {
57
+ tool,
58
+ args: toolInput,
59
+ userId: "", // server resolves from Bearer token
60
+ cwd,
61
+ gitCommit,
62
+ timestamp: new Date().toISOString(),
63
+ };
64
+
65
+ try {
66
+ const controller = new AbortController();
67
+ const timeout = setTimeout(() => controller.abort(), 3000);
68
+
69
+ await fetch(`${baseUrl}/audit/event`, {
70
+ method: "POST",
71
+ headers: {
72
+ "Content-Type": "application/json",
73
+ Authorization: `Bearer ${token}`,
74
+ },
75
+ body: JSON.stringify(body),
76
+ signal: controller.signal,
77
+ });
78
+
79
+ clearTimeout(timeout);
80
+ } catch (e) {
81
+ // Fire-and-forget — any network or timeout error is silently dropped, but
82
+ // we leave a stderr trace so production failures can be grepped.
83
+ process.stderr.write("[ashlr-audit-upload] POST /audit/event failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
84
+ }
85
+
86
+ process.exit(0);
@@ -0,0 +1,171 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/sessionstart-lean-tools.ts\""
9
+ },
10
+ {
11
+ "type": "command",
12
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/sessionstart-efficient.ts\""
13
+ },
14
+ {
15
+ "type": "command",
16
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/sessionstart-genome-author.ts\""
17
+ }
18
+ ]
19
+ }
20
+ ],
21
+ "UserPromptSubmit": [
22
+ {
23
+ "hooks": [
24
+ {
25
+ "type": "command",
26
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/userpromptsubmit-brief-trigger.ts\""
27
+ }
28
+ ]
29
+ }
30
+ ],
31
+ "PreToolUse": [
32
+ {
33
+ "matcher": "Bash|exec_command|functions.exec_command",
34
+ "hooks": [
35
+ {
36
+ "type": "command",
37
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-bash.ts\""
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "matcher": "apply_patch|functions.apply_patch",
43
+ "hooks": [
44
+ {
45
+ "type": "command",
46
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-edit.ts\""
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ "matcher": "Read",
52
+ "hooks": [
53
+ {
54
+ "type": "command",
55
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-read.ts\""
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "matcher": "Grep",
61
+ "hooks": [
62
+ {
63
+ "type": "command",
64
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-grep.ts\""
65
+ }
66
+ ]
67
+ },
68
+ {
69
+ "matcher": "Glob",
70
+ "hooks": [
71
+ {
72
+ "type": "command",
73
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-glob.ts\""
74
+ }
75
+ ]
76
+ },
77
+ {
78
+ "matcher": "Write",
79
+ "hooks": [
80
+ {
81
+ "type": "command",
82
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-write.ts\""
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ "matcher": "Edit|MultiEdit",
88
+ "hooks": [
89
+ {
90
+ "type": "command",
91
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-edit.ts\""
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "matcher": "mcp__ashlr__ashlr__read|mcp__ashlr__ashlr__grep|mcp__ashlr__ashlr__tree|mcp__ashlr__ashlr__savings",
97
+ "hooks": [
98
+ {
99
+ "type": "command",
100
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/pretooluse-budget-guard.ts\""
101
+ }
102
+ ]
103
+ }
104
+ ],
105
+ "PostToolUse": [
106
+ {
107
+ "matcher": "Bash|exec_command|functions.exec_command|apply_patch|functions.apply_patch|Read|Grep|Glob|Write|Edit|MultiEdit|mcp__ashlr__ashlr__read|mcp__ashlr__ashlr__grep|mcp__ashlr__ashlr__edit|mcp__ashlr__ashlr__multi_edit|mcp__ashlr__ashlr__write",
108
+ "hooks": [
109
+ {
110
+ "type": "command",
111
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/post-tool-use-genome.ts\""
112
+ },
113
+ {
114
+ "type": "command",
115
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/session-log-append.ts\""
116
+ }
117
+ ]
118
+ }
119
+ ],
120
+ "PreCompact": [
121
+ {
122
+ "hooks": [
123
+ {
124
+ "type": "command",
125
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/session-end-consolidate.ts\""
126
+ }
127
+ ]
128
+ }
129
+ ],
130
+ "PostCompact": [
131
+ {
132
+ "hooks": [
133
+ {
134
+ "type": "command",
135
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/sessionstart-lean-tools.ts\""
136
+ }
137
+ ]
138
+ }
139
+ ],
140
+ "SubagentStart": [
141
+ {
142
+ "hooks": [
143
+ {
144
+ "type": "command",
145
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/sessionstart-efficient.ts\""
146
+ }
147
+ ]
148
+ }
149
+ ],
150
+ "SubagentStop": [
151
+ {
152
+ "hooks": [
153
+ {
154
+ "type": "command",
155
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/subagent-stop-rollup.ts\""
156
+ }
157
+ ]
158
+ }
159
+ ],
160
+ "Stop": [
161
+ {
162
+ "hooks": [
163
+ {
164
+ "type": "command",
165
+ "command": "node \"${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs\" \"${CODEX_PLUGIN_ROOT}/hooks/stop-accounting.ts\""
166
+ }
167
+ ]
168
+ }
169
+ ]
170
+ }
171
+ }
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * commit-attribution.ts
4
+ *
5
+ * Claude Code PreToolUse hook (matched on Bash). Reads the tool call JSON
6
+ * from stdin; if the command is a `git commit -m/--message=` invocation,
7
+ * append an `Assisted-By: ashlr-plugin` trailer so commits made through
8
+ * Claude Code are honestly attributed.
9
+ *
10
+ * Pure local — no telemetry, no network. Honors `~/.claude/settings.json`
11
+ * `ashlr.attribution` (default true). Designed to never block a commit:
12
+ * any parsing failure falls through to a pass-through.
13
+ */
14
+
15
+ import { readFileSync } from "fs";
16
+ import { homedir } from "os";
17
+ import { join } from "path";
18
+
19
+ export const TRAILER = "Assisted-By: ashlr-plugin <https://plugin.ashlr.ai>";
20
+
21
+ interface HookInput {
22
+ tool_name?: string;
23
+ tool_input?: { command?: string; [k: string]: unknown };
24
+ [k: string]: unknown;
25
+ }
26
+
27
+ interface HookResult {
28
+ /** The (possibly rewritten) command to run, or null to leave it alone. */
29
+ rewrittenCommand: string | null;
30
+ /** True iff we modified the original command. */
31
+ modified: boolean;
32
+ }
33
+
34
+ export function isAttributionEnabled(settingsPath = join(homedir(), ".claude", "settings.json")): boolean {
35
+ try {
36
+ const raw = readFileSync(settingsPath, "utf8");
37
+ const parsed = JSON.parse(raw);
38
+ const ashlr = parsed?.ashlr;
39
+ if (ashlr && typeof ashlr === "object" && "attribution" in ashlr) {
40
+ return ashlr.attribution !== false;
41
+ }
42
+ return true;
43
+ } catch {
44
+ // Missing file / malformed settings → default ON.
45
+ return true;
46
+ }
47
+ }
48
+
49
+ export function alreadyAttributed(message: string): boolean {
50
+ return /Assisted-By:|Co-Authored-By:/i.test(message);
51
+ }
52
+
53
+ /**
54
+ * Rewrite a single quoted message (preserving the original quote style) by
55
+ * appending the trailer, unless already attributed.
56
+ */
57
+ function appendToMessage(message: string): string {
58
+ if (alreadyAttributed(message)) return message;
59
+ // Two blank lines between body and trailer block per git convention.
60
+ // If the message already ends with one newline, we still want a blank line.
61
+ const sep = message.endsWith("\n\n") ? "" : message.endsWith("\n") ? "\n" : "\n\n";
62
+ return message + sep + TRAILER;
63
+ }
64
+
65
+ /**
66
+ * Find a `-m`/`--message=` argument in a `git commit` command and rewrite
67
+ * the message in-place. Returns the rewritten command, or null if we can't
68
+ * confidently rewrite (in which case the caller should pass through).
69
+ *
70
+ * Handles:
71
+ * git commit -m "msg"
72
+ * git commit -m 'msg'
73
+ * git commit --message="msg"
74
+ * git commit --message='msg'
75
+ *
76
+ * Punts on:
77
+ * git commit (opens editor — nothing to rewrite)
78
+ * git commit -F file (message lives in a file; rewriting would
79
+ * require disk I/O on a user-owned path)
80
+ * git commit -m msg (unquoted — ambiguous tokenization)
81
+ */
82
+ export function rewriteGitCommit(command: string): HookResult {
83
+ const trimmed = command.trim();
84
+
85
+ // Cheap pre-check: must contain `git commit` as a recognizable token.
86
+ // Allow leading env vars / pipes by just searching.
87
+ if (!/\bgit\s+commit\b/.test(trimmed)) {
88
+ return { rewrittenCommand: null, modified: false };
89
+ }
90
+
91
+ // -m "..." / -m '...'
92
+ // Use a lazy match across the whole string so escaped quotes inside
93
+ // double-quoted strings (\") are tolerated.
94
+ const reDouble = /(\bgit\s+commit\b[^]*?)(-m\s+)"((?:\\.|[^"\\])*)"/;
95
+ const reSingle = /(\bgit\s+commit\b[^]*?)(-m\s+)'((?:[^'\\]|\\.)*)'/;
96
+ const reLongDouble = /(\bgit\s+commit\b[^]*?)(--message=)"((?:\\.|[^"\\])*)"/;
97
+ const reLongSingle = /(\bgit\s+commit\b[^]*?)(--message=)'((?:[^'\\]|\\.)*)'/;
98
+
99
+ for (const [re, quote] of [
100
+ [reDouble, '"'],
101
+ [reSingle, "'"],
102
+ [reLongDouble, '"'],
103
+ [reLongSingle, "'"],
104
+ ] as const) {
105
+ const m = trimmed.match(re);
106
+ if (!m) continue;
107
+ const [, prefix, flag, body] = m;
108
+ // Decode escapes for the duplicate check, but re-emit body literally
109
+ // and just append the trailer to the literal body.
110
+ const decoded = quote === '"' ? body.replace(/\\(["\\$`])/g, "$1") : body.replace(/\\(['\\])/g, "$1");
111
+ if (alreadyAttributed(decoded)) {
112
+ return { rewrittenCommand: null, modified: false };
113
+ }
114
+ // Append the trailer inside the quotes. Use literal newlines — both
115
+ // bash double and single quotes preserve them.
116
+ const newBody = body + (body.endsWith("\n\n") ? "" : body.endsWith("\n") ? "\n" : "\n\n") + TRAILER;
117
+ const matched = m[0];
118
+ const replacement = `${prefix}${flag}${quote}${newBody}${quote}`;
119
+ const rewritten = trimmed.replace(matched, replacement);
120
+ return { rewrittenCommand: rewritten, modified: true };
121
+ }
122
+
123
+ // Fell through — bare `git commit`, `-F`, `-m unquoted`, etc. Pass through.
124
+ return { rewrittenCommand: null, modified: false };
125
+ }
126
+
127
+ /** Pass-through hook output: tells Claude Code to run the command unchanged. */
128
+ function passThrough(): string {
129
+ return JSON.stringify({});
130
+ }
131
+
132
+ /**
133
+ * Build the PreToolUse hook output that rewrites the Bash command.
134
+ * Claude Code's PreToolUse hook supports `hookSpecificOutput` with an
135
+ * updated `tool_input` shape; we emit that so the agent runs the rewritten
136
+ * git commit instead of the original.
137
+ */
138
+ function rewriteOutput(newCommand: string): string {
139
+ return JSON.stringify({
140
+ hookSpecificOutput: {
141
+ hookEventName: "PreToolUse",
142
+ updatedInput: { command: newCommand },
143
+ },
144
+ });
145
+ }
146
+
147
+ export function processHookInput(stdin: string, settingsPath?: string): string {
148
+ let input: HookInput;
149
+ try {
150
+ input = JSON.parse(stdin);
151
+ } catch {
152
+ return passThrough();
153
+ }
154
+ if (input?.tool_name !== "Bash") return passThrough();
155
+ const command = input?.tool_input?.command;
156
+ if (typeof command !== "string" || command.length === 0) return passThrough();
157
+
158
+ if (!isAttributionEnabled(settingsPath)) return passThrough();
159
+
160
+ const { rewrittenCommand, modified } = rewriteGitCommit(command);
161
+ if (!modified || !rewrittenCommand) return passThrough();
162
+ return rewriteOutput(rewrittenCommand);
163
+ }
164
+
165
+ // Entry point — only run when executed as a script, not when imported by tests.
166
+ if (import.meta.main) {
167
+ const chunks: Buffer[] = [];
168
+ process.stdin.on("data", (c: Buffer) => chunks.push(c));
169
+ process.stdin.on("end", () => {
170
+ const stdin = Buffer.concat(chunks).toString("utf8");
171
+ process.stdout.write(processHookInput(stdin));
172
+ process.exit(0);
173
+ });
174
+ // If stdin is closed (no data piped), exit pass-through immediately.
175
+ process.stdin.on("error", () => {
176
+ process.stdout.write(passThrough());
177
+ process.exit(0);
178
+ });
179
+ }