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,386 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * cli-orchestrate-status.ts — inspect past `/ashlr-orchestrate` runs.
4
+ *
5
+ * Reads `~/.ashlr/orchestrations/<graphId>/result.json` files persisted by
6
+ * scripts/orchestrate-run.ts and renders either:
7
+ * - a table summarizing the last N runs (default), or
8
+ * - a per-node tree for a single run (when a graph-id is supplied), or
9
+ * - the raw JSON (when --json is passed).
10
+ *
11
+ * Pure / no-network / no-subprocess — safe to call from any shell.
12
+ *
13
+ * Usage:
14
+ * bun scripts/cli-orchestrate-status.ts # list last 10
15
+ * bun scripts/cli-orchestrate-status.ts <graph-id> # detail
16
+ * bun scripts/cli-orchestrate-status.ts --json [<graph-id>] # raw JSON
17
+ * bun scripts/cli-orchestrate-status.ts --last <N> # list last N
18
+ *
19
+ * Designed for dependency injection: tests pass overrides for HOME, stdout,
20
+ * and stderr so they can run in a tmp dir without touching the real ~/.ashlr/.
21
+ */
22
+
23
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
24
+ import { homedir } from "os";
25
+ import { join } from "path";
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Types — mirror the shape persisted by orchestrate-run.ts. We intentionally
29
+ // duplicate (rather than import) so this CLI keeps a narrow dependency graph
30
+ // and tests don't have to wire the executor's DI seams just to read a file.
31
+ // ---------------------------------------------------------------------------
32
+
33
+ export interface PersistedNodeResult {
34
+ id: string;
35
+ nodeId?: string;
36
+ ok: boolean;
37
+ durationMs: number;
38
+ tokens: number;
39
+ tokensUsed?: number;
40
+ output?: string;
41
+ error?: string;
42
+ }
43
+
44
+ export interface PersistedRunResult {
45
+ ok: boolean;
46
+ graphId: string;
47
+ startedAt: string;
48
+ finishedAt?: string;
49
+ totalDurationMs?: number;
50
+ totalTokens?: number;
51
+ totalTokensUsed?: number;
52
+ nodes?: PersistedNodeResult[];
53
+ nodeResults?: PersistedNodeResult[];
54
+ error?: string;
55
+ }
56
+
57
+ export interface PersistedGraphNode {
58
+ id: string;
59
+ agentKind?: string;
60
+ goal?: string;
61
+ scope?: string[];
62
+ deps?: string[];
63
+ }
64
+
65
+ export interface PersistedGraph {
66
+ id: string;
67
+ goal: string;
68
+ scope?: string;
69
+ tier?: string;
70
+ createdAt?: string;
71
+ nodes: PersistedGraphNode[];
72
+ }
73
+
74
+ export interface PersistedRecord {
75
+ graph: PersistedGraph;
76
+ result: PersistedRunResult;
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Arg parsing
81
+ // ---------------------------------------------------------------------------
82
+
83
+ export interface ParsedArgs {
84
+ graphId: string | null;
85
+ json: boolean;
86
+ last: number;
87
+ }
88
+
89
+ const DEFAULT_LAST = 10;
90
+
91
+ export function parseArgs(argv: string[]): ParsedArgs {
92
+ const out: ParsedArgs = { graphId: null, json: false, last: DEFAULT_LAST };
93
+ for (let i = 0; i < argv.length; i++) {
94
+ const a = argv[i];
95
+ if (a === "--json") {
96
+ out.json = true;
97
+ } else if (a === "--last") {
98
+ const v = argv[++i];
99
+ const n = Number.parseInt(v ?? "", 10);
100
+ if (Number.isFinite(n) && n > 0) out.last = n;
101
+ } else if (a && !a.startsWith("--")) {
102
+ out.graphId = a;
103
+ }
104
+ }
105
+ return out;
106
+ }
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Path traversal safety — graphId from CLI input is joined into a filesystem
110
+ // path, so it must be validated. We accept alphanumeric + hyphen + underscore
111
+ // only, with a sane length cap. Rejecting "." prevents `..`, `./`, and any
112
+ // absolute paths from sneaking through.
113
+ // ---------------------------------------------------------------------------
114
+
115
+ const SAFE_GRAPH_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
116
+
117
+ export function isSafeGraphId(s: string): boolean {
118
+ return SAFE_GRAPH_ID_RE.test(s);
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // Persistence root + record loading
123
+ // ---------------------------------------------------------------------------
124
+
125
+ function orchestrationsDir(home: string): string {
126
+ return join(home, ".ashlr", "orchestrations");
127
+ }
128
+
129
+ interface LoadOptions {
130
+ warn: (s: string) => void;
131
+ }
132
+
133
+ /**
134
+ * Read a single result.json. Returns null + emits a stderr warning if the
135
+ * file is missing OR malformed (caller can decide whether to skip or error).
136
+ */
137
+ export function loadRecord(
138
+ home: string,
139
+ graphId: string,
140
+ opts: LoadOptions,
141
+ ): PersistedRecord | null {
142
+ if (!isSafeGraphId(graphId)) {
143
+ opts.warn(`warning: skipping unsafe graph id ${JSON.stringify(graphId)}\n`);
144
+ return null;
145
+ }
146
+ const file = join(orchestrationsDir(home), graphId, "result.json");
147
+ if (!existsSync(file)) return null;
148
+ try {
149
+ const raw = readFileSync(file, "utf-8");
150
+ const parsed = JSON.parse(raw);
151
+ if (!parsed || typeof parsed !== "object") {
152
+ opts.warn(`warning: malformed result.json at ${file} (not an object)\n`);
153
+ return null;
154
+ }
155
+ if (!parsed.graph || !parsed.result) {
156
+ opts.warn(`warning: malformed result.json at ${file} (missing graph/result)\n`);
157
+ return null;
158
+ }
159
+ return parsed as PersistedRecord;
160
+ } catch (e) {
161
+ opts.warn(`warning: failed to read ${file}: ${(e as Error).message}\n`);
162
+ return null;
163
+ }
164
+ }
165
+
166
+ /**
167
+ * List every run directory under ~/.ashlr/orchestrations/ and load each.
168
+ * Skips malformed entries (with a stderr warning per skip).
169
+ */
170
+ export function loadAllRecords(home: string, opts: LoadOptions): PersistedRecord[] {
171
+ const dir = orchestrationsDir(home);
172
+ if (!existsSync(dir)) return [];
173
+ let names: string[] = [];
174
+ try {
175
+ names = readdirSync(dir, { withFileTypes: true })
176
+ .filter((d) => d.isDirectory())
177
+ .map((d) => d.name);
178
+ } catch (e) {
179
+ opts.warn(`warning: failed to read ${dir}: ${(e as Error).message}\n`);
180
+ return [];
181
+ }
182
+ const records: PersistedRecord[] = [];
183
+ for (const name of names) {
184
+ const rec = loadRecord(home, name, opts);
185
+ if (rec) records.push(rec);
186
+ }
187
+ return records;
188
+ }
189
+
190
+ // ---------------------------------------------------------------------------
191
+ // Rendering helpers
192
+ // ---------------------------------------------------------------------------
193
+
194
+ function totalTokensOf(rec: PersistedRecord): number {
195
+ const r = rec.result;
196
+ if (typeof r.totalTokensUsed === "number") return r.totalTokensUsed;
197
+ if (typeof r.totalTokens === "number") return r.totalTokens;
198
+ const nodes = r.nodeResults ?? r.nodes ?? [];
199
+ return nodes.reduce((s, n) => s + (n.tokens ?? n.tokensUsed ?? 0), 0);
200
+ }
201
+
202
+ function shortGoal(s: string | undefined, max = 48): string {
203
+ const v = (s ?? "").replace(/\s+/g, " ").trim();
204
+ if (v.length <= max) return v;
205
+ return v.slice(0, max - 1) + "…";
206
+ }
207
+
208
+ function shortId(s: string, max = 24): string {
209
+ if (s.length <= max) return s;
210
+ return s.slice(0, max - 1) + "…";
211
+ }
212
+
213
+ function pad(s: string, n: number): string {
214
+ return s.length >= n ? s : s + " ".repeat(n - s.length);
215
+ }
216
+
217
+ /**
218
+ * Render a flat table of runs, newest-first by `startedAt`. Limited to
219
+ * `limit` rows; if fewer records exist, all are shown.
220
+ */
221
+ export function renderList(records: PersistedRecord[], limit: number): string {
222
+ // Sort by startedAt DESC. Records missing startedAt sort last (empty string
223
+ // < any ISO timestamp).
224
+ const sorted = [...records].sort((a, b) => {
225
+ const ax = a.result.startedAt ?? "";
226
+ const bx = b.result.startedAt ?? "";
227
+ if (ax < bx) return 1;
228
+ if (ax > bx) return -1;
229
+ return 0;
230
+ });
231
+ const rows = sorted.slice(0, Math.max(0, limit));
232
+ const lines: string[] = [];
233
+ lines.push(
234
+ `${pad("graph-id", 24)} ${pad("ok", 6)} ${pad("startedAt", 22)} ${pad("tokens", 8)} goal`,
235
+ );
236
+ lines.push(
237
+ `${"-".repeat(24)} ${"-".repeat(6)} ${"-".repeat(22)} ${"-".repeat(8)} ${"-".repeat(24)}`,
238
+ );
239
+ for (const rec of rows) {
240
+ const id = shortId(rec.graph.id ?? rec.result.graphId ?? "?", 24);
241
+ const ok = rec.result.ok ? "ok" : "fail";
242
+ const started = (rec.result.startedAt ?? "").slice(0, 22);
243
+ const tokens = String(totalTokensOf(rec));
244
+ const goal = shortGoal(rec.graph.goal, 48);
245
+ lines.push(
246
+ `${pad(id, 24)} ${pad(ok, 6)} ${pad(started, 22)} ${pad(tokens, 8)} ${goal}`,
247
+ );
248
+ }
249
+ lines.push(
250
+ `\n${rows.length} of ${records.length} run${records.length === 1 ? "" : "s"} shown`,
251
+ );
252
+ return lines.join("\n") + "\n";
253
+ }
254
+
255
+ /**
256
+ * Render a detail view (tree-style) of a single run: graph metadata then
257
+ * per-node line entries.
258
+ */
259
+ export function renderDetail(rec: PersistedRecord): string {
260
+ const g = rec.graph;
261
+ const r = rec.result;
262
+ const nodes = r.nodeResults ?? r.nodes ?? [];
263
+ const lines: string[] = [];
264
+ lines.push(`Run ${g.id}`);
265
+ lines.push(` goal: ${shortGoal(g.goal, 80)}`);
266
+ lines.push(` tier: ${g.tier ?? "?"}`);
267
+ lines.push(` ok: ${r.ok}`);
268
+ lines.push(` startedAt: ${r.startedAt ?? "?"}`);
269
+ if (r.finishedAt) lines.push(` finishedAt: ${r.finishedAt}`);
270
+ if (typeof r.totalDurationMs === "number") lines.push(` duration: ${r.totalDurationMs}ms`);
271
+ lines.push(` tokens: ${totalTokensOf(rec)}`);
272
+ if (r.error) lines.push(` error: ${r.error}`);
273
+ lines.push(` nodes:`);
274
+ if (nodes.length === 0) {
275
+ lines.push(` (none)`);
276
+ } else {
277
+ // Build a quick lookup so we can show agentKind alongside each result.
278
+ const kindById = new Map<string, string>();
279
+ for (const gn of g.nodes ?? []) kindById.set(gn.id, gn.agentKind ?? "?");
280
+ const last = nodes.length - 1;
281
+ nodes.forEach((n, i) => {
282
+ const branch = i === last ? "└─" : "├─";
283
+ const tag = n.ok ? "[ok] " : "[fail]";
284
+ const kind = kindById.get(n.id) ?? "?";
285
+ const dur = `${n.durationMs}ms`;
286
+ const tokens = `${n.tokens ?? n.tokensUsed ?? 0} tokens`;
287
+ lines.push(` ${branch} ${tag} ${pad(n.id, 18)} (${kind}, ${dur}, ${tokens})`);
288
+ if (n.error) lines.push(` error: ${n.error}`);
289
+ });
290
+ }
291
+ return lines.join("\n") + "\n";
292
+ }
293
+
294
+ // ---------------------------------------------------------------------------
295
+ // Main entry — dependency-injected for tests.
296
+ // ---------------------------------------------------------------------------
297
+
298
+ export interface MainDeps {
299
+ argv: string[];
300
+ home?: () => string;
301
+ stdout?: (s: string) => void;
302
+ stderr?: (s: string) => void;
303
+ }
304
+
305
+ export async function main(deps: MainDeps): Promise<number> {
306
+ const out = deps.stdout ?? ((s) => process.stdout.write(s));
307
+ const err = deps.stderr ?? ((s) => process.stderr.write(s));
308
+ const home = (deps.home ?? homedir)();
309
+ const args = parseArgs(deps.argv);
310
+ const dir = orchestrationsDir(home);
311
+
312
+ // Detail mode (single graph id, no --json or --json with id).
313
+ if (args.graphId) {
314
+ if (!isSafeGraphId(args.graphId)) {
315
+ err(`Invalid graph id ${JSON.stringify(args.graphId)} — must be alphanumeric + hyphen/underscore.\n`);
316
+ return 1;
317
+ }
318
+ const rec = loadRecord(home, args.graphId, { warn: err });
319
+ if (!rec) {
320
+ err(`No run found with id ${args.graphId}\n`);
321
+ return 1;
322
+ }
323
+ if (args.json) {
324
+ out(JSON.stringify(rec, null, 2) + "\n");
325
+ } else {
326
+ out(renderDetail(rec));
327
+ }
328
+ return 0;
329
+ }
330
+
331
+ // List mode — also handles `--json` (with no id).
332
+ if (!existsSync(dir)) {
333
+ if (args.json) {
334
+ out(JSON.stringify([], null, 2) + "\n");
335
+ return 0;
336
+ }
337
+ out("No orchestration runs yet — use /ashlr-orchestrate to start.\n");
338
+ return 0;
339
+ }
340
+
341
+ const records = loadAllRecords(home, { warn: err });
342
+ if (records.length === 0) {
343
+ if (args.json) {
344
+ out(JSON.stringify([], null, 2) + "\n");
345
+ return 0;
346
+ }
347
+ out("No orchestration runs yet — use /ashlr-orchestrate to start.\n");
348
+ return 0;
349
+ }
350
+
351
+ // Sort newest-first for both renderers + JSON.
352
+ const sorted = [...records].sort((a, b) => {
353
+ const ax = a.result.startedAt ?? "";
354
+ const bx = b.result.startedAt ?? "";
355
+ if (ax < bx) return 1;
356
+ if (ax > bx) return -1;
357
+ return 0;
358
+ });
359
+
360
+ if (args.json) {
361
+ out(JSON.stringify(sorted.slice(0, args.last), null, 2) + "\n");
362
+ return 0;
363
+ }
364
+
365
+ out(renderList(records, args.last));
366
+ return 0;
367
+ }
368
+
369
+ // ---------------------------------------------------------------------------
370
+ // Direct invocation guard
371
+ // ---------------------------------------------------------------------------
372
+
373
+ const isDirectInvocation =
374
+ (import.meta as ImportMeta & { main?: boolean }).main === true ||
375
+ (typeof process !== "undefined" && process.argv[1]?.endsWith("cli-orchestrate-status.ts"));
376
+
377
+ if (isDirectInvocation) {
378
+ // Reference statSync so it's not tree-shaken in case future logic needs it.
379
+ void statSync;
380
+ main({ argv: process.argv.slice(2) })
381
+ .then((code) => process.exit(code))
382
+ .catch((e) => {
383
+ process.stderr.write(`fatal: ${(e as Error).message}\n`);
384
+ process.exit(2);
385
+ });
386
+ }
@@ -0,0 +1,303 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * cli-orchestrate.ts — entry point invoked by /ashlr-orchestrate.
4
+ *
5
+ * Pipeline:
6
+ * 1. Resolve tier (isProSync + optional ASHLR_TEST_TIER override).
7
+ * 2. Either parse --yaml <path> OR auto-expand the goal via Track A.
8
+ * 3. Render the DAG via Track B and print to stdout.
9
+ * 4. Confirm via stdin (y/n/e), unless --auto-confirm.
10
+ * 5. Run the graph via Track B and print a summary.
11
+ *
12
+ * Designed for dependency injection: tests pass overrides for isProSync,
13
+ * the expander, the runner, stdin, and stdout/stderr writers so they can
14
+ * assert every branch without spawning real subprocesses.
15
+ *
16
+ * Targets the Track A schema landed in main: discriminated-union returns
17
+ * for fromYaml + validateTaskGraph; TaskGraph requires id/createdAt/
18
+ * handoffs/metadata; tier is restricted to "pro" | "team" (free is gated
19
+ * before we ever reach the expander).
20
+ */
21
+
22
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
23
+ import { homedir } from "os";
24
+ import { join, resolve as resolvePath } from "path";
25
+
26
+ import { isProSync } from "../servers/_pro.ts";
27
+ import {
28
+ fromYaml,
29
+ toYaml,
30
+ validateTaskGraph,
31
+ type TaskGraph,
32
+ } from "../servers/_task-graph.ts";
33
+ import { renderTaskGraph } from "../servers/_task-graph-render.ts";
34
+ import { expandToTaskGraph } from "./orchestrate-expand.ts";
35
+ import { runTaskGraph, type RunResult } from "./orchestrate-run.ts";
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Arg parsing
39
+ // ---------------------------------------------------------------------------
40
+
41
+ export interface ParsedArgs {
42
+ goal: string | null;
43
+ scope: string | null;
44
+ yamlPath: string | null;
45
+ autoConfirm: boolean;
46
+ dryRun: boolean;
47
+ tierOverride: "free" | "pro" | "team" | null;
48
+ }
49
+
50
+ export function parseArgs(argv: string[]): ParsedArgs {
51
+ const out: ParsedArgs = {
52
+ goal: null,
53
+ scope: null,
54
+ yamlPath: null,
55
+ autoConfirm: false,
56
+ dryRun: false,
57
+ tierOverride: null,
58
+ };
59
+ const positional: string[] = [];
60
+ for (let i = 0; i < argv.length; i++) {
61
+ const a = argv[i];
62
+ if (a === "--scope") out.scope = argv[++i] ?? null;
63
+ else if (a === "--yaml") out.yamlPath = argv[++i] ?? null;
64
+ else if (a === "--auto-confirm") out.autoConfirm = true;
65
+ else if (a === "--dry-run") out.dryRun = true;
66
+ else if (a === "--tier") {
67
+ const v = argv[++i];
68
+ if (v === "free" || v === "pro" || v === "team") out.tierOverride = v;
69
+ } else if (a && !a.startsWith("--")) {
70
+ positional.push(a);
71
+ }
72
+ }
73
+ if (positional.length > 0) out.goal = positional.join(" ");
74
+ return out;
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Tier resolution
79
+ // ---------------------------------------------------------------------------
80
+
81
+ export type Tier = "free" | "pro" | "team";
82
+
83
+ export function resolveTier(
84
+ env: NodeJS.ProcessEnv = process.env,
85
+ isPro: () => boolean = isProSync,
86
+ argOverride?: Tier | null,
87
+ ): Tier {
88
+ if (argOverride) return argOverride;
89
+ // Test escape hatch — keeps the smoke test self-contained without
90
+ // requiring a real ~/.ashlr/pro-token-cache.json. Mirrors the
91
+ // ASHLR_PRO_ASSUME pattern in servers/_pro.ts.
92
+ const testTier = env["ASHLR_TEST_TIER"];
93
+ if (testTier === "free" || testTier === "pro" || testTier === "team") {
94
+ return testTier;
95
+ }
96
+ return isPro() ? "pro" : "free";
97
+ }
98
+
99
+ // ---------------------------------------------------------------------------
100
+ // Confirmation prompt
101
+ // ---------------------------------------------------------------------------
102
+
103
+ export type Choice = "y" | "n" | "e";
104
+
105
+ export interface PromptOptions {
106
+ readLine: () => Promise<string>;
107
+ }
108
+
109
+ export async function readConfirmation(opts: PromptOptions): Promise<Choice> {
110
+ const raw = (await opts.readLine()).trim().toLowerCase();
111
+ if (raw.startsWith("y")) return "y";
112
+ if (raw.startsWith("e")) return "e";
113
+ return "n";
114
+ }
115
+
116
+ function defaultStdinReadLine(): Promise<string> {
117
+ return new Promise((resolveP) => {
118
+ let buf = "";
119
+ const onData = (chunk: Buffer) => {
120
+ buf += chunk.toString("utf-8");
121
+ const nl = buf.indexOf("\n");
122
+ if (nl >= 0) {
123
+ process.stdin.removeListener("data", onData);
124
+ process.stdin.pause();
125
+ resolveP(buf.slice(0, nl));
126
+ }
127
+ };
128
+ process.stdin.resume();
129
+ process.stdin.on("data", onData);
130
+ });
131
+ }
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // Edit-path YAML scratch file
135
+ // ---------------------------------------------------------------------------
136
+
137
+ export function writeEditScratch(
138
+ graph: TaskGraph,
139
+ home: string = homedir(),
140
+ now: () => Date = () => new Date(),
141
+ ): string {
142
+ const dir = join(home, ".ashlr");
143
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
144
+ const id = now().toISOString().replace(/[:.]/g, "-");
145
+ const path = join(dir, `orchestrate-edit-${id}.yaml`);
146
+ writeFileSync(path, toYaml(graph), "utf-8");
147
+ return path;
148
+ }
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // Summary formatter
152
+ // ---------------------------------------------------------------------------
153
+
154
+ export function formatSummary(result: RunResult): string {
155
+ const lines: string[] = [];
156
+ lines.push("Run summary");
157
+ lines.push(` ok: ${result.ok}`);
158
+ lines.push(` duration: ${result.totalDurationMs}ms`);
159
+ lines.push(` tokens: ${result.totalTokens}`);
160
+ lines.push(" nodes:");
161
+ for (const n of result.nodes) {
162
+ const tag = n.ok ? "[ok] " : "[fail]";
163
+ lines.push(
164
+ ` ${tag}${n.id.padEnd(12)} (${n.durationMs}ms, ${n.tokens} tokens)${
165
+ n.error ? ` — ${n.error}` : ""
166
+ }`,
167
+ );
168
+ }
169
+ lines.push("completed");
170
+ return lines.join("\n");
171
+ }
172
+
173
+ // ---------------------------------------------------------------------------
174
+ // Main
175
+ // ---------------------------------------------------------------------------
176
+
177
+ export interface MainDeps {
178
+ argv: string[];
179
+ env?: NodeJS.ProcessEnv;
180
+ cwd?: () => string;
181
+ isPro?: () => boolean;
182
+ expand?: typeof expandToTaskGraph;
183
+ run?: typeof runTaskGraph;
184
+ readLine?: () => Promise<string>;
185
+ stdout?: (s: string) => void;
186
+ stderr?: (s: string) => void;
187
+ readFile?: (p: string) => string;
188
+ home?: () => string;
189
+ now?: () => Date;
190
+ isTTY?: boolean;
191
+ }
192
+
193
+ export async function main(deps: MainDeps): Promise<number> {
194
+ const env = deps.env ?? process.env;
195
+ const cwd = deps.cwd ?? (() => process.cwd());
196
+ const out = deps.stdout ?? ((s) => process.stdout.write(s));
197
+ const err = deps.stderr ?? ((s) => process.stderr.write(s));
198
+ const expand = deps.expand ?? expandToTaskGraph;
199
+ const run = deps.run ?? runTaskGraph;
200
+ const readFile = deps.readFile ?? ((p: string) => readFileSync(p, "utf-8"));
201
+ const home = deps.home ?? homedir;
202
+ const now = deps.now ?? (() => new Date());
203
+ const isTTY = deps.isTTY ?? Boolean(process.stdout.isTTY);
204
+
205
+ const args = parseArgs(deps.argv);
206
+ const tier = resolveTier(env, deps.isPro, args.tierOverride);
207
+
208
+ // 1. Tier gate
209
+ if (tier === "free") {
210
+ err("Orchestration requires Pro or Team. Run /ashlr-upgrade.\n");
211
+ return 1;
212
+ }
213
+
214
+ // 2. Load or expand
215
+ let graph: TaskGraph;
216
+ if (args.yamlPath) {
217
+ const absPath = resolvePath(args.yamlPath);
218
+ let raw: string;
219
+ try {
220
+ raw = readFile(absPath);
221
+ } catch (e) {
222
+ err(`Could not read YAML file: ${absPath}\n`);
223
+ err(` ${(e as Error).message}\n`);
224
+ return 1;
225
+ }
226
+ const parsed = fromYaml(raw);
227
+ if (!parsed.ok) {
228
+ err(`YAML parse error:\n`);
229
+ for (const e of parsed.errors) err(` ${e}\n`);
230
+ return 1;
231
+ }
232
+ const v = validateTaskGraph(parsed.graph);
233
+ if (!v.ok) {
234
+ err(`Graph validation failed:\n`);
235
+ for (const e of v.errors) err(` ${e}\n`);
236
+ return 1;
237
+ }
238
+ graph = v.graph;
239
+ } else {
240
+ if (!args.goal) {
241
+ err(
242
+ 'Usage: ashlr-orchestrate "<goal>" [--scope <path>] [--yaml <path>] [--auto-confirm] [--dry-run]\n',
243
+ );
244
+ return 1;
245
+ }
246
+ graph = await expand({
247
+ goal: args.goal,
248
+ scope: args.scope ?? cwd(),
249
+ tier: tier === "team" ? "team" : "pro",
250
+ });
251
+ }
252
+
253
+ // 3. Render preview
254
+ out(renderTaskGraph(graph, { color: isTTY }));
255
+ out("\n");
256
+
257
+ // 4. Confirm
258
+ if (!args.autoConfirm) {
259
+ out("Press y to execute, n to cancel, e to edit YAML.\n");
260
+ const choice = await readConfirmation({
261
+ readLine: deps.readLine ?? defaultStdinReadLine,
262
+ });
263
+ if (choice === "n") {
264
+ out("cancelled\n");
265
+ return 0;
266
+ }
267
+ if (choice === "e") {
268
+ const path = writeEditScratch(graph, home(), now);
269
+ out(`Wrote graph to ${path}\n`);
270
+ out(`Edit and re-run with --yaml ${path}\n`);
271
+ return 0;
272
+ }
273
+ }
274
+
275
+ // 5. Run + summary
276
+ const result = await run({
277
+ graph,
278
+ dryRun: args.dryRun,
279
+ cwd: args.scope ?? cwd(),
280
+ });
281
+ out(formatSummary(result));
282
+ out("\n");
283
+ return result.ok ? 0 : 1;
284
+ }
285
+
286
+ // ---------------------------------------------------------------------------
287
+ // Entry
288
+ // ---------------------------------------------------------------------------
289
+
290
+ const isDirectInvocation =
291
+ // Bun's import.meta.main is true for direct `bun run` invocations.
292
+ // Fall back to argv check for safety.
293
+ (import.meta as ImportMeta & { main?: boolean }).main === true ||
294
+ (typeof process !== "undefined" && process.argv[1]?.endsWith("cli-orchestrate.ts"));
295
+
296
+ if (isDirectInvocation) {
297
+ main({ argv: process.argv.slice(2) })
298
+ .then((code) => process.exit(code))
299
+ .catch((e) => {
300
+ process.stderr.write(`fatal: ${(e as Error).message}\n`);
301
+ process.exit(2);
302
+ });
303
+ }