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,614 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * ashlr session-resume — reads ~/.ashlr/session-log.jsonl (+ rotated .1/.2)
4
+ * and produces a compact "pick up where you left off" summary.
5
+ *
6
+ * Exported surface:
7
+ * buildResume(opts?) → formatted string, never throws.
8
+ * SessionSummary — the data structure for tests to inspect.
9
+ *
10
+ * CLI usage:
11
+ * bun run scripts/session-resume.ts [<branch>]
12
+ *
13
+ * Limitations:
14
+ * - The session log records tool names + cwd, NOT individual file paths or
15
+ * grep patterns. "Files touched" is approximated from read/edit tool call
16
+ * counts grouped by cwd directory; "patterns" and "bash commands" are
17
+ * reconstructed from tool-call frequency only (no argument capture).
18
+ * - Branch detection uses git log timestamps — requires git in PATH and the
19
+ * cwd to be a git repo at resume time.
20
+ */
21
+
22
+ import { existsSync, readFileSync } from "fs";
23
+ import { homedir } from "os";
24
+ import { join, basename } from "path";
25
+ import { execSync } from "child_process";
26
+ import { costFor } from "../servers/_pricing.ts";
27
+ import { getDigestForResume } from "./weekly-digest.ts";
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Schema
31
+ // ---------------------------------------------------------------------------
32
+
33
+ interface LogRecord {
34
+ ts: string;
35
+ agent?: string;
36
+ event: string;
37
+ tool: string;
38
+ cwd: string;
39
+ session: string;
40
+ input_size: number;
41
+ output_size: number;
42
+ calls?: number;
43
+ tokens_saved?: number;
44
+ started_at?: string;
45
+ reason?: string;
46
+ }
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Options
50
+ // ---------------------------------------------------------------------------
51
+
52
+ export interface BuildResumeOpts {
53
+ /** Override home dir — used by tests. */
54
+ home?: string;
55
+ /** Filter to sessions on this branch (cross-refs via git log timestamps). */
56
+ branch?: string;
57
+ /** Injected now in ms — tests pin this. */
58
+ now?: number;
59
+ /** Max recent sessions to show (default: 1). */
60
+ maxSessions?: number;
61
+ /** Max lines to read per log file (default: unlimited). */
62
+ limitLines?: number;
63
+ /** Git cwd for branch detection (default: process.cwd()). */
64
+ gitCwd?: string;
65
+ /**
66
+ * When true, suppress the weekly digest prepend even if 7+ days have
67
+ * elapsed. Used by tests that don't want digest output mixed in.
68
+ */
69
+ suppressDigest?: boolean;
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Per-session summary data (exported for tests)
74
+ // ---------------------------------------------------------------------------
75
+
76
+ export interface FileStat {
77
+ cwd: string;
78
+ readCalls: number;
79
+ editCalls: number;
80
+ totalCalls: number;
81
+ }
82
+
83
+ export interface ToolCallStat {
84
+ tool: string;
85
+ calls: number;
86
+ }
87
+
88
+ export interface SessionSummary {
89
+ sessionId: string;
90
+ startTs: number; // ms
91
+ endTs: number; // ms
92
+ totalCalls: number;
93
+ tokensSaved: number; // from session_end if present, else 0
94
+ /** Top N cwds touched by read/edit tools, sorted by call count. */
95
+ topFiles: FileStat[];
96
+ /** Top N tools used, sorted by call count — for pattern extraction. */
97
+ topTools: ToolCallStat[];
98
+ /** Top N bash-family tool calls. */
99
+ topBash: ToolCallStat[];
100
+ /** Branch active during session (if detectable). */
101
+ branch?: string;
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Tool classification
106
+ // ---------------------------------------------------------------------------
107
+
108
+ const READ_TOOLS = new Set([
109
+ "Read", "ashlr__read", "ashlr__diff", "ashlr__diff_semantic",
110
+ "ashlr__tree", "ashlr__ls", "ashlr__glob",
111
+ ]);
112
+
113
+ const EDIT_TOOLS = new Set([
114
+ "Edit", "MultiEdit", "Write",
115
+ "ashlr__edit", "ashlr__multi_edit", "ashlr__edit_structural",
116
+ "ashlr__search_replace_regex", "ashlr__rename_file",
117
+ ]);
118
+
119
+ const GREP_TOOLS = new Set([
120
+ "Grep", "ashlr__grep",
121
+ ]);
122
+
123
+ const BASH_TOOLS = new Set([
124
+ "Bash", "ashlr__bash", "ashlr__bash_list", "ashlr__bash_start",
125
+ "ashlr__bash_stop", "ashlr__bash_tail",
126
+ ]);
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // JSONL reader
130
+ // ---------------------------------------------------------------------------
131
+
132
+ function readLines(path: string, limit?: number): string[] {
133
+ try {
134
+ if (!existsSync(path)) return [];
135
+ const raw = readFileSync(path, "utf-8");
136
+ const lines = raw.split("\n").filter((l) => l.trim().length > 0);
137
+ return limit ? lines.slice(0, limit) : lines;
138
+ } catch {
139
+ return [];
140
+ }
141
+ }
142
+
143
+ function parseRecords(lines: string[]): LogRecord[] {
144
+ const out: LogRecord[] = [];
145
+ for (const line of lines) {
146
+ try {
147
+ const r = JSON.parse(line) as Partial<LogRecord>;
148
+ if (typeof r.ts !== "string" || typeof r.event !== "string") continue;
149
+ out.push({
150
+ ts: r.ts,
151
+ agent: r.agent ?? "unknown",
152
+ event: r.event,
153
+ tool: r.tool ?? "unknown",
154
+ cwd: r.cwd ?? "",
155
+ session: r.session ?? "",
156
+ input_size: typeof r.input_size === "number" ? r.input_size : 0,
157
+ output_size: typeof r.output_size === "number" ? r.output_size : 0,
158
+ calls: r.calls,
159
+ tokens_saved: r.tokens_saved,
160
+ started_at: r.started_at,
161
+ reason: r.reason,
162
+ });
163
+ } catch {
164
+ // skip malformed
165
+ }
166
+ }
167
+ return out;
168
+ }
169
+
170
+ // ---------------------------------------------------------------------------
171
+ // Session grouping
172
+ // ---------------------------------------------------------------------------
173
+
174
+ interface SessionGroup {
175
+ sessionId: string;
176
+ records: LogRecord[];
177
+ sessionEnd?: LogRecord;
178
+ }
179
+
180
+ function groupBySessions(records: LogRecord[]): Map<string, SessionGroup> {
181
+ const groups = new Map<string, SessionGroup>();
182
+
183
+ for (const r of records) {
184
+ const sid = r.session || "unknown";
185
+ let g = groups.get(sid);
186
+ if (!g) {
187
+ g = { sessionId: sid, records: [] };
188
+ groups.set(sid, g);
189
+ }
190
+ if (r.event === "session_end") {
191
+ g.sessionEnd = r;
192
+ } else {
193
+ g.records.push(r);
194
+ }
195
+ }
196
+
197
+ return groups;
198
+ }
199
+
200
+ // ---------------------------------------------------------------------------
201
+ // Branch detection via git log
202
+ // ---------------------------------------------------------------------------
203
+
204
+ function detectBranchForTimeRange(
205
+ startMs: number,
206
+ endMs: number,
207
+ cwd: string,
208
+ ): string | undefined {
209
+ try {
210
+ // Use git log to find the branch that was active around the session time.
211
+ // Strategy: ask git for refs that have commits in the session window.
212
+ const since = new Date(startMs - 300_000).toISOString(); // 5m before
213
+ const until = new Date(endMs + 300_000).toISOString(); // 5m after
214
+
215
+ const result = execSync(
216
+ `git log --all --oneline --format="%D" --after="${since}" --before="${until}" 2>/dev/null | head -5`,
217
+ { cwd, encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] },
218
+ ).trim();
219
+
220
+ if (!result) return undefined;
221
+
222
+ // Parse "HEAD -> main, origin/main" style refs
223
+ for (const line of result.split("\n")) {
224
+ const headArrow = line.match(/HEAD -> ([^\s,]+)/);
225
+ if (headArrow) return headArrow[1];
226
+ // fallback: first local branch name
227
+ const local = line.match(/(?:^|, )([^\s,/]+\/[^\s,]+|[a-zA-Z][^\s,]*)/);
228
+ if (local && !local[1].startsWith("origin/")) return local[1];
229
+ }
230
+ return undefined;
231
+ } catch {
232
+ return undefined;
233
+ }
234
+ }
235
+
236
+ // ---------------------------------------------------------------------------
237
+ // Session analysis
238
+ // ---------------------------------------------------------------------------
239
+
240
+ function analyzeSession(
241
+ group: SessionGroup,
242
+ branch?: string,
243
+ gitCwd?: string,
244
+ now?: number,
245
+ ): SessionSummary {
246
+ const { sessionId, records, sessionEnd } = group;
247
+
248
+ // Timestamps
249
+ const timestamps = records
250
+ .map((r) => Date.parse(r.ts))
251
+ .filter(Number.isFinite);
252
+
253
+ const startTs = timestamps.length > 0 ? Math.min(...timestamps) : (now ?? Date.now());
254
+ const endTs = timestamps.length > 0 ? Math.max(...timestamps) : startTs;
255
+
256
+ // Tokens saved — prefer session_end, else 0
257
+ const tokensSaved = sessionEnd?.tokens_saved ?? 0;
258
+
259
+ // Total calls (tool_call events only)
260
+ const toolCalls = records.filter((r) =>
261
+ r.event === "tool_call" || r.event === "tool_escalate" || r.event === "tool_fallback"
262
+ );
263
+ const totalCalls = toolCalls.length;
264
+
265
+ // Files touched: group by cwd, count read vs edit calls
266
+ const cwdStats = new Map<string, { read: number; edit: number; grep: number; bash: number }>();
267
+ const toolFreq = new Map<string, number>();
268
+ const bashFreq = new Map<string, number>();
269
+
270
+ for (const r of toolCalls) {
271
+ const tool = r.tool;
272
+
273
+ // Tool frequency (all tools)
274
+ toolFreq.set(tool, (toolFreq.get(tool) ?? 0) + 1);
275
+
276
+ // Bash frequency
277
+ if (BASH_TOOLS.has(tool)) {
278
+ bashFreq.set(tool, (bashFreq.get(tool) ?? 0) + 1);
279
+ }
280
+
281
+ // CWD-level file stats
282
+ if (READ_TOOLS.has(tool) || EDIT_TOOLS.has(tool) || GREP_TOOLS.has(tool)) {
283
+ const cwd = r.cwd || "unknown";
284
+ let s = cwdStats.get(cwd);
285
+ if (!s) {
286
+ s = { read: 0, edit: 0, grep: 0, bash: 0 };
287
+ cwdStats.set(cwd, s);
288
+ }
289
+ if (READ_TOOLS.has(tool)) s.read++;
290
+ else if (EDIT_TOOLS.has(tool)) s.edit++;
291
+ else if (GREP_TOOLS.has(tool)) s.grep++;
292
+ }
293
+ }
294
+
295
+ // Top 5 files (cwds) by total call count
296
+ const topFiles: FileStat[] = [...cwdStats.entries()]
297
+ .map(([cwd, s]) => ({
298
+ cwd,
299
+ readCalls: s.read,
300
+ editCalls: s.edit,
301
+ totalCalls: s.read + s.edit + s.grep,
302
+ }))
303
+ .sort((a, b) => b.totalCalls - a.totalCalls)
304
+ .slice(0, 5);
305
+
306
+ // Top 3 grep/search tools + top N all tools (for "patterns" context)
307
+ const topTools: ToolCallStat[] = [...toolFreq.entries()]
308
+ .map(([tool, calls]) => ({ tool, calls }))
309
+ .sort((a, b) => b.calls - a.calls)
310
+ .slice(0, 5);
311
+
312
+ // Top 5 bash tools
313
+ const topBash: ToolCallStat[] = [...bashFreq.entries()]
314
+ .map(([tool, calls]) => ({ tool, calls }))
315
+ .sort((a, b) => b.calls - a.calls)
316
+ .slice(0, 5);
317
+
318
+ // Branch detection
319
+ let detectedBranch = branch;
320
+ if (!detectedBranch && gitCwd && timestamps.length > 0) {
321
+ detectedBranch = detectBranchForTimeRange(startTs, endTs, gitCwd);
322
+ }
323
+
324
+ return {
325
+ sessionId,
326
+ startTs,
327
+ endTs,
328
+ totalCalls,
329
+ tokensSaved,
330
+ topFiles,
331
+ topTools,
332
+ topBash,
333
+ branch: detectedBranch,
334
+ };
335
+ }
336
+
337
+ // ---------------------------------------------------------------------------
338
+ // Formatting helpers
339
+ // ---------------------------------------------------------------------------
340
+
341
+ function fmtDurationRelative(ms: number, now: number): string {
342
+ const diffMs = now - ms;
343
+ if (diffMs < 0) return "just now";
344
+ const mins = Math.floor(diffMs / 60_000);
345
+ const hours = Math.floor(mins / 60);
346
+ const days = Math.floor(hours / 24);
347
+ if (days >= 2) return `${days} days ago`;
348
+ if (days === 1) return "yesterday";
349
+ if (hours >= 1) return `${hours}h ago`;
350
+ if (mins >= 1) return `${mins}m ago`;
351
+ return "just now";
352
+ }
353
+
354
+ function fmtDuration(ms: number): string {
355
+ if (ms <= 0) return "< 1m";
356
+ const mins = Math.floor(ms / 60_000);
357
+ const hours = Math.floor(mins / 60);
358
+ if (hours >= 1) return `${hours}h ${mins % 60}m`;
359
+ if (mins >= 1) return `${mins}m`;
360
+ return "< 1m";
361
+ }
362
+
363
+ function fmtTokens(n: number): string {
364
+ if (!Number.isFinite(n) || n < 0) return "0";
365
+ if (n < 1000) return String(Math.floor(n));
366
+ if (n < 1_000_000) return (n / 1000).toFixed(1) + "K";
367
+ return (n / 1_000_000).toFixed(1) + "M";
368
+ }
369
+
370
+ function fmtCost(tokens: number): string {
371
+ const dollars = costFor(tokens);
372
+ return `$${dollars.toFixed(2)}`;
373
+ }
374
+
375
+ function shortId(id: string): string {
376
+ if (!id || id.length <= 8) return id || "?";
377
+ return id.slice(0, 8);
378
+ }
379
+
380
+ // ---------------------------------------------------------------------------
381
+ // Render
382
+ // ---------------------------------------------------------------------------
383
+
384
+ function renderSession(
385
+ summary: SessionSummary,
386
+ now: number,
387
+ index: number,
388
+ ): string {
389
+ const lines: string[] = [];
390
+
391
+ const ago = fmtDurationRelative(summary.startTs, now);
392
+ const dur = fmtDuration(summary.endTs - summary.startTs);
393
+ const tokStr = fmtTokens(summary.tokensSaved);
394
+ const costStr = summary.tokensSaved > 0 ? ` ≈${fmtCost(summary.tokensSaved)}` : "";
395
+ const saved = summary.tokensSaved > 0
396
+ ? ` — saved ${tokStr} tokens${costStr}`
397
+ : "";
398
+
399
+ const label = index === 0 ? "Last session" : `Session ${index + 1}`;
400
+ lines.push(`${label} (${ago}, ${dur}${saved}):`);
401
+
402
+ if (summary.branch) {
403
+ lines.push(` Branch: ${summary.branch}`);
404
+ }
405
+
406
+ if (summary.topFiles.length > 0) {
407
+ const first = summary.topFiles[0]!;
408
+ const dirName = basename(first.cwd) || first.cwd;
409
+ lines.push(
410
+ ` Work dir: ${dirName} (${first.readCalls} reads, ${first.editCalls} edits)`,
411
+ );
412
+ for (const f of summary.topFiles.slice(1)) {
413
+ const d = basename(f.cwd) || f.cwd;
414
+ lines.push(
415
+ ` ${d} (${f.readCalls} reads, ${f.editCalls} edits)`,
416
+ );
417
+ }
418
+ } else {
419
+ lines.push(" Work dir: (not recorded)");
420
+ }
421
+
422
+ // Tool patterns: highlight grep, search, read calls
423
+ const searchTools = summary.topTools.filter((t) => GREP_TOOLS.has(t.tool));
424
+ if (searchTools.length > 0) {
425
+ const top3 = searchTools.slice(0, 3);
426
+ lines.push(` Patterns: ${top3.map((t) => `${t.tool} ×${t.calls}`).join(", ")}`);
427
+ }
428
+
429
+ // Bash calls
430
+ if (summary.topBash.length > 0) {
431
+ const top5 = summary.topBash.slice(0, 5);
432
+ lines.push(` Bash: ${top5.map((t) => `${t.tool} ×${t.calls}`).join(", ")}`);
433
+ }
434
+
435
+ lines.push(` Calls: ${summary.totalCalls} tool invocations`);
436
+
437
+ return lines.join("\n");
438
+ }
439
+
440
+ function renderSuggestions(summary: SessionSummary): string {
441
+ const lines: string[] = [];
442
+ lines.push("");
443
+ lines.push("Resume? Suggested next steps based on the trail:");
444
+
445
+ if (summary.topFiles.length > 0) {
446
+ const top = summary.topFiles[0]!;
447
+ const dirName = basename(top.cwd) || top.cwd;
448
+ if (top.editCalls > 0) {
449
+ lines.push(` - Re-open ${dirName} (last edits in this session)`);
450
+ } else {
451
+ lines.push(` - Review ${dirName} (most activity in this session)`);
452
+ }
453
+ }
454
+
455
+ if (summary.topBash.length > 0) {
456
+ const topBash = summary.topBash[0]!;
457
+ lines.push(` - Re-run: ${topBash.tool} (used ×${topBash.calls} last session)`);
458
+ }
459
+
460
+ if (summary.branch) {
461
+ lines.push(` - Continue on branch: ${summary.branch}`);
462
+ }
463
+
464
+ if (lines.length === 2) {
465
+ // Only the header was added
466
+ lines.push(" - Check git status and recent commits to orient");
467
+ }
468
+
469
+ return lines.join("\n");
470
+ }
471
+
472
+ // ---------------------------------------------------------------------------
473
+ // Branch filter: match sessions whose timestamp range overlaps a git branch
474
+ // ---------------------------------------------------------------------------
475
+
476
+ function getGitBranchCommitRange(
477
+ branch: string,
478
+ gitCwd: string,
479
+ ): { earliest: number; latest: number } | undefined {
480
+ try {
481
+ const result = execSync(
482
+ `git log "${branch}" --format="%ci" --max-count=50 2>/dev/null`,
483
+ { cwd: gitCwd, encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] },
484
+ ).trim();
485
+
486
+ if (!result) return undefined;
487
+
488
+ const timestamps = result
489
+ .split("\n")
490
+ .map((l) => Date.parse(l.trim()))
491
+ .filter(Number.isFinite);
492
+
493
+ if (timestamps.length === 0) return undefined;
494
+
495
+ return {
496
+ earliest: Math.min(...timestamps),
497
+ latest: Math.max(...timestamps),
498
+ };
499
+ } catch {
500
+ return undefined;
501
+ }
502
+ }
503
+
504
+ // ---------------------------------------------------------------------------
505
+ // Public API
506
+ // ---------------------------------------------------------------------------
507
+
508
+ export function buildResume(opts: BuildResumeOpts = {}): string {
509
+ const home = opts.home ?? homedir();
510
+ const now = opts.now ?? Date.now();
511
+ const maxSessions = opts.maxSessions ?? 1;
512
+ const gitCwd = opts.gitCwd ?? process.cwd();
513
+
514
+ const logPath = join(home, ".ashlr", "session-log.jsonl");
515
+ const rotated1 = logPath + ".1";
516
+ const rotated2 = logPath + ".2";
517
+
518
+ const lines1 = readLines(rotated2, opts.limitLines);
519
+ const lines2 = readLines(rotated1, opts.limitLines);
520
+ const lines3 = readLines(logPath, opts.limitLines);
521
+ const allLines = [...lines1, ...lines2, ...lines3];
522
+
523
+ if (allLines.length === 0) {
524
+ return [
525
+ "No prior sessions found — you're starting fresh.",
526
+ "",
527
+ "Once you use ashlr__read, ashlr__grep, or ashlr__edit, activity will",
528
+ `be recorded to: ${logPath}`,
529
+ ].join("\n");
530
+ }
531
+
532
+ const records = parseRecords(allLines);
533
+ const groups = groupBySessions(records);
534
+
535
+ if (groups.size === 0) {
536
+ return "No prior sessions found — you're starting fresh.";
537
+ }
538
+
539
+ // Compute summaries, sorted by most recent first
540
+ let summaries: SessionSummary[] = [...groups.values()]
541
+ .map((g) => analyzeSession(g, undefined, gitCwd, now))
542
+ .filter((s) => s.totalCalls > 0)
543
+ .sort((a, b) => b.endTs - a.endTs);
544
+
545
+ if (summaries.length === 0) {
546
+ return "No activity recorded yet — you're starting fresh.";
547
+ }
548
+
549
+ // Branch filter
550
+ if (opts.branch) {
551
+ const range = getGitBranchCommitRange(opts.branch, gitCwd);
552
+ if (range) {
553
+ const branchSummaries = summaries.filter(
554
+ (s) => s.startTs <= range.latest + 24 * 60 * 60_000 &&
555
+ s.endTs >= range.earliest - 24 * 60 * 60_000,
556
+ );
557
+ if (branchSummaries.length > 0) {
558
+ summaries = branchSummaries.map((s) => ({ ...s, branch: opts.branch }));
559
+ } else {
560
+ // No sessions match the branch — fall back to most recent with a note
561
+ summaries = summaries.slice(0, maxSessions);
562
+ const parts = summaries
563
+ .map((s, i) => renderSession(s, now, i))
564
+ .join("\n\n");
565
+ return [
566
+ `No sessions found on branch "${opts.branch}".`,
567
+ "Showing most recent session instead:",
568
+ "",
569
+ parts,
570
+ renderSuggestions(summaries[0]!),
571
+ ].join("\n");
572
+ }
573
+ } else {
574
+ // Can't determine branch range — tag matching sessions with branch name
575
+ summaries = summaries.slice(0, maxSessions).map((s) => ({
576
+ ...s,
577
+ branch: opts.branch,
578
+ }));
579
+ }
580
+ }
581
+
582
+ const toShow = summaries.slice(0, maxSessions);
583
+ const parts = toShow.map((s, i) => renderSession(s, now, i)).join("\n\n");
584
+ const suggestions = renderSuggestions(toShow[0]!);
585
+
586
+ // Weekly digest — prepend when 7+ days have elapsed since last shown.
587
+ // Suppressed when suppressDigest=true (test isolation) or on error.
588
+ let digestPrefix = "";
589
+ if (!opts.suppressDigest) {
590
+ try {
591
+ const digest = getDigestForResume(home, now);
592
+ if (digest) digestPrefix = digest + "\n\n---\n\n";
593
+ } catch {
594
+ /* digest is decoration — never break resume */
595
+ }
596
+ }
597
+
598
+ return digestPrefix + [parts, suggestions].join("\n");
599
+ }
600
+
601
+ // ---------------------------------------------------------------------------
602
+ // CLI entry
603
+ // ---------------------------------------------------------------------------
604
+
605
+ if (import.meta.main) {
606
+ try {
607
+ const branch = process.argv[2]; // optional branch arg
608
+ process.stdout.write(buildResume({ branch }) + "\n");
609
+ } catch (err) {
610
+ const msg = err instanceof Error ? err.message : String(err);
611
+ process.stdout.write(`session-resume failed: ${msg}\n`);
612
+ }
613
+ process.exit(0);
614
+ }
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * set-hook-mode.ts — Flip ~/.ashlr/config.json::hookMode.
4
+ *
5
+ * Usage:
6
+ * bun run scripts/set-hook-mode.ts redirect
7
+ * bun run scripts/set-hook-mode.ts nudge
8
+ * bun run scripts/set-hook-mode.ts off
9
+ *
10
+ * Prints: "ashlr hook mode set to <mode>. Restart Claude Code to apply."
11
+ * Exits 1 with an error message on invalid input.
12
+ *
13
+ * Design:
14
+ * - Reads existing config.json to preserve other keys.
15
+ * - Writes atomically (write to tmp, then rename — avoids corruption if
16
+ * killed mid-write).
17
+ * - Creates ~/.ashlr/ if it doesn't exist.
18
+ * - Never touches settings.json or any other file.
19
+ */
20
+
21
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
22
+ import { homedir } from "os";
23
+ import { dirname, join } from "path";
24
+
25
+ export type HookMode = "redirect" | "nudge" | "off";
26
+
27
+ const VALID_MODES: HookMode[] = ["redirect", "nudge", "off"];
28
+
29
+ function configPath(home: string = process.env.HOME ?? homedir()): string {
30
+ return join(home, ".ashlr", "config.json");
31
+ }
32
+
33
+ /**
34
+ * Set the hook mode in ~/.ashlr/config.json.
35
+ * Returns the path written to — exported so tests can call directly.
36
+ */
37
+ export function setHookMode(mode: HookMode, home?: string): string {
38
+ const p = configPath(home);
39
+ mkdirSync(dirname(p), { recursive: true });
40
+
41
+ // Read existing config, preserve other keys.
42
+ let existing: Record<string, unknown> = {};
43
+ if (existsSync(p)) {
44
+ try {
45
+ existing = JSON.parse(readFileSync(p, "utf-8")) as Record<string, unknown>;
46
+ } catch {
47
+ // Overwrite corrupt config.
48
+ existing = {};
49
+ }
50
+ }
51
+
52
+ existing.hookMode = mode;
53
+
54
+ // Atomic write via tmp + rename.
55
+ const tmp = p + ".tmp." + process.pid;
56
+ writeFileSync(tmp, JSON.stringify(existing, null, 2) + "\n", "utf-8");
57
+ renameSync(tmp, p);
58
+
59
+ return p;
60
+ }
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // CLI entry point
64
+ // ---------------------------------------------------------------------------
65
+
66
+ if (import.meta.main) {
67
+ const arg = process.argv[2];
68
+
69
+ if (!arg || !(VALID_MODES as string[]).includes(arg)) {
70
+ process.stderr.write(
71
+ `Error: invalid hook mode "${arg ?? ""}". ` +
72
+ `Valid values: redirect, nudge, off.\n` +
73
+ `Usage: bun run scripts/set-hook-mode.ts <redirect|nudge|off>\n`,
74
+ );
75
+ process.exit(1);
76
+ }
77
+
78
+ const mode = arg as HookMode;
79
+ setHookMode(mode);
80
+ process.stdout.write(
81
+ `ashlr hook mode set to ${mode}. Restart Claude Code to apply.\n`,
82
+ );
83
+ }