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,438 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * ashlr handoff-pack — generates a compact context primer so the next
4
+ * session can resume cold without re-exploring.
5
+ *
6
+ * Writes to .ashlr/handoffs/YYYY-MM-DD-HHMMSS-<rand>.md
7
+ *
8
+ * CLI flags:
9
+ * --session <id> override which session to pack (default: current)
10
+ * --last re-print the most recent handoff (no new write)
11
+ * --dir <path> override output directory
12
+ * --days N how many days of log to scan (default: 7)
13
+ *
14
+ * Exported surface:
15
+ * buildHandoffPack(opts?) -> { path: string; content: string }, never throws.
16
+ */
17
+
18
+ import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync } from "fs";
19
+ import { homedir } from "os";
20
+ import { join, basename, relative } from "path";
21
+ import { randomBytes } from "crypto";
22
+ import { currentSessionId, readCurrentSession, readStats } from "../servers/_stats.ts";
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Schema (matches session-log-report.ts)
26
+ // ---------------------------------------------------------------------------
27
+
28
+ interface LogRecord {
29
+ ts: string;
30
+ agent: string;
31
+ event: string;
32
+ tool: string;
33
+ cwd: string;
34
+ session: string;
35
+ input_size: number;
36
+ output_size: number;
37
+ calls?: number;
38
+ tokens_saved?: number;
39
+ started_at?: string;
40
+ reason?: string;
41
+ // TodoWrite input payload (serialized JSON string or object)
42
+ input?: unknown;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Options
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export interface BuildHandoffPackOpts {
50
+ /** Override home dir — used by tests. */
51
+ home?: string;
52
+ /** Override session id to pack. Default: currentSessionId(). */
53
+ sessionId?: string;
54
+ /** Override output directory. Default: <home>/.ashlr/handoffs/ */
55
+ outDir?: string;
56
+ /** Injected now in ms — tests pin this. */
57
+ now?: number;
58
+ /** Max lines per file. */
59
+ limitLines?: number;
60
+ /** Random suffix override — lets tests force collision-safe filenames. */
61
+ randSuffix?: string;
62
+ /** Skip writing to disk and return content only (for tests). */
63
+ dryRun?: boolean;
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // JSONL helpers
68
+ // ---------------------------------------------------------------------------
69
+
70
+ function readLines(path: string, limit?: number): string[] {
71
+ try {
72
+ if (!existsSync(path)) return [];
73
+ const raw = readFileSync(path, "utf-8");
74
+ const lines = raw.split("\n").filter((l) => l.trim().length > 0);
75
+ return limit ? lines.slice(0, limit) : lines;
76
+ } catch {
77
+ return [];
78
+ }
79
+ }
80
+
81
+ function parseRecords(lines: string[]): LogRecord[] {
82
+ const out: LogRecord[] = [];
83
+ for (const line of lines) {
84
+ try {
85
+ const r = JSON.parse(line) as Partial<LogRecord>;
86
+ if (typeof r.ts !== "string" || typeof r.event !== "string") continue;
87
+ out.push({
88
+ ts: r.ts,
89
+ agent: r.agent ?? "unknown",
90
+ event: r.event,
91
+ tool: r.tool ?? "unknown",
92
+ cwd: r.cwd ?? "",
93
+ session: r.session ?? "",
94
+ input_size: typeof r.input_size === "number" ? r.input_size : 0,
95
+ output_size: typeof r.output_size === "number" ? r.output_size : 0,
96
+ calls: r.calls,
97
+ tokens_saved: r.tokens_saved,
98
+ started_at: r.started_at,
99
+ reason: r.reason,
100
+ input: r.input,
101
+ });
102
+ } catch {
103
+ // skip malformed
104
+ }
105
+ }
106
+ return out;
107
+ }
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // Timestamp filename helper
111
+ // ---------------------------------------------------------------------------
112
+
113
+ function makeFilename(now: number, randSuffix?: string): string {
114
+ const d = new Date(now);
115
+ const pad = (n: number, w = 2) => String(n).padStart(w, "0");
116
+ const date =
117
+ `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
118
+ const time =
119
+ `${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}`;
120
+ const suffix = randSuffix ?? randomBytes(3).toString("hex");
121
+ return `${date}-${time}-${suffix}.md`;
122
+ }
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Section builders
126
+ // ---------------------------------------------------------------------------
127
+
128
+ function fmtTokens(n: number): string {
129
+ if (n < 1000) return `${n}`;
130
+ return `${(n / 1000).toFixed(1)}K`;
131
+ }
132
+
133
+ async function buildSessionSummarySection(
134
+ sessionId: string,
135
+ statsHome?: string,
136
+ ): Promise<string> {
137
+ try {
138
+ // readCurrentSession reads from the real stats.json path — we can't
139
+ // easily redirect it in tests. For test isolation, callers use dryRun +
140
+ // a synthetic sessionId that won't be found, so we gracefully degrade.
141
+ const session = await readCurrentSession(sessionId);
142
+ const lines: string[] = ["## Session Summary", ""];
143
+
144
+ lines.push(`- Session ID: ${sessionId}`);
145
+ lines.push(`- Started at: ${session.startedAt}`);
146
+ lines.push(`- Calls: ${session.calls}`);
147
+ lines.push(`- Tokens saved: ${fmtTokens(session.tokensSaved)}`);
148
+
149
+ // Dominant tools — top 3 by calls
150
+ const toolEntries = Object.entries(session.byTool)
151
+ .sort((a, b) => b[1].calls - a[1].calls)
152
+ .slice(0, 3);
153
+
154
+ if (toolEntries.length > 0) {
155
+ lines.push(`- Dominant tools:`);
156
+ for (const [tool, stat] of toolEntries) {
157
+ lines.push(` - ${tool}: ${stat.calls} calls, ${fmtTokens(stat.tokensSaved)} tok saved`);
158
+ }
159
+ } else {
160
+ lines.push(`- Dominant tools: (none recorded)`);
161
+ }
162
+
163
+ return lines.join("\n");
164
+ } catch {
165
+ return [
166
+ "## Session Summary",
167
+ "",
168
+ `- Session ID: ${sessionId}`,
169
+ "- Stats unavailable (no stats.json or session not found).",
170
+ ].join("\n");
171
+ }
172
+ }
173
+
174
+ const READ_TOOLS = new Set([
175
+ "Read", "Edit", "MultiEdit", "ashlr__read", "ashlr__edit", "ashlr__multi_edit",
176
+ ]);
177
+
178
+ function buildRecentFilesSection(
179
+ records: LogRecord[],
180
+ sessionId: string,
181
+ cwd: string,
182
+ ): string {
183
+ const sessionRecords = sessionId
184
+ ? records.filter((r) => r.session === sessionId)
185
+ : records;
186
+
187
+ const pathCounts = new Map<string, number>();
188
+
189
+ for (const r of sessionRecords) {
190
+ if (!READ_TOOLS.has(r.tool)) continue;
191
+ // We don't have the file path in the log. Use cwd as a coarse proxy, but
192
+ // more usefully track input_size as a stand-in identifier. Since the path
193
+ // isn't logged, we emit the cwd + tool combination as context.
194
+ // Best we can do: emit unique (cwd, tool) pairs.
195
+ const key = r.cwd || cwd;
196
+ pathCounts.set(key, (pathCounts.get(key) ?? 0) + 1);
197
+ }
198
+
199
+ const lines: string[] = ["## Recent Files Touched", ""];
200
+
201
+ if (pathCounts.size === 0) {
202
+ lines.push("(no file operations recorded for this session)");
203
+ return lines.join("\n");
204
+ }
205
+
206
+ const sorted = [...pathCounts.entries()]
207
+ .sort((a, b) => b[1] - a[1])
208
+ .slice(0, 10);
209
+
210
+ for (const [p, count] of sorted) {
211
+ // Show last two path components to keep lines short but identifiable.
212
+ // Split on both separators so Windows cwds (C:\foo\bar) render correctly.
213
+ const parts = p.replace(/[\\/]+$/, "").split(/[\\/]/).filter(Boolean);
214
+ const display = parts.length >= 2
215
+ ? parts.slice(-2).join("/")
216
+ : parts[0] ?? p;
217
+ lines.push(`- ${display} (${count} ops)`);
218
+ }
219
+
220
+ return lines.join("\n");
221
+ }
222
+
223
+ function buildGenomeSection(cwd: string): string {
224
+ const lines: string[] = ["## Genome Status", ""];
225
+
226
+ if (!cwd) {
227
+ lines.push("(no working directory)");
228
+ return lines.join("\n");
229
+ }
230
+
231
+ const manifestPath = join(cwd, ".ashlrcode", "genome", "manifest.json");
232
+ if (!existsSync(manifestPath)) {
233
+ lines.push("No genome found. Run /ashlr-genome-init to initialize.");
234
+ return lines.join("\n");
235
+ }
236
+
237
+ try {
238
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")) as {
239
+ sections?: unknown[];
240
+ updatedAt?: string;
241
+ [k: string]: unknown;
242
+ };
243
+ const sectionCount = Array.isArray(manifest.sections) ? manifest.sections.length : "?";
244
+ const updatedAt = manifest.updatedAt ?? "unknown";
245
+ lines.push(`- Sections: ${sectionCount}`);
246
+ lines.push(`- Last updated: ${updatedAt}`);
247
+ } catch {
248
+ lines.push("Manifest exists but could not be parsed.");
249
+ }
250
+
251
+ return lines.join("\n");
252
+ }
253
+
254
+ function buildOpenTodosSection(records: LogRecord[], sessionId: string): string {
255
+ const lines: string[] = ["## Open Todos", ""];
256
+
257
+ const todoRecords = records.filter(
258
+ (r) =>
259
+ r.tool === "TodoWrite" &&
260
+ (sessionId ? r.session === sessionId : true),
261
+ );
262
+
263
+ if (todoRecords.length === 0) {
264
+ lines.push("(no TodoWrite calls in this session)");
265
+ return lines.join("\n");
266
+ }
267
+
268
+ // Use the latest TodoWrite record's input
269
+ const latest = todoRecords[todoRecords.length - 1]!;
270
+ const input = latest.input;
271
+
272
+ if (!input) {
273
+ lines.push("(TodoWrite called but input not recorded)");
274
+ return lines.join("\n");
275
+ }
276
+
277
+ try {
278
+ // input may be a JSON string or already an object
279
+ const parsed: unknown =
280
+ typeof input === "string" ? JSON.parse(input) : input;
281
+
282
+ // TodoWrite input shape: { todos: Array<{ content, status, priority, id }> }
283
+ const todos =
284
+ parsed !== null &&
285
+ typeof parsed === "object" &&
286
+ "todos" in (parsed as object) &&
287
+ Array.isArray((parsed as { todos: unknown }).todos)
288
+ ? (parsed as { todos: Array<{ content?: string; status?: string; priority?: string }> }).todos
289
+ : Array.isArray(parsed)
290
+ ? (parsed as Array<{ content?: string; status?: string; priority?: string }>)
291
+ : null;
292
+
293
+ if (!todos) {
294
+ lines.push("(could not parse todo list)");
295
+ return lines.join("\n");
296
+ }
297
+
298
+ for (const todo of todos) {
299
+ const status = todo.status ?? "pending";
300
+ const content = todo.content ?? "(no content)";
301
+ const priority = todo.priority ? ` [${todo.priority}]` : "";
302
+ lines.push(`- [${status}]${priority} ${content}`);
303
+ }
304
+ } catch {
305
+ lines.push("(could not parse TodoWrite input)");
306
+ }
307
+
308
+ return lines.join("\n");
309
+ }
310
+
311
+ // ---------------------------------------------------------------------------
312
+ // Public API
313
+ // ---------------------------------------------------------------------------
314
+
315
+ export interface HandoffResult {
316
+ path: string;
317
+ content: string;
318
+ /** True when --last was used and no prior handoffs exist. */
319
+ empty?: boolean;
320
+ }
321
+
322
+ export async function buildHandoffPack(
323
+ opts: BuildHandoffPackOpts = {},
324
+ ): Promise<HandoffResult> {
325
+ const homeDir = opts.home ?? homedir();
326
+ const now = opts.now ?? Date.now();
327
+ const outDir = opts.outDir ?? join(homeDir, ".ashlr", "handoffs");
328
+ const sessionId = opts.sessionId ?? currentSessionId();
329
+
330
+ const logPath = join(homeDir, ".ashlr", "session-log.jsonl");
331
+ const rotatedPath = join(homeDir, ".ashlr", "session-log.jsonl.1");
332
+
333
+ const lines1 = readLines(rotatedPath, opts.limitLines);
334
+ const lines2 = readLines(logPath, opts.limitLines);
335
+ const allRecords = parseRecords([...lines1, ...lines2]);
336
+
337
+ // Best-guess cwd: most common cwd in session records
338
+ const sessionRecords = allRecords.filter((r) => r.session === sessionId);
339
+ const cwdCounts = new Map<string, number>();
340
+ for (const r of sessionRecords) {
341
+ if (r.cwd) cwdCounts.set(r.cwd, (cwdCounts.get(r.cwd) ?? 0) + 1);
342
+ }
343
+ let cwd = "";
344
+ let cwdMax = 0;
345
+ for (const [c, n] of cwdCounts) {
346
+ if (n > cwdMax) { cwdMax = n; cwd = c; }
347
+ }
348
+ // Fall back to process.cwd() if no session records found
349
+ if (!cwd) cwd = process.cwd();
350
+
351
+ const sections: string[] = [];
352
+
353
+ sections.push(await buildSessionSummarySection(sessionId));
354
+ sections.push(buildRecentFilesSection(allRecords, sessionId, cwd));
355
+ sections.push(buildGenomeSection(cwd));
356
+ sections.push(buildOpenTodosSection(allRecords, sessionId));
357
+
358
+ const filename = makeFilename(now, opts.randSuffix);
359
+ const outPath = join(outDir, filename);
360
+
361
+ const footerNote =
362
+ `---\n` +
363
+ `Paste the contents of ${outPath} into your next session to resume context.`;
364
+
365
+ const content = [
366
+ `# ashlr handoff — ${new Date(now).toISOString()}`,
367
+ "",
368
+ ...sections.map((s) => s + "\n"),
369
+ footerNote,
370
+ ].join("\n");
371
+
372
+ if (!opts.dryRun) {
373
+ mkdirSync(outDir, { recursive: true });
374
+ writeFileSync(outPath, content, "utf-8");
375
+ }
376
+
377
+ return { path: outPath, content };
378
+ }
379
+
380
+ /** Return the most recent existing handoff file, or null. */
381
+ export function findLastHandoff(outDir: string): string | null {
382
+ try {
383
+ if (!existsSync(outDir)) return null;
384
+ const files = readdirSync(outDir)
385
+ .filter((f) => f.endsWith(".md"))
386
+ .sort() // lexicographic == chronological for YYYY-MM-DD-HHMMSS prefix
387
+ .reverse();
388
+ return files.length > 0 ? join(outDir, files[0]!) : null;
389
+ } catch {
390
+ return null;
391
+ }
392
+ }
393
+
394
+ // ---------------------------------------------------------------------------
395
+ // CLI entry
396
+ // ---------------------------------------------------------------------------
397
+
398
+ if (import.meta.main) {
399
+ const args = process.argv.slice(2);
400
+
401
+ const getFlag = (flag: string): string | undefined => {
402
+ const idx = args.indexOf(flag);
403
+ return idx !== -1 ? args[idx + 1] : undefined;
404
+ };
405
+ const hasFlag = (flag: string): boolean => args.includes(flag);
406
+
407
+ const sessionFlag = getFlag("--session");
408
+ const dirFlag = getFlag("--dir");
409
+ const lastFlag = hasFlag("--last");
410
+
411
+ const homeDir = homedir();
412
+ const outDir = dirFlag ?? join(homeDir, ".ashlr", "handoffs");
413
+
414
+ if (lastFlag) {
415
+ const last = findLastHandoff(outDir);
416
+ if (!last) {
417
+ process.stdout.write("(no handoffs yet)\n");
418
+ } else {
419
+ const preview = readFileSync(last, "utf-8").split("\n").slice(0, 5).join("\n");
420
+ process.stdout.write(`Last handoff: ${last}\n\n${preview}\n...\n`);
421
+ }
422
+ process.exit(0);
423
+ }
424
+
425
+ buildHandoffPack({
426
+ sessionId: sessionFlag,
427
+ outDir,
428
+ })
429
+ .then(({ path, content }) => {
430
+ const preview = content.split("\n").slice(0, 5).join("\n");
431
+ process.stdout.write(`Handoff written: ${path}\n\n${preview}\n...\n`);
432
+ })
433
+ .catch((err) => {
434
+ const msg = err instanceof Error ? err.message : String(err);
435
+ process.stdout.write(`handoff-pack failed: ${msg}\n`);
436
+ })
437
+ .finally(() => process.exit(0));
438
+ }
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * hook-bootstrap.mjs — node trampoline for every hook in hooks/hooks.json.
4
+ *
5
+ * Mirror of scripts/bootstrap.mjs but for the Claude Code hooks surface.
6
+ * Claude Code spawns hook commands with its *own* parent-process PATH, which
7
+ * doesn't include ~/.bun/bin right after a first-run auto-install. Without
8
+ * this trampoline, every hook silently dies with "bun: not found" until the
9
+ * user restarts Claude Code.
10
+ *
11
+ * Flow:
12
+ * 1. If bun is already on PATH: spawn it directly.
13
+ * 2. Otherwise, try prepending ~/.bun/bin (populated by a prior
14
+ * bootstrap.mjs auto-install from the MCP server spawn).
15
+ * 3. If bun is STILL unreachable: exit 0 silently. Hooks are decoration,
16
+ * never a gate on the harness — a missing bun here just means the hook
17
+ * no-ops for this session, not that the user's workflow breaks.
18
+ *
19
+ * Critical: this trampoline NEVER auto-installs bun. Hooks fire concurrently
20
+ * (multiple PreToolUse + PostToolUse in parallel); racing installers would
21
+ * be catastrophic. Only scripts/bootstrap.mjs (MCP server spawn — single
22
+ * producer) is allowed to trigger the installer.
23
+ *
24
+ * ASHLR_NO_AUTO_INSTALL only gates *installation*; PATH *resolution* always
25
+ * runs — we never refuse to look at a bin dir the user already has on disk.
26
+ *
27
+ * Usage (wired from hooks/hooks.json):
28
+ * node "${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs" \
29
+ * "${CODEX_PLUGIN_ROOT}/hooks/<hook>.ts" [args...]
30
+ */
31
+
32
+ import { spawnSync } from "node:child_process";
33
+ import { hasBun, prependBunToPath } from "./bun-resolve.mjs";
34
+
35
+ const hookPath = process.argv[2];
36
+ if (!hookPath) {
37
+ process.stderr.write("[ashlr:hook-bootstrap] missing hook path argument\n");
38
+ process.exit(0); // still don't gate the harness
39
+ }
40
+
41
+ const looksLikeCodexPluginHook =
42
+ Boolean(process.env.CODEX_PLUGIN_ROOT) ||
43
+ Boolean(process.env.ASHLR_CODEX_HOOK_MODE) ||
44
+ (Boolean(process.env.CLAUDE_PLUGIN_ROOT) &&
45
+ !process.env.CLAUDE_SESSION_ID &&
46
+ !process.env.CLAUDE_CODE_MODEL);
47
+
48
+ if (looksLikeCodexPluginHook && !process.env.ASHLR_MCP_HOST) {
49
+ process.env.ASHLR_MCP_HOST = "codex-cli";
50
+ }
51
+ if (process.env.ASHLR_MCP_HOST === "codex-cli" && !process.env.ASHLR_HOOK_MODE) {
52
+ process.env.ASHLR_HOOK_MODE = process.env.ASHLR_CODEX_HOOK_MODE || "nudge";
53
+ }
54
+
55
+ if (!hasBun()) {
56
+ prependBunToPath();
57
+ if (!hasBun()) {
58
+ // Bun genuinely unreachable. Exit 0 — hooks must never fail the harness.
59
+ // The MCP server's bootstrap.mjs will install bun on its next spawn if it
60
+ // hasn't already; this hook simply no-ops for now.
61
+ process.exit(0);
62
+ }
63
+ }
64
+
65
+ const forwarded = process.argv.slice(3);
66
+ const result = spawnSync("bun", ["run", hookPath, ...forwarded], {
67
+ stdio: "inherit",
68
+ env: process.env,
69
+ });
70
+
71
+ if (result.error) {
72
+ process.stderr.write(`[ashlr:hook-bootstrap] ${result.error.message}\n`);
73
+ process.exit(0);
74
+ }
75
+ process.exit(result.status ?? 0);