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,533 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * ashlr genome auto-consolidate — drains `.ashlrcode/genome/proposals.jsonl`
4
+ * into the genome section files it targets, and truncates the queue when
5
+ * done.
6
+ *
7
+ * Behavior:
8
+ * - Gate on ≥ 3 pending proposals. Fewer → no-op exit 0.
9
+ * - Group by section path, merge contents as bulleted append onto the
10
+ * existing section body (simple offline-deterministic fallback).
11
+ * - Log every run to `~/.ashlr/genome-consolidation.log`.
12
+ * - ASHLR_GENOME_AUTO=0 → silent exit 0.
13
+ *
14
+ * Usage:
15
+ * bun run scripts/genome-auto-consolidate.ts --dir <path>
16
+ *
17
+ * The `consolidateProposals` import exists in the import graph so the
18
+ * plugin's dependency on core-efficiency stays explicit; we invoke the
19
+ * deterministic fallback directly because core-efficiency's consolidator
20
+ * reads a different queue (`evolution/pending.jsonl`, owned by the MCP
21
+ * server). The fallback below is the "simple merge-as-bullets" path the
22
+ * spec allows when no LLMSummarizer is available.
23
+ */
24
+
25
+ import {
26
+ appendFileSync,
27
+ existsSync,
28
+ mkdirSync,
29
+ readFileSync,
30
+ writeFileSync,
31
+ } from "fs";
32
+ import { homedir } from "os";
33
+ import { dirname, join, resolve } from "path";
34
+ // Imported for API-surface parity with the MCP server; the auto-consolidator
35
+ // uses the direct-fallback path so it stays offline and deterministic.
36
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
37
+ import { consolidateProposals } from "@ashlr/core-efficiency/genome";
38
+ import { summarizeIfLarge as _summarizeIfLargeImpl, PROMPTS, type SummarizeResult, type SummarizeOpts } from "../servers/_summarize";
39
+
40
+ // Mutable hook container so tests can inject stubs:
41
+ // _hooks.summarizeIfLarge — controls LLM synthesis
42
+ // _hooks.isLlmReachable — controls reachability probe (stub to false in offline tests)
43
+ export const _hooks = {
44
+ summarizeIfLarge: _summarizeIfLargeImpl as (rawText: string, opts: SummarizeOpts) => Promise<SummarizeResult>,
45
+ isLlmReachable: isLlmReachable as () => Promise<boolean>,
46
+ };
47
+
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // LLM reachability probe
51
+ // ---------------------------------------------------------------------------
52
+
53
+ /**
54
+ * Check whether any configured LLM endpoint is reachable.
55
+ *
56
+ * Priority: ASHLR_LLM_URL > ANTHROPIC_API_KEY > OPENAI_API_KEY.
57
+ * Uses a 2-second AbortSignal timeout so a hung endpoint doesn't block
58
+ * consolidation. Returns false on any error (network, auth, DNS, etc.).
59
+ *
60
+ * Calling convention: this is called once per `applyFallback` invocation
61
+ * when `ASHLR_GENOME_LLM_SYNTHESIS` is not explicitly "0".
62
+ */
63
+ async function isLlmReachable(): Promise<boolean> {
64
+ // Explicit override: ASHLR_LLM_URL takes priority
65
+ const llmUrl = process.env["ASHLR_LLM_URL"];
66
+ if (llmUrl) {
67
+ try {
68
+ const res = await fetch(llmUrl, {
69
+ method: "HEAD",
70
+ signal: AbortSignal.timeout(2_000),
71
+ });
72
+ return res.ok || res.status < 500;
73
+ } catch {
74
+ return false;
75
+ }
76
+ }
77
+ // ANTHROPIC_API_KEY — probe the models endpoint
78
+ if (process.env["ANTHROPIC_API_KEY"]) {
79
+ try {
80
+ const res = await fetch("https://api.anthropic.com/v1/models", {
81
+ method: "GET",
82
+ headers: { "x-api-key": process.env["ANTHROPIC_API_KEY"]!, "anthropic-version": "2023-06-01" },
83
+ signal: AbortSignal.timeout(2_000),
84
+ });
85
+ // 401/403 means the API is reachable (key may be invalid, but network is up)
86
+ return res.status < 500;
87
+ } catch {
88
+ return false;
89
+ }
90
+ }
91
+ // OPENAI_API_KEY — probe the models endpoint
92
+ if (process.env["OPENAI_API_KEY"]) {
93
+ try {
94
+ const res = await fetch("https://api.openai.com/v1/models", {
95
+ method: "GET",
96
+ headers: { "Authorization": `Bearer ${process.env["OPENAI_API_KEY"]!}` },
97
+ signal: AbortSignal.timeout(2_000),
98
+ });
99
+ return res.status < 500;
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
104
+ return false;
105
+ }
106
+
107
+ interface Proposal {
108
+ id: string;
109
+ agentId: string;
110
+ section: string;
111
+ operation: "append" | "update" | "create";
112
+ content: string;
113
+ rationale: string;
114
+ timestamp: string;
115
+ generation: number;
116
+ }
117
+
118
+ interface AshlrConfig {
119
+ genomeAuto?: boolean;
120
+ }
121
+
122
+ const LOG_PATH = join(homedir(), ".ashlr", "genome-consolidation.log");
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Args
126
+ // ---------------------------------------------------------------------------
127
+
128
+ export function parseArgs(argv: string[]): { dir?: string } {
129
+ let dir: string | undefined;
130
+ for (let i = 0; i < argv.length; i++) {
131
+ const a = argv[i];
132
+ if (a === "--dir" && argv[i + 1]) dir = argv[++i];
133
+ }
134
+ return { dir };
135
+ }
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Auto-disable check
139
+ // ---------------------------------------------------------------------------
140
+
141
+ function isAutoEnabled(): boolean {
142
+ if (process.env.ASHLR_GENOME_AUTO === "0") return false;
143
+ const cfgPath = join(homedir(), ".ashlr", "config.json");
144
+ if (existsSync(cfgPath)) {
145
+ try {
146
+ const cfg = JSON.parse(readFileSync(cfgPath, "utf-8")) as AshlrConfig;
147
+ if (cfg.genomeAuto === false) return false;
148
+ } catch {
149
+ /* ignore */
150
+ }
151
+ }
152
+ return true;
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Proposal queue IO
157
+ // ---------------------------------------------------------------------------
158
+
159
+ function readProposals(path: string): Proposal[] {
160
+ if (!existsSync(path)) return [];
161
+ const raw = readFileSync(path, "utf-8");
162
+ const out: Proposal[] = [];
163
+ for (const line of raw.split("\n")) {
164
+ const t = line.trim();
165
+ if (!t) continue;
166
+ try {
167
+ const j = JSON.parse(t) as Proposal;
168
+ if (j && typeof j.section === "string" && typeof j.content === "string") {
169
+ out.push(j);
170
+ }
171
+ } catch {
172
+ /* skip malformed */
173
+ }
174
+ }
175
+ return out;
176
+ }
177
+
178
+ function sectionFilePath(genomeDir: string, section: string): string {
179
+ return join(genomeDir, section);
180
+ }
181
+
182
+ function bulletize(content: string): string {
183
+ const flat = content.replace(/\s+/g, " ").trim();
184
+ // Keep bullets reasonable length; append-only.
185
+ return `- ${flat}`;
186
+ }
187
+
188
+ /**
189
+ * Route target for a proposal aimed at `knowledge/discoveries.md`.
190
+ *
191
+ * discoveries.md is a curated human-readable file — the 2026-04-21 audit
192
+ * found it was 66% bigger than the previous month's audit threshold,
193
+ * dominated by raw JSON/diff blobs captured by the auto-propose hook.
194
+ *
195
+ * This classifier downgrades proposals that look like auto-observed noise
196
+ * (JSON-wrapped tool results, raw file listings, git diff hunks) to a
197
+ * sibling `discoveries-auto.md` sink which is not indexed in the manifest
198
+ * and therefore does not participate in retrieval.
199
+ *
200
+ * Heuristics are conservative: a proposal is routed to the sink only when
201
+ * it clearly matches a known noise shape. When in doubt, keep in the
202
+ * curated file.
203
+ */
204
+ export function isNoiseProposal(content: string): boolean {
205
+ const flat = content.trim();
206
+ if (flat.length === 0) return false;
207
+
208
+ // JSON blob captured from a tool result — the dominant noise shape.
209
+ // Tool-result JSON typically starts with `{"stdout":`, `{"type":`,
210
+ // `{"filePath":`, `{"mode":`, `{"result":`, or similar.
211
+ if (/^\{["']?(stdout|stderr|type|filePath|mode|result|numFiles|file)["']?\s*:/.test(flat)) {
212
+ return true;
213
+ }
214
+
215
+ // Raw file listings: many lines that all look like absolute paths.
216
+ // `.tsv`-ish output (e.g. from `ls`, `find`, or glob tool).
217
+ const lines = flat.split("\n").filter((l) => l.trim().length > 0);
218
+ if (lines.length >= 3) {
219
+ const pathish = lines.filter((l) => /^\/[\w./\-@]+$/.test(l.trim())).length;
220
+ if (pathish / lines.length >= 0.7) return true;
221
+ }
222
+
223
+ // Unified-diff fragments captured from Bash output — lots of +/- lines or
224
+ // hunk headers.
225
+ const diffish = lines.filter((l) => /^(\+\+\+ |--- |@@ |diff --git|\+[^+]|-[^-])/.test(l)).length;
226
+ if (lines.length >= 5 && diffish / lines.length >= 0.6) return true;
227
+
228
+ return false;
229
+ }
230
+
231
+ /**
232
+ * Section path a proposal should actually land in. Returns the proposal's
233
+ * declared section unless it targets `discoveries.md` and looks like auto
234
+ * noise — in which case it's rerouted to `discoveries-auto.md`.
235
+ */
236
+ export function routeSectionForProposal(section: string, content: string): string {
237
+ if (section === "knowledge/discoveries.md" && isNoiseProposal(content)) {
238
+ return "knowledge/discoveries-auto.md";
239
+ }
240
+ return section;
241
+ }
242
+
243
+ /**
244
+ * Jaccard similarity on the token sets of two strings. Used as a cheap
245
+ * novelty gate so we don't re-append bullets that duplicate existing
246
+ * section content. Stopwords-agnostic — relies on the length floor in
247
+ * `tokenize` to skip grammatical filler.
248
+ */
249
+ function tokenize(text: string): Set<string> {
250
+ const tokens = new Set<string>();
251
+ for (const raw of text.toLowerCase().split(/[^a-z0-9_\-]+/)) {
252
+ if (raw.length < 4) continue;
253
+ tokens.add(raw);
254
+ }
255
+ return tokens;
256
+ }
257
+
258
+ function jaccard(a: Set<string>, b: Set<string>): number {
259
+ if (a.size === 0 || b.size === 0) return 0;
260
+ let intersection = 0;
261
+ for (const token of a) if (b.has(token)) intersection++;
262
+ const union = a.size + b.size - intersection;
263
+ return union === 0 ? 0 : intersection / union;
264
+ }
265
+
266
+ /**
267
+ * Similarity threshold above which a proposed bullet is considered a
268
+ * duplicate of an existing line. 0.6 is deliberately conservative — high
269
+ * enough to catch near-identical auto-observations but low enough that
270
+ * proposals genuinely rephrasing prior knowledge still land.
271
+ */
272
+ const DUPLICATE_JACCARD_THRESHOLD = 0.6;
273
+
274
+ /**
275
+ * Return existing section content split into non-empty trimmed lines —
276
+ * used as the baseline for the duplicate-bullet check below.
277
+ */
278
+ function readExistingLines(path: string): string[] {
279
+ if (!existsSync(path)) return [];
280
+ try {
281
+ return readFileSync(path, "utf-8")
282
+ .split("\n")
283
+ .map((l) => l.trim())
284
+ .filter((l) => l.length > 0);
285
+ } catch {
286
+ return [];
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Fallback: append bullets grouped by section to the section file.
292
+ *
293
+ * v1.13 adds a novelty gate — before appending, every proposal's content
294
+ * is compared (Jaccard token-overlap) against existing lines in the
295
+ * target section file. Proposals whose top similarity exceeds
296
+ * {@link DUPLICATE_JACCARD_THRESHOLD} are dropped so the file stops
297
+ * accumulating near-duplicate auto-observations over time.
298
+ *
299
+ * v1.15 (Phase 5.3): when ASHLR_GENOME_LLM_SYNTHESIS=1, raw proposal
300
+ * content is routed through summarizeIfLarge with the genome_synthesize
301
+ * prompt before Jaccard dedup. The LLM either returns 1-3 curated bullets
302
+ * or a {novel:false} sentinel that drops the whole batch. Falls back to
303
+ * the deterministic path on LLM error.
304
+ */
305
+ export async function applyFallback(
306
+ genomeDir: string,
307
+ proposals: Proposal[],
308
+ ): Promise<number> {
309
+ // LLM synthesis is enabled by default when any LLM endpoint is reachable
310
+ // (ASHLR_LLM_URL, ANTHROPIC_API_KEY, or OPENAI_API_KEY) AND
311
+ // ASHLR_GENOME_LLM_SYNTHESIS is not explicitly "0" (opt-out).
312
+ // Falls back to deterministic Jaccard path when no endpoint is reachable.
313
+ const synthEnv = process.env["ASHLR_GENOME_LLM_SYNTHESIS"];
314
+ const llmSynthesis =
315
+ synthEnv !== "0" && (synthEnv === "1" || (await _hooks.isLlmReachable()));
316
+
317
+ const bySection = new Map<string, Proposal[]>();
318
+ for (const p of proposals) {
319
+ const target = routeSectionForProposal(p.section, p.content);
320
+ const g = bySection.get(target) ?? [];
321
+ g.push(p);
322
+ bySection.set(target, g);
323
+ }
324
+
325
+ let applied = 0;
326
+ const stamp = new Date().toISOString().slice(0, 10);
327
+
328
+ for (const [section, items] of bySection) {
329
+ const target = sectionFilePath(genomeDir, section);
330
+ mkdirSync(dirname(target), { recursive: true });
331
+ const existing = existsSync(target) ? readFileSync(target, "utf-8") : "";
332
+ const existingLines = readExistingLines(target);
333
+ const existingTokenSets = existingLines.map((line) => tokenize(line));
334
+
335
+ let acceptedBullets: string[] = [];
336
+
337
+ if (llmSynthesis) {
338
+ // LLM synthesis path: batch all raw proposal content and send to LLM.
339
+ const rawBatch = items.map((item) => item.content).join("\n");
340
+ let llmBullets: string[] | null = null;
341
+ try {
342
+ const result = await _hooks.summarizeIfLarge(rawBatch, {
343
+ systemPrompt: PROMPTS.genome_synthesize,
344
+ toolName: "genome-consolidate",
345
+ thresholdBytes: 200,
346
+ });
347
+ const llmText = result.text.trim();
348
+ // Strip any trailing bypass hint line added by summarizeIfLarge.
349
+ const lines = llmText.split("\n").filter((l) => l.trim().length > 0);
350
+ const contentLines = lines.filter((l) => !l.startsWith("[ashlr "));
351
+ // Check for novel:false sentinel — first non-whitespace line.
352
+ const firstLine = contentLines[0]?.trim() ?? "";
353
+ let parsed: unknown;
354
+ try { parsed = JSON.parse(firstLine); } catch { parsed = null; }
355
+ if (
356
+ parsed !== null &&
357
+ typeof parsed === "object" &&
358
+ (parsed as Record<string, unknown>)["novel"] === false
359
+ ) {
360
+ const reason = (parsed as Record<string, unknown>)["reason"] ?? "no reason given";
361
+ logLine(
362
+ `${new Date().toISOString()} section=${section} llm-synthesis=novel:false reason=${String(reason)}`,
363
+ );
364
+ continue; // drop entire batch for this section
365
+ }
366
+ // Extract bullet lines (lines starting with "- ").
367
+ llmBullets = contentLines.filter((l) => l.startsWith("- "));
368
+ if (llmBullets.length === 0 && contentLines.length > 0) {
369
+ // LLM returned content but not in bullet form — wrap each line.
370
+ llmBullets = contentLines.map((l) => `- ${l.replace(/^[-*]\s*/, "")}`);
371
+ }
372
+ } catch {
373
+ // LLM error — fall through to deterministic path.
374
+ llmBullets = null;
375
+ }
376
+
377
+ if (llmBullets !== null) {
378
+ // Run Jaccard dedup on LLM-synthesized bullets against existing content.
379
+ const acceptedBulletSets: Set<string>[] = [];
380
+ for (const bullet of llmBullets) {
381
+ const tokens = tokenize(bullet);
382
+ const isDuplicate =
383
+ existingTokenSets.some((et) => jaccard(tokens, et) >= DUPLICATE_JACCARD_THRESHOLD) ||
384
+ acceptedBulletSets.some((at) => jaccard(tokens, at) >= DUPLICATE_JACCARD_THRESHOLD);
385
+ if (isDuplicate) continue;
386
+ acceptedBulletSets.push(tokens);
387
+ acceptedBullets.push(bullet);
388
+ }
389
+ } else {
390
+ // Graceful degradation: LLM errored, run deterministic bulletize path.
391
+ const acceptedBulletSets: Set<string>[] = [];
392
+ for (const item of items) {
393
+ const bullet = bulletize(item.content);
394
+ const tokens = tokenize(bullet);
395
+ const isDuplicate =
396
+ existingTokenSets.some((et) => jaccard(tokens, et) >= DUPLICATE_JACCARD_THRESHOLD) ||
397
+ acceptedBulletSets.some((at) => jaccard(tokens, at) >= DUPLICATE_JACCARD_THRESHOLD);
398
+ if (isDuplicate) continue;
399
+ acceptedBulletSets.push(tokens);
400
+ acceptedBullets.push(bullet);
401
+ }
402
+ }
403
+ } else {
404
+ // Deterministic path (default): Jaccard dedup on bulletized proposals.
405
+ const acceptedBulletSets: Set<string>[] = [];
406
+ for (const item of items) {
407
+ const bullet = bulletize(item.content);
408
+ const tokens = tokenize(bullet);
409
+ const isDuplicate =
410
+ existingTokenSets.some((et) => jaccard(tokens, et) >= DUPLICATE_JACCARD_THRESHOLD) ||
411
+ acceptedBulletSets.some((at) => jaccard(tokens, at) >= DUPLICATE_JACCARD_THRESHOLD);
412
+ if (isDuplicate) continue;
413
+ acceptedBulletSets.push(tokens);
414
+ acceptedBullets.push(bullet);
415
+ }
416
+ }
417
+
418
+ if (acceptedBullets.length === 0) continue;
419
+
420
+ const header = `\n\n## Auto-observations · ${stamp}\n`;
421
+ const combined =
422
+ (existing.endsWith("\n") || existing === "" ? existing : existing + "\n") +
423
+ header +
424
+ acceptedBullets.join("\n") +
425
+ "\n";
426
+ writeFileSync(target, combined, "utf-8");
427
+ applied += acceptedBullets.length;
428
+ }
429
+ return applied;
430
+ }
431
+
432
+ // ---------------------------------------------------------------------------
433
+ // Logging
434
+ // ---------------------------------------------------------------------------
435
+
436
+ function logLine(msg: string): void {
437
+ try {
438
+ mkdirSync(dirname(LOG_PATH), { recursive: true });
439
+ appendFileSync(LOG_PATH, msg + "\n", "utf-8");
440
+ } catch {
441
+ /* ignore */
442
+ }
443
+ }
444
+
445
+ // ---------------------------------------------------------------------------
446
+ // Main
447
+ // ---------------------------------------------------------------------------
448
+
449
+ export interface ConsolidateResult {
450
+ ran: boolean;
451
+ reason: string;
452
+ before: number;
453
+ after: number;
454
+ applied: number;
455
+ }
456
+
457
+ export async function runConsolidate(dir: string): Promise<ConsolidateResult> {
458
+ const cwd = resolve(dir);
459
+ const genomeDir = join(cwd, ".ashlrcode", "genome");
460
+ if (!existsSync(genomeDir)) {
461
+ return { ran: false, reason: "no-genome", before: 0, after: 0, applied: 0 };
462
+ }
463
+ const proposalsPath = join(genomeDir, "proposals.jsonl");
464
+ const before = readProposals(proposalsPath);
465
+ if (before.length < 3) {
466
+ return {
467
+ ran: false,
468
+ reason: "below-threshold",
469
+ before: before.length,
470
+ after: before.length,
471
+ applied: 0,
472
+ };
473
+ }
474
+
475
+ let applied = 0;
476
+ try {
477
+ applied = await applyFallback(genomeDir, before);
478
+ } catch (e) {
479
+ const msg = e instanceof Error ? e.message : String(e);
480
+ logLine(
481
+ `${new Date().toISOString()} cwd=${cwd} error=${msg} before=${before.length}`,
482
+ );
483
+ return {
484
+ ran: false,
485
+ reason: "apply-failed",
486
+ before: before.length,
487
+ after: before.length,
488
+ applied: 0,
489
+ };
490
+ }
491
+
492
+ // Truncate queue.
493
+ try {
494
+ writeFileSync(proposalsPath, "", "utf-8");
495
+ } catch {
496
+ /* ignore */
497
+ }
498
+
499
+ logLine(
500
+ `${new Date().toISOString()} cwd=${cwd} before=${before.length} after=0 applied=${applied}`,
501
+ );
502
+
503
+ return {
504
+ ran: true,
505
+ reason: "ok",
506
+ before: before.length,
507
+ after: 0,
508
+ applied,
509
+ };
510
+ }
511
+
512
+ async function main(): Promise<void> {
513
+ if (!isAutoEnabled()) {
514
+ process.exit(0);
515
+ }
516
+ const { dir } = parseArgs(process.argv.slice(2));
517
+ const target = dir ?? process.env.PROJECT_ROOT ?? process.cwd();
518
+ try {
519
+ const result = await runConsolidate(target);
520
+ if (result.ran) {
521
+ process.stdout.write(
522
+ `ashlr-genome: consolidated ${result.applied} proposal${result.applied === 1 ? "" : "s"} into ${target}/.ashlrcode/genome/\n`,
523
+ );
524
+ }
525
+ } catch {
526
+ /* never block */
527
+ }
528
+ process.exit(0);
529
+ }
530
+
531
+ if (import.meta.main) {
532
+ await main();
533
+ }