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,177 @@
1
+ /**
2
+ * orchestrate-emit-telemetry.ts — Q1'27 plugin emit for orchestration runs.
3
+ *
4
+ * Called by scripts/orchestrate-run.ts AFTER runTaskGraph() returns, but
5
+ * before the result is handed back to the caller. Best-effort: never blocks
6
+ * the return, never throws, never leaks identity content into logs.
7
+ *
8
+ * Wire format mirrors the server zod schema in
9
+ * server/src/routes/orchestration-runs.ts.
10
+ *
11
+ * Privacy gate: ONLY fires when telemetry consent is on (~/.ashlr/config.json
12
+ * :: { telemetry: "opt-in" } OR ASHLR_TELEMETRY=on). When consent is off,
13
+ * this function is a no-op — no fetch, no DNS, no work.
14
+ *
15
+ * Network behavior: 5s timeout via AbortSignal.timeout, fire-and-forget on
16
+ * failure (same fetch pattern as emitDailyHeartbeat in servers/_telemetry.ts).
17
+ *
18
+ * Test seams: _setOrchestrationTelemetryUrl + _setOrchestrationTelemetryFetch
19
+ * follow the existing _setDailyHeartbeat* pattern so the runner integration
20
+ * test can assert "emit fired with this body" without hitting the network.
21
+ */
22
+
23
+ import { getIdentityHash } from "../servers/_identity-hash.ts";
24
+ import { isTelemetryEnabled } from "../servers/_telemetry.ts";
25
+ import type { TaskGraph } from "../servers/_task-graph.ts";
26
+ import type { RunResult } from "./orchestrate-run.ts";
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Test seams
30
+ // ---------------------------------------------------------------------------
31
+
32
+ let _urlOverride: string | null = null;
33
+ let _fetchOverride: typeof fetch | null = null;
34
+ let _isTelemetryOverride: (() => boolean) | null = null;
35
+
36
+ /** @internal — tests force POSTs to a mock URL. */
37
+ export function _setOrchestrationTelemetryUrl(url: string | null): void {
38
+ _urlOverride = url;
39
+ }
40
+
41
+ /** @internal — tests inject a fake fetch so they can capture calls. */
42
+ export function _setOrchestrationTelemetryFetch(fn: typeof fetch | null): void {
43
+ _fetchOverride = fn;
44
+ }
45
+
46
+ /** @internal — tests skip the consent check. */
47
+ export function _setIsTelemetryEnabledForOrchestrationEmit(
48
+ fn: (() => boolean) | null,
49
+ ): void {
50
+ _isTelemetryOverride = fn;
51
+ }
52
+
53
+ function isTelemetryGuarded(): boolean {
54
+ return _isTelemetryOverride ? _isTelemetryOverride() : isTelemetryEnabled();
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // URL resolution — matches the daily-heartbeat pattern.
59
+ // ---------------------------------------------------------------------------
60
+
61
+ function endpointUrl(): string {
62
+ if (_urlOverride) return _urlOverride;
63
+ const base = process.env["ASHLR_API_URL"] ?? "https://api.ashlr.ai";
64
+ return `${base.replace(/\/+$/, "")}/v1/orchestration-runs`;
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Public API
69
+ // ---------------------------------------------------------------------------
70
+
71
+ export interface OrchestrationRunTelemetryPayload {
72
+ identity_hash: string;
73
+ github_hash: string | null;
74
+ graph_id: string;
75
+ goal: string;
76
+ tier: "pro" | "team";
77
+ mode: "stub" | "real-llm";
78
+ started_at: string;
79
+ finished_at: string;
80
+ duration_ms: number;
81
+ node_count: number;
82
+ fail_count: number;
83
+ ok: boolean;
84
+ total_tokens_in: number;
85
+ total_tokens_out: number;
86
+ }
87
+
88
+ /**
89
+ * Build the wire payload from a RunResult + TaskGraph + mode flag.
90
+ *
91
+ * Pure function — easy to unit-test without network. Exported so the runner
92
+ * integration test can verify shape without invoking the full fetch path.
93
+ */
94
+ export function buildOrchestrationRunPayload(
95
+ result: RunResult,
96
+ graph: TaskGraph,
97
+ mode: "stub" | "real-llm",
98
+ ): OrchestrationRunTelemetryPayload {
99
+ const { machineHash, githubHash } = getIdentityHash();
100
+
101
+ // tier on the graph is "pro" | "team" — free is gated client-side, so a
102
+ // free-tier reject result never reaches us. We coerce defensively to
103
+ // satisfy the wire enum if a future caller mis-routes us.
104
+ const tier: "pro" | "team" = graph.tier === "team" ? "team" : "pro";
105
+
106
+ // Token totals: today the runner only tracks one running total. The wire
107
+ // schema accommodates input vs output separately for future LLM-mode
108
+ // wiring; in stub mode we account all tokens as "in" (the dominant
109
+ // cost in real Claude usage), leaving "out" at 0.
110
+ const totalTokens = typeof result.totalTokensUsed === "number"
111
+ ? result.totalTokensUsed
112
+ : (result.totalTokens ?? 0);
113
+
114
+ const failCount = (result.nodeResults ?? []).filter((n) => !n.ok).length;
115
+
116
+ return {
117
+ identity_hash: machineHash,
118
+ github_hash: githubHash,
119
+ graph_id: result.graphId ?? graph.id,
120
+ goal: graph.goal,
121
+ tier,
122
+ mode,
123
+ started_at: result.startedAt,
124
+ finished_at: result.finishedAt,
125
+ duration_ms: result.totalDurationMs,
126
+ node_count: (result.nodeResults ?? []).length,
127
+ fail_count: failCount,
128
+ ok: result.ok,
129
+ total_tokens_in: totalTokens,
130
+ total_tokens_out: 0,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Fire-and-forget POST of an orchestration-run telemetry record.
136
+ *
137
+ * Guarantees:
138
+ * - Returns immediately on the success path (Promise resolves; network
139
+ * call dispatched in background).
140
+ * - Silent on every error path — never breaks the runner return.
141
+ * - No-op when telemetry consent is off.
142
+ *
143
+ * The runner calls this from runTaskGraph() right before returning. We
144
+ * await this call so tests can assert "the POST fired," but the underlying
145
+ * fetch uses AbortSignal.timeout(5000) and the catch swallows failures,
146
+ * so the actual network round-trip never blocks the caller.
147
+ */
148
+ export async function emitOrchestrationRunTelemetry(
149
+ result: RunResult,
150
+ graph: TaskGraph,
151
+ mode: "stub" | "real-llm",
152
+ ): Promise<void> {
153
+ try {
154
+ if (!isTelemetryGuarded()) return;
155
+
156
+ const payload = buildOrchestrationRunPayload(result, graph, mode);
157
+ const url = endpointUrl();
158
+ const fetcher = _fetchOverride ?? globalThis.fetch;
159
+
160
+ // Fire-and-forget: kick off the fetch, don't await it. We DO want errors
161
+ // swallowed so a flaky network never breaks the runner.
162
+ void Promise.resolve()
163
+ .then(() =>
164
+ fetcher(url, {
165
+ method: "POST",
166
+ headers: { "Content-Type": "application/json" },
167
+ body: JSON.stringify(payload),
168
+ signal: AbortSignal.timeout(5_000),
169
+ }),
170
+ )
171
+ .catch(() => {
172
+ /* network failure — drop silently, same as emitDailyHeartbeat */
173
+ });
174
+ } catch {
175
+ /* never propagate — telemetry must never break orchestrate-run */
176
+ }
177
+ }
@@ -0,0 +1,517 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * orchestrate-expand — Q1 2027 Distributed Orchestration: auto-expander.
4
+ *
5
+ * Given a goal + scope, deterministically generate a TaskGraph by:
6
+ * 1. Discovering "modules" at the scope root (direct child directories,
7
+ * optionally fronted by a `src/` layout that we descend through once).
8
+ * 2. Bucketing source files per module + classifying each module by content
9
+ * (tests, docs, or general source → refactorer).
10
+ * 3. Tracing first-party imports between modules via the regex extractor
11
+ * borrowed from servers/_prefetch.ts and turning those into `deps`.
12
+ * 4. Ranking modules by file-count + goal-keyword relevance and capping at
13
+ * `maxNodes` (default: 3 for pro, 10 for team).
14
+ * 5. Sizing each node's `estimatedTokens` (≈2000 base + 50/file, capped at
15
+ * 20000).
16
+ *
17
+ * No LLM calls. Pure + deterministic for the same inputs. The runner (Track B)
18
+ * and slash command (Track C) consume the output verbatim.
19
+ *
20
+ * CLI:
21
+ * bun scripts/orchestrate-expand.ts \
22
+ * --goal "Add freshness logging" \
23
+ * --scope servers/ \
24
+ * --tier pro [--yaml] [--max-nodes 5]
25
+ */
26
+
27
+ import { readdirSync, lstatSync, statSync, existsSync, readFileSync } from "fs";
28
+ import { join, relative, resolve, basename, dirname, isAbsolute, sep } from "path";
29
+ import { randomUUID } from "crypto";
30
+
31
+ import {
32
+ toYaml,
33
+ validateTaskGraph,
34
+ type AgentKind,
35
+ type TaskGraph,
36
+ type TaskNode,
37
+ } from "../servers/_task-graph.ts";
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Public API
41
+ // ---------------------------------------------------------------------------
42
+
43
+ export interface ExpandOptions {
44
+ goal: string;
45
+ scope: string; // cwd-relative or absolute directory
46
+ tier: "pro" | "team";
47
+ maxNodes?: number; // default 3 for pro, 10 for team
48
+ cwd?: string;
49
+ }
50
+
51
+ const DEFAULT_PRO_CAP = 3;
52
+ const DEFAULT_TEAM_CAP = 10;
53
+
54
+ const SOURCE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs"]);
55
+ const TOKENISH_EXTS = new Set([".ts", ".tsx", ".py", ".go"]); // used for token estimation per spec
56
+ const DOC_EXTS = new Set([".md", ".mdx", ".rst", ".txt"]);
57
+ const SKIP_DIRS = new Set([
58
+ "node_modules",
59
+ ".git",
60
+ "dist",
61
+ "build",
62
+ ".next",
63
+ ".cache",
64
+ ".turbo",
65
+ "__pycache__",
66
+ ".venv",
67
+ ".ashlrcode",
68
+ "coverage",
69
+ ".vscode",
70
+ ".idea",
71
+ ".in_use",
72
+ ]);
73
+
74
+ const TOKENS_BASE = 2000;
75
+ const TOKENS_PER_FILE = 50;
76
+ const TOKENS_CAP = 20000;
77
+
78
+ /** Auto-expand a goal+scope into a TaskGraph. Deterministic for the same inputs. */
79
+ export async function expandToTaskGraph(opts: ExpandOptions): Promise<TaskGraph> {
80
+ const cwd = opts.cwd ?? process.cwd();
81
+ const scopeAbs = isAbsolute(opts.scope) ? opts.scope : resolve(cwd, opts.scope);
82
+ if (!existsSync(scopeAbs) || !statSync(scopeAbs).isDirectory()) {
83
+ throw new Error(`scope is not a directory: ${scopeAbs}`);
84
+ }
85
+
86
+ const cap = opts.maxNodes ?? (opts.tier === "team" ? DEFAULT_TEAM_CAP : DEFAULT_PRO_CAP);
87
+ const modules = discoverModules(scopeAbs);
88
+
89
+ // Score modules: file count + relevance to goal keywords.
90
+ const keywords = extractKeywords(opts.goal);
91
+ type Scored = { module: ModuleInfo; score: number; tokens: number; agentKind: AgentKind };
92
+ const scored: Scored[] = modules.map((m) => {
93
+ const tokenish = m.files.filter((f) => TOKENISH_EXTS.has(ext(f))).length;
94
+ const tokens = Math.min(TOKENS_CAP, TOKENS_BASE + tokenish * TOKENS_PER_FILE);
95
+ const relevance = scoreRelevance(m, keywords);
96
+ return {
97
+ module: m,
98
+ // Files dominate the rank; relevance is a tiebreaker boost.
99
+ score: m.files.length + relevance * 5,
100
+ tokens,
101
+ agentKind: classifyAgent(m),
102
+ };
103
+ });
104
+
105
+ scored.sort((a, b) => {
106
+ if (b.score !== a.score) return b.score - a.score;
107
+ return a.module.name.localeCompare(b.module.name);
108
+ });
109
+
110
+ const picked = scored.slice(0, cap);
111
+
112
+ // Build a quick lookup from absolute file path → module name (only modules
113
+ // we kept). Imports pointing at files outside this set are ignored.
114
+ const fileToModule = new Map<string, string>();
115
+ for (const s of picked) {
116
+ for (const f of s.module.files) fileToModule.set(f, s.module.name);
117
+ }
118
+
119
+ // Build dep edges based on imports.
120
+ const depsByModule = new Map<string, Set<string>>();
121
+ for (const s of picked) depsByModule.set(s.module.name, new Set());
122
+
123
+ for (const s of picked) {
124
+ for (const f of s.module.files) {
125
+ if (!shouldScanImports(f)) continue;
126
+ let text: string;
127
+ try {
128
+ text = readFileSync(f, "utf-8");
129
+ } catch {
130
+ continue;
131
+ }
132
+ const imports = extractImports(text);
133
+ for (const spec of imports) {
134
+ const resolved = resolveImport(spec, f, cwd);
135
+ if (!resolved) continue;
136
+ const otherModule = fileToModule.get(resolved);
137
+ if (!otherModule || otherModule === s.module.name) continue;
138
+ depsByModule.get(s.module.name)!.add(otherModule);
139
+ }
140
+ }
141
+ }
142
+
143
+ const nodes: TaskNode[] = picked.map((s) => ({
144
+ id: nodeIdFor(s.module.name),
145
+ agentKind: s.agentKind,
146
+ goal: `Apply '${opts.goal}' to module ${s.module.name}`,
147
+ scope: [relative(cwd, s.module.path) || s.module.path],
148
+ deps: Array.from(depsByModule.get(s.module.name) ?? []).map(nodeIdFor).sort(),
149
+ estimatedTokens: s.tokens,
150
+ }));
151
+
152
+ // Break any cycles by dropping the dep that closes them. Auto-expansion
153
+ // should never emit a cyclic graph — sibling tracks rely on this guarantee.
154
+ breakCycles(nodes);
155
+
156
+ const totalTokenBudget = nodes.reduce((sum, n) => sum + n.estimatedTokens, 0);
157
+ const graph: TaskGraph = {
158
+ id: randomUUID(),
159
+ goal: opts.goal,
160
+ scope: relative(cwd, scopeAbs) || scopeAbs,
161
+ tier: opts.tier,
162
+ createdAt: new Date().toISOString(),
163
+ nodes,
164
+ handoffs: [],
165
+ metadata: { autoExpanded: true, totalTokenBudget },
166
+ };
167
+
168
+ // Validate before returning — fast-fail if we ever produce a malformed graph.
169
+ const v = validateTaskGraph(graph);
170
+ if (!v.ok) {
171
+ throw new Error(`expander produced invalid graph: ${v.errors.join("; ")}`);
172
+ }
173
+ return v.graph;
174
+ }
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // Module discovery
178
+ // ---------------------------------------------------------------------------
179
+
180
+ interface ModuleInfo {
181
+ name: string; // e.g. "auth"
182
+ path: string; // absolute
183
+ files: string[]; // absolute file paths inside the module
184
+ }
185
+
186
+ function discoverModules(scopeAbs: string): ModuleInfo[] {
187
+ // Direct children of the scope dir → candidate modules. We also descend
188
+ // into a single layer of `src/` if it's the only meaningful child.
189
+ let root = scopeAbs;
190
+ const children = safeReaddir(root).filter((c) => !SKIP_DIRS.has(c));
191
+ const dirChildren = children.filter((c) => isDir(join(root, c)));
192
+ // If scope has exactly one source-bearing dir named `src` / `lib`, descend.
193
+ if (dirChildren.length === 1 && (dirChildren[0] === "src" || dirChildren[0] === "lib")) {
194
+ root = join(root, dirChildren[0]!);
195
+ }
196
+
197
+ const modules: ModuleInfo[] = [];
198
+ for (const c of safeReaddir(root)) {
199
+ if (SKIP_DIRS.has(c)) continue;
200
+ const childPath = join(root, c);
201
+ if (!isDir(childPath)) continue;
202
+ const files = collectFiles(childPath);
203
+ if (files.length === 0) continue;
204
+ modules.push({ name: c, path: childPath, files });
205
+ }
206
+ return modules;
207
+ }
208
+
209
+ function collectFiles(dir: string): string[] {
210
+ const out: string[] = [];
211
+ const visit = (d: string): void => {
212
+ let entries: string[];
213
+ try {
214
+ entries = readdirSync(d);
215
+ } catch {
216
+ return;
217
+ }
218
+ for (const entry of entries) {
219
+ if (SKIP_DIRS.has(entry)) continue;
220
+ const abs = join(d, entry);
221
+ let st;
222
+ try {
223
+ st = lstatSync(abs);
224
+ } catch {
225
+ continue;
226
+ }
227
+ if (st.isDirectory()) {
228
+ visit(abs);
229
+ } else if (st.isFile()) {
230
+ const e = ext(abs);
231
+ if (SOURCE_EXTS.has(e) || DOC_EXTS.has(e)) out.push(abs);
232
+ }
233
+ }
234
+ };
235
+ visit(dir);
236
+ return out;
237
+ }
238
+
239
+ function classifyAgent(m: ModuleInfo): AgentKind {
240
+ const testCount = m.files.filter((f) => /\.test\.[tj]sx?$/.test(f) || /_test\.(go|py)$/.test(f)).length;
241
+ const docCount = m.files.filter((f) => DOC_EXTS.has(ext(f))).length;
242
+ const lowerName = m.name.toLowerCase();
243
+
244
+ if (testCount > 0 && testCount * 2 >= m.files.length) return "test-writer";
245
+ if (docCount > 0 && docCount * 2 >= m.files.length) return "doc-writer";
246
+ if (lowerName.includes("test") || lowerName.startsWith("__tests__")) return "test-writer";
247
+ if (lowerName === "docs" || lowerName === "documentation") return "doc-writer";
248
+ return "refactorer";
249
+ }
250
+
251
+ // ---------------------------------------------------------------------------
252
+ // Relevance scoring
253
+ // ---------------------------------------------------------------------------
254
+
255
+ function extractKeywords(goal: string): string[] {
256
+ return goal
257
+ .toLowerCase()
258
+ .split(/[^a-z0-9]+/)
259
+ .filter((w) => w.length > 2 && !STOPWORDS.has(w));
260
+ }
261
+
262
+ const STOPWORDS = new Set([
263
+ "the",
264
+ "and",
265
+ "for",
266
+ "with",
267
+ "from",
268
+ "into",
269
+ "this",
270
+ "that",
271
+ "add",
272
+ "make",
273
+ "use",
274
+ "all",
275
+ "any",
276
+ "ref",
277
+ "fix",
278
+ "new",
279
+ "out",
280
+ ]);
281
+
282
+ function scoreRelevance(m: ModuleInfo, keywords: string[]): number {
283
+ if (keywords.length === 0) return 0;
284
+ const lowerName = m.name.toLowerCase();
285
+ let score = 0;
286
+ for (const k of keywords) {
287
+ if (lowerName.includes(k)) score += 2;
288
+ for (const f of m.files) {
289
+ if (basename(f).toLowerCase().includes(k)) {
290
+ score += 1;
291
+ break;
292
+ }
293
+ }
294
+ }
295
+ return score;
296
+ }
297
+
298
+ // ---------------------------------------------------------------------------
299
+ // Import extraction (borrowed from servers/_prefetch.ts)
300
+ // ---------------------------------------------------------------------------
301
+
302
+ function extractImports(source: string): string[] {
303
+ const patterns: RegExp[] = [
304
+ /import\s+[^'"`;]*?\s+from\s+['"]([^'"]+)['"]/g,
305
+ /from\s+['"]([^'"]+)['"]/g,
306
+ /require\(\s*['"]([^'"]+)['"]\s*\)/g,
307
+ /^\s*from\s+([A-Za-z_][\w.]*)\s+import\b/gm,
308
+ /^\s*import\s+([A-Za-z_][\w.]*)/gm,
309
+ ];
310
+ const counts = new Map<string, number>();
311
+ for (const re of patterns) {
312
+ let m: RegExpExecArray | null;
313
+ while ((m = re.exec(source)) !== null) {
314
+ const spec = m[1];
315
+ if (!spec) continue;
316
+ counts.set(spec, (counts.get(spec) ?? 0) + 1);
317
+ }
318
+ }
319
+ return Array.from(counts.keys());
320
+ }
321
+
322
+ const RESOLVE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go"];
323
+
324
+ function resolveImport(spec: string, fromFile: string, cwd: string): string | null {
325
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return null;
326
+ const base = isAbsolute(spec) ? spec : resolve(dirname(fromFile), spec);
327
+ const candidates: string[] = [base];
328
+ for (const e of RESOLVE_EXTENSIONS) candidates.push(base + e);
329
+ for (const e of RESOLVE_EXTENSIONS) candidates.push(join(base, "index" + e));
330
+ for (const candidate of candidates) {
331
+ if (existsSync(candidate) && statSync(candidate).isFile()) {
332
+ // Bound to the cwd — anything outside is ignored.
333
+ const rel = relative(cwd, candidate);
334
+ if (rel.startsWith("..") || isAbsolute(rel)) return null;
335
+ return candidate;
336
+ }
337
+ }
338
+ return null;
339
+ }
340
+
341
+ function shouldScanImports(file: string): boolean {
342
+ const e = ext(file);
343
+ return e === ".ts" || e === ".tsx" || e === ".js" || e === ".jsx" || e === ".mjs" || e === ".cjs" || e === ".py";
344
+ }
345
+
346
+ // ---------------------------------------------------------------------------
347
+ // Cycle breaker
348
+ // ---------------------------------------------------------------------------
349
+
350
+ function breakCycles(nodes: TaskNode[]): void {
351
+ // Repeatedly find any cycle and drop the LAST edge along it until none
352
+ // remain. Auto-expansion graphs are small (≤10 nodes) so this is cheap.
353
+ while (true) {
354
+ const cycle = findCycleEdges(nodes);
355
+ if (!cycle) return;
356
+ const [from, to] = cycle;
357
+ const node = nodes.find((n) => n.id === from);
358
+ if (!node) return;
359
+ node.deps = node.deps.filter((d) => d !== to);
360
+ }
361
+ }
362
+
363
+ function findCycleEdges(nodes: TaskNode[]): [string, string] | null {
364
+ const byId = new Map<string, TaskNode>();
365
+ for (const n of nodes) byId.set(n.id, n);
366
+ const WHITE = 0;
367
+ const GREY = 1;
368
+ const BLACK = 2;
369
+ const color = new Map<string, number>();
370
+ for (const n of nodes) color.set(n.id, WHITE);
371
+
372
+ const stack: string[] = [];
373
+ function dfs(id: string): [string, string] | null {
374
+ color.set(id, GREY);
375
+ stack.push(id);
376
+ const node = byId.get(id);
377
+ if (node) {
378
+ for (const dep of node.deps) {
379
+ const c = color.get(dep);
380
+ if (c === GREY) return [id, dep];
381
+ if (c === WHITE) {
382
+ const found = dfs(dep);
383
+ if (found) return found;
384
+ }
385
+ }
386
+ }
387
+ color.set(id, BLACK);
388
+ stack.pop();
389
+ return null;
390
+ }
391
+ for (const n of nodes) {
392
+ if (color.get(n.id) === WHITE) {
393
+ const found = dfs(n.id);
394
+ if (found) return found;
395
+ }
396
+ }
397
+ return null;
398
+ }
399
+
400
+ // ---------------------------------------------------------------------------
401
+ // Helpers
402
+ // ---------------------------------------------------------------------------
403
+
404
+ function safeReaddir(dir: string): string[] {
405
+ try {
406
+ return readdirSync(dir);
407
+ } catch {
408
+ return [];
409
+ }
410
+ }
411
+
412
+ function isDir(p: string): boolean {
413
+ try {
414
+ return lstatSync(p).isDirectory();
415
+ } catch {
416
+ return false;
417
+ }
418
+ }
419
+
420
+ function ext(p: string): string {
421
+ const idx = p.lastIndexOf(".");
422
+ return idx === -1 ? "" : p.slice(idx);
423
+ }
424
+
425
+ function nodeIdFor(moduleName: string): string {
426
+ const safe = moduleName.replace(/[^a-zA-Z0-9_-]+/g, "-").toLowerCase();
427
+ return `node-${safe}`;
428
+ }
429
+
430
+ // ---------------------------------------------------------------------------
431
+ // CLI entry
432
+ // ---------------------------------------------------------------------------
433
+
434
+ interface CliArgs {
435
+ goal?: string;
436
+ scope?: string;
437
+ tier?: "pro" | "team";
438
+ maxNodes?: number;
439
+ yaml: boolean;
440
+ }
441
+
442
+ function parseArgs(argv: string[]): CliArgs {
443
+ const out: CliArgs = { yaml: false };
444
+ for (let i = 0; i < argv.length; i++) {
445
+ const a = argv[i];
446
+ switch (a) {
447
+ case "--goal":
448
+ out.goal = argv[++i];
449
+ break;
450
+ case "--scope":
451
+ out.scope = argv[++i];
452
+ break;
453
+ case "--tier": {
454
+ const v = argv[++i];
455
+ if (v === "pro" || v === "team") out.tier = v;
456
+ break;
457
+ }
458
+ case "--max-nodes":
459
+ out.maxNodes = Number(argv[++i]);
460
+ break;
461
+ case "--yaml":
462
+ out.yaml = true;
463
+ break;
464
+ case "--help":
465
+ case "-h":
466
+ printUsage();
467
+ process.exit(0);
468
+ break;
469
+ }
470
+ }
471
+ return out;
472
+ }
473
+
474
+ function printUsage(): void {
475
+ process.stdout.write(
476
+ [
477
+ "Usage: bun scripts/orchestrate-expand.ts --goal <goal> --scope <path> --tier <pro|team> [--max-nodes N] [--yaml]",
478
+ "",
479
+ " --goal The user's top-level intent. Required.",
480
+ " --scope Directory to expand. Required.",
481
+ " --tier 'pro' (cap 3) or 'team' (cap 10). Required.",
482
+ " --max-nodes Override the tier-default cap.",
483
+ " --yaml Emit YAML instead of JSON.",
484
+ "",
485
+ ].join("\n"),
486
+ );
487
+ }
488
+
489
+ // Only run the CLI when this file is executed directly (not when imported by
490
+ // tests). `Bun.main` resolves to the script bun started with.
491
+ const isDirectRun =
492
+ typeof Bun !== "undefined" && typeof Bun.main === "string" && Bun.main === import.meta.path;
493
+
494
+ if (isDirectRun) {
495
+ const args = parseArgs(process.argv.slice(2));
496
+ if (!args.goal || !args.scope || !args.tier) {
497
+ printUsage();
498
+ process.exit(2);
499
+ }
500
+ const opts: ExpandOptions = {
501
+ goal: args.goal,
502
+ scope: args.scope,
503
+ tier: args.tier,
504
+ };
505
+ if (args.maxNodes !== undefined && Number.isFinite(args.maxNodes)) {
506
+ opts.maxNodes = args.maxNodes;
507
+ }
508
+ expandToTaskGraph(opts).then(
509
+ (g) => {
510
+ process.stdout.write(args.yaml ? toYaml(g) : JSON.stringify(g, null, 2) + "\n");
511
+ },
512
+ (err: Error) => {
513
+ process.stderr.write(`orchestrate-expand: ${err.message}\n`);
514
+ process.exit(1);
515
+ },
516
+ );
517
+ }