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,68 @@
1
+ /**
2
+ * pipe-server-handlers — registers ashlr__pipe into the shared tool registry.
3
+ *
4
+ * ROLLOUT FLAG: the tool is only registered when ASHLR_PIPE_ENABLE=1.
5
+ * When the flag is absent (the default), this module runs as a no-op so the
6
+ * registered tool count stays at 40 and `bun run smoke:tools` keeps passing.
7
+ *
8
+ * To enable for testing:
9
+ * ASHLR_PIPE_ENABLE=1 bun test __tests__/pipe-server.test.ts
10
+ *
11
+ * The conditional registration is evaluated at import time (side-effect
12
+ * module); the router imports this file unconditionally via _router-handlers.ts.
13
+ */
14
+
15
+ import { registerTool, toErrorResult, type ToolCallContext, type ToolResult } from "./_tool-base";
16
+ import { ashlrPipe, type PipeArgs } from "./pipe-server";
17
+
18
+ // Guard: only register when the rollout flag is set.
19
+ if (process.env.ASHLR_PIPE_ENABLE === "1") {
20
+ registerTool({
21
+ name: "ashlr__pipe",
22
+ description:
23
+ "[EXPERIMENTAL] Executes model-authored JS code server-side — off by default, enable with ASHLR_PIPE_ENABLE=1. " +
24
+ "Run a JS expression that calls ctx.{grep,read,ls,glob} internally — " +
25
+ "intermediate results NEVER enter context, only the return value does. " +
26
+ "Typical 80–95% token savings vs calling tools individually. " +
27
+ "ctx methods accept the same args as the ashlr__ tools they wrap. " +
28
+ "ctx.bash requires the additional ASHLR_PIPE_ALLOW_BASH=1 flag. " +
29
+ "Example: ctx.grep({pattern:'TODO',cwd:'.'}) returns the grep text as a string.",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ expr: {
34
+ type: "string",
35
+ description:
36
+ "Async function body (≤2000 chars). Receives `ctx` with " +
37
+ "grep/read/ls/glob methods (always available) and bash (requires ASHLR_PIPE_ALLOW_BASH=1). " +
38
+ "Return value is serialized as JSON. " +
39
+ "Blocked tokens: process, Bun, require, import(, globalThis, eval, fetch(, etc.",
40
+ },
41
+ cwd: {
42
+ type: "string",
43
+ description: "Working directory for cwd-relative tool calls (default: project root).",
44
+ },
45
+ timeout_ms: {
46
+ type: "number",
47
+ description: "Execution timeout in ms (default 10000, hard max 30000).",
48
+ },
49
+ max_output_bytes: {
50
+ type: "number",
51
+ description: "Truncate output to this many bytes (default 4096).",
52
+ },
53
+ },
54
+ required: ["expr"],
55
+ },
56
+ handler: async (
57
+ args: Record<string, unknown>,
58
+ ctx: ToolCallContext,
59
+ ): Promise<ToolResult> => {
60
+ try {
61
+ const result = await ashlrPipe(args as unknown as PipeArgs, ctx);
62
+ return { content: [{ type: "text", text: result.text }] };
63
+ } catch (err) {
64
+ return toErrorResult("ashlr__pipe error", err);
65
+ }
66
+ },
67
+ });
68
+ }
@@ -0,0 +1,300 @@
1
+ /**
2
+ * pipe-server — core logic for ashlr__pipe.
3
+ *
4
+ * ashlr__pipe lets the model run a multi-step JavaScript expression that
5
+ * calls grep/read/bash/ls/glob internally. Intermediate results NEVER enter
6
+ * the model context — only the expression's return value does. This yields
7
+ * 80–95% token savings vs calling the same tools individually.
8
+ *
9
+ * ROLLOUT: tool is only registered when ASHLR_PIPE_ENABLE=1 (off by default
10
+ * for v1.34). See pipe-server-handlers.ts for the registration guard.
11
+ *
12
+ * _noAccounting mechanism: ashlr__pipe calls intermediate tools via getTool()
13
+ * handler dispatch. Those handlers internally call recordSaving(). To prevent
14
+ * double-counting, pipe runs each intermediate call inside
15
+ * _withSuppressedAccounting() (AsyncLocalStorage-scoped) so the inner
16
+ * recordSaving() no-ops. The pipe then records ONE aggregate saving at the end
17
+ * (rawBytes = sum of intermediate result sizes, compactBytes = final output).
18
+ * Scoping is async-local, so a pipe in flight never suppresses a concurrent
19
+ * unrelated tool call.
20
+ */
21
+
22
+ import { getTool, type ToolCallContext } from "./_tool-base";
23
+ import { recordSaving, _withSuppressedAccounting } from "./_stats";
24
+ import { clampToCwd } from "./_cwd-clamp";
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Constants
28
+ // ---------------------------------------------------------------------------
29
+
30
+ const MAX_EXPR_LENGTH = 2000;
31
+ const DEFAULT_TIMEOUT_MS = 10_000;
32
+ const MAX_TIMEOUT_MS = 30_000;
33
+ const DEFAULT_MAX_OUTPUT_BYTES = 4096;
34
+ /** Per-intermediate-result heap guard: 64 KB */
35
+ const INTERMEDIATE_CAP_BYTES = 64 * 1024;
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Security deny-list
39
+ //
40
+ // Defense-in-depth: reject expressions containing any of these tokens BEFORE
41
+ // executing. This is not a sandbox — it's a best-effort guardrail to prevent
42
+ // the most obvious misuse patterns (process exfiltration, dynamic import,
43
+ // prototype pollution, timing attacks). The AsyncFunction constructor already
44
+ // prevents direct access to module scope and closures; the deny-list adds a
45
+ // second layer for runtime globals that ARE accessible via the global object.
46
+ // ---------------------------------------------------------------------------
47
+
48
+ const DENY_TOKENS = [
49
+ "process",
50
+ "Bun",
51
+ "require",
52
+ "import(",
53
+ "globalThis",
54
+ "__proto__",
55
+ "constructor[",
56
+ "eval",
57
+ "Function(",
58
+ "setTimeout",
59
+ "setInterval",
60
+ "fetch(",
61
+ ];
62
+
63
+ export interface PipeArgs {
64
+ expr: string;
65
+ cwd?: string;
66
+ timeout_ms?: number;
67
+ max_output_bytes?: number;
68
+ }
69
+
70
+ export interface PipeResult {
71
+ text: string;
72
+ /** Accumulated byte length of all intermediate results (for accounting). */
73
+ rawIntermediate: number;
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Deadline helper
78
+ // ---------------------------------------------------------------------------
79
+
80
+ function makeDeadline(ms: number): Promise<never> {
81
+ return new Promise((_, reject) =>
82
+ // Using the raw global setTimeout here is intentional — this is the
83
+ // internal timeout mechanism, NOT user-supplied code. The deny-list
84
+ // blocks "setTimeout" only in user expressions (expr).
85
+ (globalThis as unknown as { setTimeout: (fn: () => void, ms: number) => void }).setTimeout(
86
+ () => reject(new Error(`ashlr__pipe: timed out after ${ms}ms`)),
87
+ ms,
88
+ ),
89
+ );
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Build ctx proxy
94
+ // ---------------------------------------------------------------------------
95
+
96
+ /**
97
+ * Build the `ctx` object passed into the user's expression.
98
+ *
99
+ * Each method (grep/read/bash/ls/glob) delegates to the corresponding
100
+ * registered tool handler. Around each call, _setSuppressAccounting(true)
101
+ * prevents the inner handler from recording savings — the pipe records ONE
102
+ * aggregate at the end.
103
+ *
104
+ * Security design — two-tier ctx:
105
+ *
106
+ * By default `ctx` exposes ONLY read-only, non-shell tools: grep, read, ls,
107
+ * glob. These tools cannot mutate the filesystem or spawn arbitrary subprocesses.
108
+ *
109
+ * `ctx.bash` is included ONLY when ASHLR_PIPE_ALLOW_BASH=1 is set. Shell
110
+ * access is a separate, narrower opt-in so that enabling the pipe feature
111
+ * (ASHLR_PIPE_ENABLE=1) does NOT automatically expose unrestricted shell
112
+ * execution to model-authored expressions. The deny-list is best-effort, not
113
+ * a true sandbox — keeping bash out of the default ctx is the primary defence.
114
+ *
115
+ * Returns: `{ proxy, getIntermediate }` where `getIntermediate()` returns
116
+ * the running byte total of all intermediate results.
117
+ */
118
+ function buildCtx(parentCtx: ToolCallContext): {
119
+ proxy: Record<string, (args: unknown) => Promise<string>>;
120
+ getIntermediate: () => number;
121
+ } {
122
+ let intermediateBytes = 0;
123
+
124
+ async function callTool(name: string, args: Record<string, unknown>): Promise<string> {
125
+ const handler = getTool(name);
126
+ if (!handler) throw new Error(`ashlr__pipe: tool "${name}" is not registered`);
127
+
128
+ // Suppress savings accounting for this intermediate call, scoped to this
129
+ // call's async tree only (AsyncLocalStorage). Concurrent unrelated tool
130
+ // calls are never affected. The pipe records ONE aggregate at the end.
131
+ const result = await _withSuppressedAccounting(() => handler.handler(args, parentCtx));
132
+
133
+ const text = result.content.map((c) => c.text).join("");
134
+
135
+ // Cap each intermediate result to INTERMEDIATE_CAP_BYTES (heap guard).
136
+ const capped =
137
+ text.length > INTERMEDIATE_CAP_BYTES
138
+ ? text.slice(0, INTERMEDIATE_CAP_BYTES) +
139
+ "\n[ashlr__pipe: intermediate result capped at 64KB]"
140
+ : text;
141
+
142
+ intermediateBytes += Buffer.byteLength(capped, "utf8");
143
+ return capped;
144
+ }
145
+
146
+ // Read-only tools are always available in ctx.
147
+ const proxy: Record<string, (args: unknown) => Promise<string>> = {
148
+ grep: (args: unknown) => callTool("ashlr__grep", args as Record<string, unknown>),
149
+ read: (args: unknown) => callTool("ashlr__read", args as Record<string, unknown>),
150
+ ls: (args: unknown) => callTool("ashlr__ls", args as Record<string, unknown>),
151
+ glob: (args: unknown) => callTool("ashlr__glob", args as Record<string, unknown>),
152
+ };
153
+
154
+ // ctx.bash is an opt-in: requires ASHLR_PIPE_ALLOW_BASH=1 in addition to
155
+ // ASHLR_PIPE_ENABLE=1. When absent, calling ctx.bash() returns a clear
156
+ // error so the model knows why it failed and what flag to set.
157
+ if (process.env.ASHLR_PIPE_ALLOW_BASH === "1") {
158
+ proxy.bash = (args: unknown) => callTool("ashlr__bash", args as Record<string, unknown>);
159
+ } else {
160
+ proxy.bash = async () => {
161
+ throw new Error(
162
+ "ctx.bash is disabled; set ASHLR_PIPE_ALLOW_BASH=1 to enable shell access in pipes",
163
+ );
164
+ };
165
+ }
166
+
167
+ return { proxy, getIntermediate: () => intermediateBytes };
168
+ }
169
+
170
+ // ---------------------------------------------------------------------------
171
+ // Main entry point
172
+ // ---------------------------------------------------------------------------
173
+
174
+ export async function ashlrPipe(args: PipeArgs, ctx: ToolCallContext): Promise<PipeResult> {
175
+ const { expr, cwd, timeout_ms, max_output_bytes } = args;
176
+
177
+ // --- Input validation ---
178
+
179
+ if (typeof expr !== "string" || expr.length === 0) {
180
+ throw new Error("ashlr__pipe: expr must be a non-empty string");
181
+ }
182
+
183
+ if (expr.length > MAX_EXPR_LENGTH) {
184
+ throw new Error(
185
+ `ashlr__pipe: expr exceeds ${MAX_EXPR_LENGTH} characters (got ${expr.length})`,
186
+ );
187
+ }
188
+
189
+ // Deny-list check — must happen BEFORE constructing/executing the function.
190
+ for (const token of DENY_TOKENS) {
191
+ if (expr.includes(token)) {
192
+ throw new Error(
193
+ `ashlr__pipe: expr contains disallowed token "${token}" — ` +
194
+ `process/Bun/require/import/globalThis/eval/fetch and similar globals are blocked`,
195
+ );
196
+ }
197
+ }
198
+
199
+ // Timeout validation.
200
+ const timeoutMs = (() => {
201
+ if (timeout_ms === undefined) return DEFAULT_TIMEOUT_MS;
202
+ if (!Number.isFinite(timeout_ms) || timeout_ms <= 0) {
203
+ throw new Error(
204
+ `ashlr__pipe: timeout_ms must be a positive finite number (got ${timeout_ms})`,
205
+ );
206
+ }
207
+ return Math.min(timeout_ms, MAX_TIMEOUT_MS);
208
+ })();
209
+
210
+ const maxOutputBytes = (() => {
211
+ if (max_output_bytes === undefined) return DEFAULT_MAX_OUTPUT_BYTES;
212
+ if (!Number.isFinite(max_output_bytes) || max_output_bytes <= 0) {
213
+ throw new Error(
214
+ `ashlr__pipe: max_output_bytes must be a positive finite number (got ${max_output_bytes})`,
215
+ );
216
+ }
217
+ return max_output_bytes;
218
+ })();
219
+
220
+ // cwd clamp — optional; if provided must be inside an allowed root.
221
+ if (cwd !== undefined) {
222
+ const clamp = clampToCwd(cwd, "ashlr__pipe");
223
+ if (!clamp.ok) {
224
+ throw new Error(clamp.message);
225
+ }
226
+ }
227
+
228
+ // --- Build the expression function ---
229
+ //
230
+ // Use AsyncFunction constructor, NOT eval. This creates a new function
231
+ // scope that does NOT capture the module's closures or `require`. The only
232
+ // binding available to the user expression is the explicit `ctx` argument.
233
+
234
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
235
+ const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor as new (
236
+ ...args: string[]
237
+ ) => (ctx: unknown) => Promise<unknown>;
238
+
239
+ // Force strict mode on the user body. In sloppy mode a Function-constructed
240
+ // function sees `this === globalThis` at call time, which would let an expr
241
+ // reach real globals via `this["pro"+"cess"]` and defeat the substring
242
+ // deny-list (token-splitting). Strict mode makes `this` undefined and turns
243
+ // implicit-global access into a ReferenceError — the deny-list is then a
244
+ // second layer, not the only one.
245
+ const fn = new AsyncFunction("ctx", `"use strict";\n${expr}`);
246
+
247
+ // --- Execute with deadline race ---
248
+
249
+ const { proxy, getIntermediate } = buildCtx(ctx);
250
+
251
+ let rawValue: unknown;
252
+ try {
253
+ rawValue = await Promise.race([fn(proxy), makeDeadline(timeoutMs)]);
254
+ } catch (err) {
255
+ const msg = err instanceof Error ? err.message : String(err);
256
+ throw new Error(msg);
257
+ }
258
+
259
+ // --- Serialize return value ---
260
+
261
+ let serialized: string;
262
+ try {
263
+ serialized = JSON.stringify(rawValue, null, 2) ?? "null";
264
+ } catch {
265
+ // Circular references or non-serializable values (BigInt, etc.).
266
+ serialized = "[non-serializable result]";
267
+ }
268
+
269
+ // --- Truncate to max_output_bytes ---
270
+
271
+ const outputBytes = Buffer.byteLength(serialized, "utf8");
272
+ let output = serialized;
273
+ if (outputBytes > maxOutputBytes) {
274
+ // Slice at byte boundary (Buffer) to avoid splitting a multi-byte codepoint.
275
+ output =
276
+ Buffer.from(serialized, "utf8").slice(0, maxOutputBytes).toString("utf8") +
277
+ "\n[ashlr__pipe: output truncated]";
278
+ }
279
+
280
+ // --- Savings accounting ---
281
+ //
282
+ // ONE aggregate recording for the entire pipe execution:
283
+ // rawBytes = sum of all intermediate result byte lengths
284
+ // (what the model would have seen calling tools individually)
285
+ // compactBytes = final output byte length (what the model actually sees)
286
+ //
287
+ // Intermediate ctx calls had _setSuppressAccounting(true) active so they
288
+ // called recordSaving but it returned 0 immediately — no double-counting.
289
+
290
+ const rawBytes = getIntermediate();
291
+ const compactBytes = Buffer.byteLength(output, "utf8");
292
+ if (rawBytes > 0) {
293
+ // Fire-and-forget; accounting errors must never surface to the caller.
294
+ recordSaving(rawBytes, compactBytes, "ashlr__pipe", {
295
+ sessionId: ctx.sessionId,
296
+ }).catch(() => undefined);
297
+ }
298
+
299
+ return { text: output, rawIntermediate: rawBytes };
300
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * read-server-handlers — registers ashlr__read into the shared tool registry.
3
+ */
4
+
5
+ import { registerTool, type ToolCallContext, type ToolResult } from "./_tool-base";
6
+ import { ashlrRead } from "./read-server";
7
+
8
+ registerTool({
9
+ name: "ashlr__read",
10
+ description:
11
+ "Read a file with snipCompact truncation (head + tail, elided middle). " +
12
+ "Use instead of native Read when the file is >2KB — avoids returning the " +
13
+ "full payload; compresses by preserving only the structurally significant " +
14
+ "head and tail; typical savings 60–90% on large files. " +
15
+ "Pass bypassSummary:true to get the full file when you need it.",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ path: { type: "string", description: "Absolute or cwd-relative file path" },
20
+ bypassSummary: {
21
+ type: "boolean",
22
+ description: "Skip LLM summarization, return snipCompact-truncated content (default: false)",
23
+ },
24
+ mode: {
25
+ type: "string",
26
+ enum: ["auto", "ast", "snip"],
27
+ description: "auto (default): use AST skeleton for large wired-lang files; ast: force skeleton; snip: force snipCompact head+tail",
28
+ },
29
+ },
30
+ required: ["path"],
31
+ },
32
+ handler: async (args: Record<string, unknown>, _ctx: ToolCallContext): Promise<ToolResult> => {
33
+ const text = await ashlrRead({
34
+ path: String(args.path ?? ""),
35
+ bypassSummary: args.bypassSummary === true,
36
+ mode: ["auto", "ast", "snip"].includes(String(args.mode ?? ""))
37
+ ? (args.mode as "auto" | "ast" | "snip")
38
+ : undefined,
39
+ });
40
+ return { content: [{ type: "text", text }] };
41
+ },
42
+ });
@@ -0,0 +1,206 @@
1
+ /**
2
+ * read-server — ashlr__read tool implementation.
3
+ *
4
+ * Owns the snipCompact + summarization pipeline for file reads.
5
+ * Uses _read-cache for per-session mtime-keyed caching.
6
+ */
7
+
8
+ import { readFile } from "fs/promises";
9
+ import { statSync } from "fs";
10
+ import {
11
+ estimateTokensFromString,
12
+ type Message,
13
+ snipCompact,
14
+ } from "@ashlr/core-efficiency";
15
+ import { summarizeIfLarge, PROMPTS, confidenceBadge, confidenceTier } from "./_summarize";
16
+ import { logEvent } from "./_events";
17
+ import { recordSaving } from "./_stats";
18
+ import { clampToCwd, primaryProjectRoot } from "./_cwd-clamp";
19
+ import { getCached, setCached, type ReadCacheEntry } from "./_read-cache";
20
+ import { schedulePrefetch, type PrefetchTier } from "./_prefetch";
21
+ import { resolveLanguage } from "./_ast-languages";
22
+ import { renderAstSkeleton } from "./_ast-skeleton";
23
+ import { isProSync } from "./_pro";
24
+
25
+ /**
26
+ * Minimum content byte-length before AST skeleton rendering is attempted.
27
+ * Below this threshold the full snipCompact path is cheaper.
28
+ */
29
+ const AST_THRESHOLD = 6144;
30
+
31
+ /**
32
+ * File extensions treated as code for the line-number-preservation path.
33
+ * When ashlr__read returns a snipCompact-truncated view of one of these files,
34
+ * every preserved line is prefixed with its original line number so Claude can
35
+ * cite `file:line` accurately even across the elided middle.
36
+ */
37
+ const CODE_EXTENSIONS = new Set([
38
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
39
+ ".py", ".go", ".rs", ".java", ".kt", ".kts",
40
+ ".rb", ".php", ".swift", ".cs", ".scala", ".cpp", ".c", ".h", ".hpp",
41
+ ".sh", ".bash", ".zsh", ".fish",
42
+ ".sql", ".graphql", ".proto", ".css", ".scss", ".html", ".vue", ".svelte",
43
+ ]);
44
+
45
+ function isCodeFile(path: string): boolean {
46
+ const m = path.match(/\.[a-zA-Z0-9]+$/);
47
+ if (!m) return false;
48
+ return CODE_EXTENSIONS.has(m[0].toLowerCase());
49
+ }
50
+
51
+ /**
52
+ * Prepend every line with its 1-based line number + ": " so that head/tail
53
+ * fragments surviving snipCompact still carry positional information.
54
+ */
55
+ function numberCodeLines(source: string): string {
56
+ const lines = source.split("\n");
57
+ const pad = String(lines.length).length;
58
+ return lines.map((line, i) => `${String(i + 1).padStart(pad, " ")}: ${line}`).join("\n");
59
+ }
60
+
61
+ export async function ashlrRead(input: { path: string; bypassSummary?: boolean; preserveLineNumbers?: boolean; mode?: "auto" | "ast" | "snip" }): Promise<string> {
62
+ const clamp = clampToCwd(input.path, "ashlr__read");
63
+ if (!clamp.ok) return clamp.message;
64
+ const abs = clamp.abs;
65
+
66
+ // Cache hit path: same absolute path + unchanged mtime → return cached result.
67
+ let mtimeMs = 0;
68
+ try {
69
+ mtimeMs = statSync(abs).mtimeMs;
70
+ const hit = getCached(abs);
71
+ if (hit && hit.mtimeMs === mtimeMs && input.bypassSummary !== true) {
72
+ // On a repeat read we would otherwise have re-paid the full source bytes.
73
+ // Credit the original-size saving again since the agent received zero new
74
+ // tokens of file content.
75
+ await recordSaving(hit.sourceBytes, 0, "ashlr__read");
76
+ return `(cached)\n${hit.result}`;
77
+ }
78
+ } catch {
79
+ // If stat fails (broken symlink, perms), fall through to the normal read
80
+ // path which will surface a descriptive error.
81
+ }
82
+
83
+ const content = await readFile(abs, "utf-8");
84
+
85
+ // ------------------------------------------------------------------
86
+ // AST skeleton path: for large wired-language files in auto/ast mode,
87
+ // return signatures-only skeleton instead of snipCompact head+tail.
88
+ // Falls back to snipCompact on ANY failure (null return from skeleton).
89
+ // ------------------------------------------------------------------
90
+ const mode = input.mode ?? "auto";
91
+ const wantAst =
92
+ input.bypassSummary !== true &&
93
+ mode !== "snip" &&
94
+ (mode === "ast" ||
95
+ (isCodeFile(abs) &&
96
+ content.length > AST_THRESHOLD &&
97
+ (() => {
98
+ const lang = resolveLanguage(abs);
99
+ return (
100
+ lang === "typescript" || lang === "tsx" || lang === "javascript"
101
+ );
102
+ })()));
103
+
104
+ if (wantAst) {
105
+ const skel = await renderAstSkeleton(abs, content).catch(() => null);
106
+ if (skel !== null && skel.length < content.length) {
107
+ await recordSaving(content.length, skel.length, "ashlr__read");
108
+ const skelBadgeOpts = {
109
+ toolName: "ashlr__read",
110
+ rawBytes: content.length,
111
+ outputBytes: skel.length,
112
+ fellBack: false,
113
+ extra: `ast-skeleton${mtimeMs > 0 ? ` · mtime=${mtimeMs}` : ""}`,
114
+ };
115
+ const skelBadge = confidenceBadge(skelBadgeOpts);
116
+ const skelNote =
117
+ "\n// [ast-skeleton] Signatures only. Use bypassSummary:true to get the full file.";
118
+ const skelResult = skel + skelNote + skelBadge;
119
+ if (mtimeMs > 0) {
120
+ setCached(abs, {
121
+ mtimeMs,
122
+ result: skelResult,
123
+ sourceBytes: content.length,
124
+ });
125
+ }
126
+ return skelResult;
127
+ }
128
+ // skeleton was null or not smaller → fall through to snipCompact
129
+ }
130
+
131
+ // For code files, prepend 1-based line numbers to every line before
132
+ // snipCompact runs so `file:line` citations survive truncation.
133
+ const preserveLineNumbers =
134
+ input.preserveLineNumbers ?? isCodeFile(abs);
135
+ const renderedContent = preserveLineNumbers
136
+ ? numberCodeLines(content)
137
+ : content;
138
+
139
+ // Wrap as a fake tool_result message so snipCompact has something to snip.
140
+ const msgs: Message[] = [
141
+ {
142
+ role: "user",
143
+ content: [
144
+ { type: "tool_result", tool_use_id: "ashlr-read", content: renderedContent },
145
+ ],
146
+ },
147
+ ];
148
+
149
+ const compact = snipCompact(msgs);
150
+ const block = (compact[0]!.content as { type: string; content: string }[])[0]!;
151
+ const out = (block as { content: string }).content;
152
+
153
+ if (!(renderedContent.length > out.length)) {
154
+ await logEvent("tool_noop", { tool: "ashlr__read", reason: "small-file" });
155
+ }
156
+ const summarizeInput = renderedContent.length > out.length ? renderedContent : out;
157
+ const summarized = await summarizeIfLarge(summarizeInput, {
158
+ toolName: "ashlr__read",
159
+ systemPrompt: PROMPTS.read,
160
+ bypass: input.bypassSummary === true,
161
+ });
162
+ const finalText = summarized.summarized || summarized.fellBack || input.bypassSummary ? summarized.text : out;
163
+ const finalBytes = summarized.summarized || summarized.fellBack ? summarized.outputBytes : out.length;
164
+ await recordSaving(content.length, finalBytes, "ashlr__read");
165
+
166
+ const badgeOpts = {
167
+ toolName: "ashlr__read",
168
+ rawBytes: content.length,
169
+ outputBytes: finalBytes,
170
+ fellBack: summarized.fellBack,
171
+ extra: mtimeMs > 0 ? `mtime=${mtimeMs}` : undefined,
172
+ };
173
+ if (confidenceTier(badgeOpts) === "low") {
174
+ await logEvent("tool_low_confidence_shipped", { tool: "ashlr__read", reason: "low-confidence" });
175
+ }
176
+ const badge = confidenceBadge(badgeOpts);
177
+ const finalTextWithBadge = finalText + badge;
178
+
179
+ // Cache the fully computed result for this (path, mtimeMs). Skip caching
180
+ // when bypassSummary was used — that's an opt-out path and shouldn't
181
+ // poison future non-bypass calls.
182
+ if (input.bypassSummary !== true && mtimeMs > 0) {
183
+ setCached(abs, { mtimeMs, result: finalTextWithBadge, sourceBytes: content.length });
184
+ }
185
+
186
+ // Fire-and-forget predictive prefetch — Q3 supporting pillar. Scans X's
187
+ // imports via lightweight regex and pre-caches the top-N neighbours so the
188
+ // next ashlr__read on any of them is instant.
189
+ //
190
+ // MUST NOT be awaited. setImmediate inside schedulePrefetch defers the work
191
+ // to the next tick, so the user's read response goes out first.
192
+ try {
193
+ const tier: PrefetchTier = isProSync() ? "pro" : "free";
194
+ // Pro tier is the conservative default — team plans get bumped via cap.
195
+ // schedulePrefetch clamps to per-tier cap regardless of what we pass.
196
+ void schedulePrefetch(abs, {
197
+ tier,
198
+ maxNeighbors: 10,
199
+ cwd: primaryProjectRoot(),
200
+ });
201
+ } catch {
202
+ // Never let prefetch failures touch the read path.
203
+ }
204
+
205
+ return finalTextWithBadge;
206
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * rename-file-server-handlers — registers ashlr__rename_file on the shared
3
+ * registry (Track A · v1.19).
4
+ *
5
+ * Companion to `ashlr__edit_structural`. That tool renames IDENTIFIERS
6
+ * (symbol names); this one renames MODULE PATHS (file locations + every
7
+ * import specifier that resolves to them).
8
+ */
9
+
10
+ import { registerTool, toErrorResult, type ToolCallContext, type ToolResult } from "./_tool-base";
11
+ import { ashlrRenameFile, type RenameFileArgs } from "./rename-file-server";
12
+ import { recordSaving } from "./_stats";
13
+
14
+ const ERR_PREFIX = "ashlr__rename_file error";
15
+
16
+ registerTool({
17
+ name: "ashlr__rename_file",
18
+ description:
19
+ "Rename (move) a source file AND update every import specifier in the project that resolved to it. " +
20
+ "Handles extension elision ('./foo' → './bar'), directory-index resolution ('./foo' when foo/index.ts), " +
21
+ "and skips bare package specifiers. Refuses if 'to' already exists, 'to's parent directory is missing, " +
22
+ "paths escape cwd, or either path is a binary file. Supports dryRun. Complements ashlr__edit_structural " +
23
+ "(which renames identifiers, not module paths).",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ from: { type: "string", description: "Current file path (absolute or cwd-relative). Must exist." },
28
+ to: { type: "string", description: "New file path (absolute or cwd-relative). Must NOT exist; parent dir must exist." },
29
+ dryRun: {
30
+ type: "boolean",
31
+ description: "If true, list planned edits without writing or renaming (default false).",
32
+ },
33
+ maxFiles: {
34
+ type: "number",
35
+ description: "Hard cap on importer candidate files (default 200). Beyond this the scan truncates with a warning.",
36
+ },
37
+ roots: {
38
+ type: "array",
39
+ items: { type: "string" },
40
+ description: "Limit the importer scan to these subdirectories (absolute or cwd-relative). Default: process.cwd().",
41
+ },
42
+ },
43
+ required: ["from", "to"],
44
+ },
45
+ handler: async (args: Record<string, unknown>, _ctx: ToolCallContext): Promise<ToolResult> => {
46
+ try {
47
+ const a = args as unknown as RenameFileArgs;
48
+ const text = await ashlrRenameFile(a);
49
+ // Savings accounting: the baseline is the byte-size of the importer
50
+ // source files we rewrote (what a naive "read every candidate and
51
+ // have the model stitch a multi_edit call" approach would have
52
+ // shipped). We don't know the per-file sizes from the summary alone,
53
+ // so approximate via the summary length itself against a nominal
54
+ // multiplier — compact-vs-raw accounting in this tool is a lower
55
+ // bound, not exact. Any positive saving is credited.
56
+ const compactBytes = Buffer.byteLength(text, "utf-8");
57
+ // Claim a conservative 2× multiplier so we don't inflate savings on
58
+ // zero-importer renames; on large renames the real savings are
59
+ // much higher. This is a floor — the router's recordSaving is
60
+ // additive, not a source of truth for per-call math.
61
+ await recordSaving(compactBytes * 2, compactBytes, "ashlr__rename_file");
62
+ return { content: [{ type: "text", text }] };
63
+ } catch (err) {
64
+ return toErrorResult(ERR_PREFIX, err);
65
+ }
66
+ },
67
+ });