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,297 @@
1
+ /**
2
+ * _genome-sync.ts — Plugin-side team genome sync client.
3
+ *
4
+ * Opt-in: set ASHLR_TEAM_GENOME_ID (plus existing ASHLR_PRO_TOKEN).
5
+ *
6
+ * Used by:
7
+ * - hooks/session-start.ts: pull remote sections on session start
8
+ * - servers/_genome-live.ts: push updated sections after ashlr__edit
9
+ *
10
+ * Design constraints:
11
+ * - Never throws — all functions catch internally and log to stderr.
12
+ * - Non-blocking: callers fire-and-forget (no await required).
13
+ * - Tracks a local sequence number in ASHLR_GENOME_LOCAL_SEQ_PATH so
14
+ * incremental pulls only fetch sections modified since last pull.
15
+ *
16
+ * Encryption (v1):
17
+ * When a team key exists at ~/.ashlr/team-keys/<genomeId>.key, every
18
+ * section's content is encrypted with AES-256-GCM before upload and
19
+ * decrypted after pull. The server stores only ciphertext; it cannot read
20
+ * section content. Vclock metadata is transmitted in plaintext so the
21
+ * server can perform conflict detection without decrypting.
22
+ *
23
+ * If no key file exists, sync falls back to plaintext (backward compat).
24
+ * Existing plaintext sections are returned as-is on pull; if a section
25
+ * fails to decrypt (wrong key, tamper), it is skipped with a warning.
26
+ */
27
+
28
+ import { readFile, writeFile, mkdir } from "fs/promises";
29
+ import { existsSync } from "fs";
30
+ import { join, dirname } from "path";
31
+ import { homedir } from "os";
32
+ import {
33
+ loadKey,
34
+ encryptSection,
35
+ decryptSection,
36
+ serializeBlob,
37
+ parseBlob,
38
+ } from "./_genome-crypto.ts";
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Config — read at call time so tests can set env vars after module load
42
+ // ---------------------------------------------------------------------------
43
+
44
+ function cfg() {
45
+ return {
46
+ apiUrl: process.env["ASHLR_API_URL"] ?? "https://api.ashlr.ai",
47
+ genomeId: process.env["ASHLR_TEAM_GENOME_ID"] ?? "",
48
+ proToken: process.env["ASHLR_PRO_TOKEN"] ?? "",
49
+ clientId: process.env["ASHLR_CLIENT_ID"] ?? `client_${process.pid}`,
50
+ localSeqPath: process.env["ASHLR_GENOME_LOCAL_SEQ_PATH"] ?? join(homedir(), ".ashlr", "genome-seq.json"),
51
+ };
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Types
56
+ // ---------------------------------------------------------------------------
57
+
58
+ export interface RemoteSection {
59
+ path: string;
60
+ content: string;
61
+ content_encrypted: boolean;
62
+ vclock: Record<string, number>;
63
+ conflictFlag: boolean;
64
+ serverSeq: number;
65
+ }
66
+
67
+ export interface PullResult {
68
+ sections: RemoteSection[];
69
+ serverSeqNum: number;
70
+ }
71
+
72
+ export interface PushResult {
73
+ applied: string[];
74
+ conflicts: string[];
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Guards
79
+ // ---------------------------------------------------------------------------
80
+
81
+ /** Returns true when team genome sync is configured. */
82
+ export function isTeamGenomeEnabled(): boolean {
83
+ const { genomeId, proToken } = cfg();
84
+ return Boolean(genomeId && proToken);
85
+ }
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Local seq persistence
89
+ // ---------------------------------------------------------------------------
90
+
91
+ async function readLocalSeq(): Promise<number> {
92
+ try {
93
+ const raw = await readFile(cfg().localSeqPath, "utf-8");
94
+ const obj = JSON.parse(raw) as { seq?: number };
95
+ return typeof obj.seq === "number" ? obj.seq : 0;
96
+ } catch {
97
+ return 0;
98
+ }
99
+ }
100
+
101
+ async function writeLocalSeq(seq: number): Promise<void> {
102
+ try {
103
+ const p = cfg().localSeqPath;
104
+ await mkdir(dirname(p), { recursive: true });
105
+ await writeFile(p, JSON.stringify({ seq }), "utf-8");
106
+ } catch {
107
+ // Non-fatal
108
+ }
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // HTTP helpers
113
+ // ---------------------------------------------------------------------------
114
+
115
+ async function apiFetch(
116
+ method: string,
117
+ path: string,
118
+ body?: unknown,
119
+ ): Promise<Response> {
120
+ const { apiUrl, proToken } = cfg();
121
+ const url = `${apiUrl}${path}`;
122
+ const init: RequestInit = {
123
+ method,
124
+ headers: {
125
+ Authorization: `Bearer ${proToken}`,
126
+ "Content-Type": "application/json",
127
+ },
128
+ };
129
+ if (body !== undefined) {
130
+ init.body = JSON.stringify(body);
131
+ }
132
+ return fetch(url, init);
133
+ }
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // Pull
137
+ // ---------------------------------------------------------------------------
138
+
139
+ /**
140
+ * Pull remote sections since the last known sequence number, write them to
141
+ * .ashlrcode/genome/sections/, update local seq.
142
+ *
143
+ * @param genomeSectionsDir Absolute path to .ashlrcode/genome/sections/ in the repo.
144
+ */
145
+ export async function pullTeamGenome(genomeSectionsDir: string): Promise<PullResult | null> {
146
+ if (!isTeamGenomeEnabled()) return null;
147
+
148
+ try {
149
+ const since = await readLocalSeq();
150
+ const res = await apiFetch("GET", `/genome/${cfg().genomeId}/pull?since=${since}`);
151
+
152
+ if (!res.ok) {
153
+ process.stderr.write(`[ashlr-genome-sync] pull failed: ${res.status}\n`);
154
+ return null;
155
+ }
156
+
157
+ const data = await res.json() as PullResult;
158
+
159
+ if (data.sections.length > 0) {
160
+ await mkdir(genomeSectionsDir, { recursive: true });
161
+
162
+ // Load team key once for the whole pull (null = no key file = plaintext mode)
163
+ const teamKey = await loadKey(cfg().genomeId).catch(() => null);
164
+
165
+ for (const section of data.sections) {
166
+ const safeName = section.path.replace(/^sections\//, "");
167
+ // Additional client-side path safety check
168
+ if (safeName.includes("..") || safeName.startsWith("/") || /^[A-Za-z]:[/\\]/.test(safeName)) {
169
+ process.stderr.write(`[ashlr-genome-sync] skipping unsafe path: ${section.path}\n`);
170
+ continue;
171
+ }
172
+
173
+ let plaintext = section.content;
174
+
175
+ // Decrypt if the server flagged this section as encrypted and we have a key
176
+ if (section.content_encrypted) {
177
+ if (!teamKey) {
178
+ process.stderr.write(
179
+ `[ashlr-genome] WARNING: section ${section.path} is encrypted but no local key found — skipping\n`,
180
+ );
181
+ continue;
182
+ }
183
+ try {
184
+ const blob = parseBlob(section.content);
185
+ plaintext = decryptSection(blob, teamKey);
186
+ } catch {
187
+ process.stderr.write(
188
+ `[ashlr-genome] WARNING: section ${section.path} failed to decrypt — wrong key or corrupt data\n`,
189
+ );
190
+ continue;
191
+ }
192
+ }
193
+
194
+ const dest = join(genomeSectionsDir, safeName);
195
+ await mkdir(dirname(dest), { recursive: true });
196
+ await writeFile(dest, plaintext, "utf-8");
197
+ }
198
+
199
+ await writeLocalSeq(data.serverSeqNum);
200
+ process.stderr.write(`[ashlr-genome-sync] pulled ${data.sections.length} section(s), seq=${data.serverSeqNum}\n`);
201
+ }
202
+
203
+ return data;
204
+ } catch (err) {
205
+ process.stderr.write(`[ashlr-genome-sync] pull error: ${String(err)}\n`);
206
+ return null;
207
+ }
208
+ }
209
+
210
+ // ---------------------------------------------------------------------------
211
+ // Push
212
+ // ---------------------------------------------------------------------------
213
+
214
+ /**
215
+ * Push a single updated section to the remote genome.
216
+ *
217
+ * @param sectionPath Relative path within genome sections (e.g. "sections/auth.md")
218
+ * @param content Full section content after the edit
219
+ * @param vclock Current vector clock for this client (caller manages increment)
220
+ */
221
+ export async function pushTeamGenomeSection(
222
+ sectionPath: string,
223
+ content: string,
224
+ vclock: Record<string, number>,
225
+ ): Promise<PushResult | null> {
226
+ if (!isTeamGenomeEnabled()) return null;
227
+
228
+ try {
229
+ const { genomeId, clientId } = cfg();
230
+
231
+ // Encrypt the section content if a team key is present
232
+ const teamKey = await loadKey(genomeId).catch(() => null);
233
+ let uploadContent = content;
234
+ if (teamKey) {
235
+ const blob = encryptSection(content, teamKey);
236
+ uploadContent = serializeBlob(blob);
237
+ }
238
+
239
+ const res = await apiFetch("POST", `/genome/${genomeId}/push`, {
240
+ clientId,
241
+ sections: [{ path: sectionPath, content: uploadContent, vclock }],
242
+ });
243
+
244
+ if (!res.ok) {
245
+ process.stderr.write(`[ashlr-genome-sync] push failed: ${res.status}\n`);
246
+ return null;
247
+ }
248
+
249
+ const data = await res.json() as PushResult;
250
+
251
+ if (data.conflicts.length > 0) {
252
+ process.stderr.write(
253
+ `[ashlr-genome-sync] conflict on ${data.conflicts.join(", ")} — run /ashlr-genome-conflicts to resolve\n`,
254
+ );
255
+ }
256
+
257
+ return data;
258
+ } catch (err) {
259
+ process.stderr.write(`[ashlr-genome-sync] push error: ${String(err)}\n`);
260
+ return null;
261
+ }
262
+ }
263
+
264
+ // ---------------------------------------------------------------------------
265
+ // Vclock helpers (client-side)
266
+ // ---------------------------------------------------------------------------
267
+
268
+ /** Load the local vclock state from disk. Returns {} if not found. */
269
+ export async function loadLocalVClock(genomeSectionsDir: string, sectionPath: string): Promise<Record<string, number>> {
270
+ const metaPath = join(genomeSectionsDir, ".vclock", sectionPath.replace(/^sections\//, "") + ".json");
271
+ try {
272
+ const raw = await readFile(metaPath, "utf-8");
273
+ return JSON.parse(raw) as Record<string, number>;
274
+ } catch {
275
+ return {};
276
+ }
277
+ }
278
+
279
+ /** Persist a vclock after a successful push. */
280
+ export async function saveLocalVClock(
281
+ genomeSectionsDir: string,
282
+ sectionPath: string,
283
+ vclock: Record<string, number>,
284
+ ): Promise<void> {
285
+ const metaPath = join(genomeSectionsDir, ".vclock", sectionPath.replace(/^sections\//, "") + ".json");
286
+ try {
287
+ await mkdir(dirname(metaPath), { recursive: true });
288
+ await writeFile(metaPath, JSON.stringify(vclock), "utf-8");
289
+ } catch {
290
+ // Non-fatal
291
+ }
292
+ }
293
+
294
+ /** Increment this client's counter in a vclock and return the updated clock. */
295
+ export function tickVClock(vclock: Record<string, number>, clientId = cfg().clientId): Record<string, number> {
296
+ return { ...vclock, [clientId]: (vclock[clientId] ?? 0) + 1 };
297
+ }
@@ -0,0 +1,279 @@
1
+ /**
2
+ * _history-tracker.ts — Per-session tool result history for multi-turn staleness tracking.
3
+ *
4
+ * Records every ashlr tool result emitted this session with size + freshness metadata.
5
+ * Stored in ~/.ashlr/session-history/<sessionId>.jsonl (one JSONL line per result).
6
+ *
7
+ * Freshness decay (by subsequent tool-call count since emission):
8
+ * turns 0–4 → 1.0 (fresh)
9
+ * turns 5–14 → 0.5 (stale)
10
+ * turns 15+ → 0.2 (very stale)
11
+ *
12
+ * v1.25 starting coefficients — telemetry from multi_turn_stale_estimate events will
13
+ * tune these in v1.26.
14
+ *
15
+ * Design:
16
+ * - Best-effort only — never throws, never blocks a tool call.
17
+ * - Append-only JSONL for crash-safety and cross-process reads.
18
+ * - Session-scoped: each sessionId gets its own file, GC'd by session-end hook.
19
+ * - Kill switch: ASHLR_SESSION_LOG=0 disables all writes.
20
+ */
21
+
22
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "fs";
23
+ import { homedir } from "os";
24
+ import { dirname, join } from "path";
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Constants
28
+ // ---------------------------------------------------------------------------
29
+
30
+ /** Freshness is 1.0 for the first N turns after emission. */
31
+ export const FRESH_TURNS = 5;
32
+
33
+ /** Turns 5–14 after emission decay to this freshness. */
34
+ export const STALE_FRESHNESS = 0.5;
35
+
36
+ /** Turns 15+ after emission decay to this freshness. */
37
+ export const VERY_STALE_FRESHNESS = 0.2;
38
+
39
+ /** Results older than this many turns are considered "stale" for compaction purposes. */
40
+ export const STALE_TURN_THRESHOLD = 5;
41
+
42
+ /** Nudge threshold: total stale bytes before the adaptive nudge fires. */
43
+ export const STALE_BYTES_NUDGE_THRESHOLD = 50 * 1024; // 50 KB
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Types
47
+ // ---------------------------------------------------------------------------
48
+
49
+ /** A single history entry appended per tool call. */
50
+ export interface HistoryEntry {
51
+ /** Unix ms when the result was emitted. */
52
+ ts: number;
53
+ /** Tool name (e.g. "ashlr__read", "Read"). */
54
+ tool: string;
55
+ /** Byte size of the tool result content. */
56
+ sizeBytes: number;
57
+ /** First 8 chars of SHA-256 of the result content (for dedup detection). */
58
+ contentSha8: string;
59
+ /**
60
+ * Global turn counter at time of emission. Starts at 0 and increments with
61
+ * every PostToolUse event recorded in this session (across all tools).
62
+ */
63
+ turn: number;
64
+ /** Session ID — redundant with the filename but useful for sanity checks. */
65
+ sessionId: string;
66
+ }
67
+
68
+ /** Freshness-annotated history entry (computed, not stored). */
69
+ export interface AnnotatedEntry extends HistoryEntry {
70
+ /** Turns elapsed since emission (currentTurn - entry.turn). */
71
+ turnDelta: number;
72
+ /** Freshness score [0.2, 1.0]. */
73
+ freshness: number;
74
+ /** True when turnDelta >= STALE_TURN_THRESHOLD. */
75
+ isStale: boolean;
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Path helpers
80
+ // ---------------------------------------------------------------------------
81
+
82
+ function home(): string {
83
+ return process.env.ASHLR_HOME_OVERRIDE?.trim() || process.env.HOME || homedir();
84
+ }
85
+
86
+ export function historyDir(homeDir: string = home()): string {
87
+ return join(homeDir, ".ashlr", "session-history");
88
+ }
89
+
90
+ export function historyPath(sessionId: string, homeDir: string = home()): string {
91
+ return join(historyDir(homeDir), `${sessionId}.jsonl`);
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Session ID resolution
96
+ // ---------------------------------------------------------------------------
97
+
98
+ /**
99
+ * Derive a session ID using the same strategy as session-start.ts:
100
+ * CLAUDE_SESSION_ID → ASHLR_SESSION_ID → hash of ppid + cwd.
101
+ * Stable within a single Claude Code session because ppid + cwd are constant.
102
+ */
103
+ export function resolveSessionId(): string {
104
+ const explicit =
105
+ process.env.CLAUDE_SESSION_ID?.trim() ||
106
+ process.env.ASHLR_SESSION_ID?.trim();
107
+ if (explicit) return explicit;
108
+ const seed = `${process.ppid ?? process.pid}:${process.cwd()}`;
109
+ let h = 0;
110
+ for (let i = 0; i < seed.length; i++) h = ((h << 5) - h + seed.charCodeAt(i)) | 0;
111
+ return `h${(h >>> 0).toString(16)}`;
112
+ }
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // SHA-8 helper
116
+ // ---------------------------------------------------------------------------
117
+
118
+ /** Compute first 8 hex chars of a simple djb2 hash (no native crypto needed). */
119
+ export function sha8(content: string): string {
120
+ let h = 5381;
121
+ for (let i = 0; i < content.length; i++) {
122
+ h = ((h << 5) + h + content.charCodeAt(i)) | 0;
123
+ }
124
+ return (h >>> 0).toString(16).padStart(8, "0").slice(0, 8);
125
+ }
126
+
127
+ // ---------------------------------------------------------------------------
128
+ // Current turn counter
129
+ // ---------------------------------------------------------------------------
130
+
131
+ /**
132
+ * Read the current session turn count from the history file.
133
+ * Turn = number of entries already written (0-indexed).
134
+ */
135
+ export function readCurrentTurn(sessionId: string, homeDir: string = home()): number {
136
+ try {
137
+ const p = historyPath(sessionId, homeDir);
138
+ if (!existsSync(p)) return 0;
139
+ const raw = readFileSync(p, "utf-8");
140
+ const lines = raw.split("\n").filter((l) => l.trim().length > 0);
141
+ return lines.length;
142
+ } catch {
143
+ return 0;
144
+ }
145
+ }
146
+
147
+ // ---------------------------------------------------------------------------
148
+ // Record a result
149
+ // ---------------------------------------------------------------------------
150
+
151
+ /**
152
+ * Append one history entry for a completed tool call.
153
+ * Best-effort — never throws.
154
+ *
155
+ * @param tool Tool name from PostToolUse payload.
156
+ * @param content String representation of the result (for size + sha8).
157
+ * @param sessionId Override for testing.
158
+ * @param homeDir Override for testing.
159
+ * @param now Override for testing.
160
+ */
161
+ export function recordResult(
162
+ tool: string,
163
+ content: string,
164
+ sessionId: string = resolveSessionId(),
165
+ homeDir: string = home(),
166
+ now: number = Date.now(),
167
+ ): void {
168
+ if (process.env.ASHLR_SESSION_LOG === "0") return;
169
+
170
+ try {
171
+ const dir = historyDir(homeDir);
172
+ mkdirSync(dir, { recursive: true });
173
+
174
+ const turn = readCurrentTurn(sessionId, homeDir);
175
+ const entry: HistoryEntry = {
176
+ ts: now,
177
+ tool,
178
+ sizeBytes: Buffer.byteLength(content, "utf-8"),
179
+ contentSha8: sha8(content),
180
+ turn,
181
+ sessionId,
182
+ };
183
+
184
+ appendFileSync(historyPath(sessionId, homeDir), JSON.stringify(entry) + "\n", "utf-8");
185
+ } catch {
186
+ // Best-effort — never block the tool call.
187
+ }
188
+ }
189
+
190
+ // ---------------------------------------------------------------------------
191
+ // Read and annotate history
192
+ // ---------------------------------------------------------------------------
193
+
194
+ /**
195
+ * Read all history entries for a session.
196
+ * Returns [] on any error.
197
+ */
198
+ export function readHistory(
199
+ sessionId: string = resolveSessionId(),
200
+ homeDir: string = home(),
201
+ ): HistoryEntry[] {
202
+ try {
203
+ const p = historyPath(sessionId, homeDir);
204
+ if (!existsSync(p)) return [];
205
+ const raw = readFileSync(p, "utf-8");
206
+ return raw
207
+ .split("\n")
208
+ .filter((l) => l.trim().length > 0)
209
+ .map((l) => {
210
+ try { return JSON.parse(l) as HistoryEntry; } catch { return null; }
211
+ })
212
+ .filter((e): e is HistoryEntry => e !== null);
213
+ } catch {
214
+ return [];
215
+ }
216
+ }
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Freshness computation
220
+ // ---------------------------------------------------------------------------
221
+
222
+ /**
223
+ * Compute freshness score given the number of turns since emission.
224
+ *
225
+ * 0–4 turns → 1.0
226
+ * 5–14 turns → 0.5
227
+ * 15+ turns → 0.2
228
+ */
229
+ export function freshnessScore(turnDelta: number): number {
230
+ if (turnDelta < FRESH_TURNS) return 1.0;
231
+ if (turnDelta < 15) return STALE_FRESHNESS;
232
+ return VERY_STALE_FRESHNESS;
233
+ }
234
+
235
+ /**
236
+ * Annotate all history entries with freshness metadata relative to currentTurn.
237
+ */
238
+ export function annotateHistory(
239
+ entries: HistoryEntry[],
240
+ currentTurn: number,
241
+ ): AnnotatedEntry[] {
242
+ return entries.map((e) => {
243
+ const turnDelta = Math.max(0, currentTurn - e.turn);
244
+ const freshness = freshnessScore(turnDelta);
245
+ return {
246
+ ...e,
247
+ turnDelta,
248
+ freshness,
249
+ isStale: turnDelta >= STALE_TURN_THRESHOLD,
250
+ };
251
+ });
252
+ }
253
+
254
+ // ---------------------------------------------------------------------------
255
+ // Stale byte total
256
+ // ---------------------------------------------------------------------------
257
+
258
+ /**
259
+ * Compute total byte size of stale results in the current session.
260
+ * Used by the adaptive nudge and /ashlr-compact.
261
+ */
262
+ export function staleByteTotal(
263
+ sessionId: string = resolveSessionId(),
264
+ homeDir: string = home(),
265
+ ): { staleBytes: number; staleResults: number; sessionTurnCount: number } {
266
+ try {
267
+ const entries = readHistory(sessionId, homeDir);
268
+ const currentTurn = entries.length;
269
+ const annotated = annotateHistory(entries, currentTurn);
270
+ const stale = annotated.filter((e) => e.isStale);
271
+ return {
272
+ staleBytes: stale.reduce((acc, e) => acc + e.sizeBytes, 0),
273
+ staleResults: stale.length,
274
+ sessionTurnCount: currentTurn,
275
+ };
276
+ } catch {
277
+ return { staleBytes: 0, staleResults: 0, sessionTurnCount: 0 };
278
+ }
279
+ }