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,545 @@
1
+ /**
2
+ * _task-graph — Q1 2027 Distributed Orchestration: schema + validation + YAML I/O.
3
+ *
4
+ * This file is the CONTRACT between the three orchestration tracks:
5
+ * - Track A (this file + scripts/orchestrate-expand.ts): types + auto-expander.
6
+ * - Track B (runner + renderer): consumes TaskGraph to dispatch nodes and
7
+ * pretty-print the DAG for `--dry-run` confirmation.
8
+ * - Track C (/ashlr-orchestrate slash command): parses CLI args into ExpandOptions
9
+ * and hands the resulting TaskGraph to Track B.
10
+ *
11
+ * Anything exported below is part of the contract and MUST NOT change shape without
12
+ * a coordinated update to the sibling tracks. See plans/distributed-orchestration-design.md
13
+ * sections 3 (data model) and 6 (sequencing).
14
+ *
15
+ * No external dependencies — `js-yaml` is intentionally NOT pulled in. The
16
+ * `toYaml` / `fromYaml` helpers below are a minimal fit-for-purpose serializer
17
+ * covering only the shapes exported here.
18
+ */
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Types — the contract
22
+ // ---------------------------------------------------------------------------
23
+
24
+ export type AgentKind = "refactorer" | "test-writer" | "doc-writer" | "reviewer" | "generic";
25
+
26
+ const VALID_AGENT_KINDS: ReadonlySet<AgentKind> = new Set<AgentKind>([
27
+ "refactorer",
28
+ "test-writer",
29
+ "doc-writer",
30
+ "reviewer",
31
+ "generic",
32
+ ]);
33
+
34
+ export interface TaskNode {
35
+ id: string; // stable, e.g. "node-auth-refactor"
36
+ agentKind: AgentKind;
37
+ goal: string; // 1-2 sentence task for this node
38
+ scope: string[]; // file/dir paths this node operates on
39
+ deps: string[]; // node ids that must complete before this one starts
40
+ estimatedTokens: number; // rough budget for sequencing + soft-throttle
41
+ }
42
+
43
+ export interface TaskHandoff {
44
+ fromNode: string;
45
+ toNode: string;
46
+ contextSummary: string; // <=200 words — what the receiver needs to know
47
+ payloadJson?: unknown; // optional structured handoff
48
+ }
49
+
50
+ export interface TaskGraph {
51
+ id: string; // uuid v4
52
+ goal: string; // the user's top-level intent
53
+ scope: string; // root path the orchestration operates within
54
+ tier: "pro" | "team"; // free is gated before we ever get here
55
+ createdAt: string; // ISO
56
+ nodes: TaskNode[];
57
+ handoffs: TaskHandoff[];
58
+ metadata: {
59
+ autoExpanded: boolean;
60
+ sourceYaml?: string; // present when generated from --yaml input
61
+ totalTokenBudget: number;
62
+ };
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Validation
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /**
70
+ * Validate the shape of a TaskGraph candidate.
71
+ *
72
+ * Checks performed:
73
+ * - every required field exists with the right primitive type;
74
+ * - every TaskNode.agentKind is in VALID_AGENT_KINDS;
75
+ * - every dep references a node id that exists in `nodes`;
76
+ * - the dep graph is acyclic (DFS with grey/black coloring). On cycle, the
77
+ * returned error is `"cycle: A -> B -> A"` (arrows ASCII so YAML round-trips
78
+ * are not muddied);
79
+ * - handoff endpoints reference real node ids;
80
+ * - metadata.totalTokenBudget is a finite non-negative number.
81
+ *
82
+ * Returns a discriminated union so callers can pattern-match: on `ok: true` the
83
+ * `graph` field is narrowed to `TaskGraph`; on `ok: false` `errors` lists every
84
+ * problem found (validation does not short-circuit on the first error).
85
+ */
86
+ export function validateTaskGraph(g: unknown): { ok: true; graph: TaskGraph } | { ok: false; errors: string[] } {
87
+ const errors: string[] = [];
88
+
89
+ if (g === null || typeof g !== "object" || Array.isArray(g)) {
90
+ return { ok: false, errors: ["root: expected object"] };
91
+ }
92
+ const obj = g as Record<string, unknown>;
93
+
94
+ // -- Top-level scalar checks ------------------------------------------------
95
+ if (typeof obj.id !== "string" || obj.id.length === 0) errors.push("id: missing or not a non-empty string");
96
+ if (typeof obj.goal !== "string" || obj.goal.length === 0) errors.push("goal: missing or not a non-empty string");
97
+ if (typeof obj.scope !== "string") errors.push("scope: missing or not a string");
98
+ if (obj.tier !== "pro" && obj.tier !== "team") errors.push("tier: must be 'pro' or 'team'");
99
+ if (typeof obj.createdAt !== "string") errors.push("createdAt: missing or not a string");
100
+
101
+ // -- nodes ------------------------------------------------------------------
102
+ const nodes: TaskNode[] = [];
103
+ const nodeIds = new Set<string>();
104
+ if (!Array.isArray(obj.nodes)) {
105
+ errors.push("nodes: missing or not an array");
106
+ } else {
107
+ obj.nodes.forEach((rawNode, i) => {
108
+ if (rawNode === null || typeof rawNode !== "object" || Array.isArray(rawNode)) {
109
+ errors.push(`nodes[${i}]: expected object`);
110
+ return;
111
+ }
112
+ const n = rawNode as Record<string, unknown>;
113
+ const tag = `nodes[${i}]`;
114
+ const id = typeof n.id === "string" ? n.id : null;
115
+ const goal = typeof n.goal === "string" ? n.goal : null;
116
+ const agentKind = typeof n.agentKind === "string" ? (n.agentKind as AgentKind) : null;
117
+ const estimatedTokens = typeof n.estimatedTokens === "number" ? n.estimatedTokens : null;
118
+
119
+ if (!id) errors.push(`${tag}.id: missing or not a string`);
120
+ if (!goal) errors.push(`${tag}.goal: missing or not a string`);
121
+ if (!agentKind) {
122
+ errors.push(`${tag}.agentKind: missing or not a string`);
123
+ } else if (!VALID_AGENT_KINDS.has(agentKind)) {
124
+ errors.push(`${tag}.agentKind: unknown '${agentKind}'`);
125
+ }
126
+ if (estimatedTokens === null || !Number.isFinite(estimatedTokens) || estimatedTokens < 0) {
127
+ errors.push(`${tag}.estimatedTokens: must be a finite non-negative number`);
128
+ }
129
+ if (!Array.isArray(n.scope) || !n.scope.every((s) => typeof s === "string")) {
130
+ errors.push(`${tag}.scope: must be an array of strings`);
131
+ }
132
+ if (!Array.isArray(n.deps) || !n.deps.every((s) => typeof s === "string")) {
133
+ errors.push(`${tag}.deps: must be an array of strings`);
134
+ }
135
+
136
+ if (id && agentKind && VALID_AGENT_KINDS.has(agentKind) && Array.isArray(n.scope) && Array.isArray(n.deps)) {
137
+ const node: TaskNode = {
138
+ id,
139
+ agentKind,
140
+ goal: goal ?? "",
141
+ scope: n.scope as string[],
142
+ deps: n.deps as string[],
143
+ estimatedTokens: estimatedTokens ?? 0,
144
+ };
145
+ nodes.push(node);
146
+ if (nodeIds.has(id)) errors.push(`${tag}.id: duplicate '${id}'`);
147
+ nodeIds.add(id);
148
+ }
149
+ });
150
+ }
151
+
152
+ // -- handoffs ---------------------------------------------------------------
153
+ const handoffs: TaskHandoff[] = [];
154
+ if (obj.handoffs !== undefined) {
155
+ if (!Array.isArray(obj.handoffs)) {
156
+ errors.push("handoffs: must be an array");
157
+ } else {
158
+ obj.handoffs.forEach((rawH, i) => {
159
+ if (rawH === null || typeof rawH !== "object" || Array.isArray(rawH)) {
160
+ errors.push(`handoffs[${i}]: expected object`);
161
+ return;
162
+ }
163
+ const h = rawH as Record<string, unknown>;
164
+ const tag = `handoffs[${i}]`;
165
+ if (typeof h.fromNode !== "string") errors.push(`${tag}.fromNode: missing or not a string`);
166
+ if (typeof h.toNode !== "string") errors.push(`${tag}.toNode: missing or not a string`);
167
+ if (typeof h.contextSummary !== "string") errors.push(`${tag}.contextSummary: missing or not a string`);
168
+ if (typeof h.fromNode === "string" && typeof h.toNode === "string" && typeof h.contextSummary === "string") {
169
+ if (!nodeIds.has(h.fromNode)) errors.push(`${tag}.fromNode: references unknown node '${h.fromNode}'`);
170
+ if (!nodeIds.has(h.toNode)) errors.push(`${tag}.toNode: references unknown node '${h.toNode}'`);
171
+ handoffs.push({
172
+ fromNode: h.fromNode,
173
+ toNode: h.toNode,
174
+ contextSummary: h.contextSummary,
175
+ payloadJson: h.payloadJson,
176
+ });
177
+ }
178
+ });
179
+ }
180
+ }
181
+
182
+ // -- metadata ---------------------------------------------------------------
183
+ let metadata: TaskGraph["metadata"] | null = null;
184
+ if (obj.metadata === undefined || obj.metadata === null || typeof obj.metadata !== "object" || Array.isArray(obj.metadata)) {
185
+ errors.push("metadata: missing or not an object");
186
+ } else {
187
+ const m = obj.metadata as Record<string, unknown>;
188
+ if (typeof m.autoExpanded !== "boolean") errors.push("metadata.autoExpanded: must be a boolean");
189
+ if (typeof m.totalTokenBudget !== "number" || !Number.isFinite(m.totalTokenBudget) || m.totalTokenBudget < 0) {
190
+ errors.push("metadata.totalTokenBudget: must be a finite non-negative number");
191
+ }
192
+ if (m.sourceYaml !== undefined && typeof m.sourceYaml !== "string") {
193
+ errors.push("metadata.sourceYaml: must be a string when present");
194
+ }
195
+ if (typeof m.autoExpanded === "boolean" && typeof m.totalTokenBudget === "number") {
196
+ metadata = {
197
+ autoExpanded: m.autoExpanded,
198
+ totalTokenBudget: m.totalTokenBudget,
199
+ sourceYaml: typeof m.sourceYaml === "string" ? m.sourceYaml : undefined,
200
+ };
201
+ }
202
+ }
203
+
204
+ // -- dep references + cycle detection --------------------------------------
205
+ // (only run when we have a clean nodes set; otherwise dep errors compound)
206
+ for (const node of nodes) {
207
+ for (const dep of node.deps) {
208
+ if (!nodeIds.has(dep)) errors.push(`nodes[${node.id}].deps: references unknown node '${dep}'`);
209
+ }
210
+ }
211
+
212
+ if (errors.length === 0) {
213
+ const cycle = findCycle(nodes);
214
+ if (cycle) errors.push(`cycle: ${cycle.join(" -> ")}`);
215
+ }
216
+
217
+ if (errors.length > 0) return { ok: false, errors };
218
+
219
+ return {
220
+ ok: true,
221
+ graph: {
222
+ id: obj.id as string,
223
+ goal: obj.goal as string,
224
+ scope: obj.scope as string,
225
+ tier: obj.tier as "pro" | "team",
226
+ createdAt: obj.createdAt as string,
227
+ nodes,
228
+ handoffs,
229
+ metadata: metadata as TaskGraph["metadata"],
230
+ },
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Cycle detection via DFS with grey/black coloring.
236
+ *
237
+ * Returns the cycle as a list of node ids `[A, B, A]` so the caller can render
238
+ * "cycle: A -> B -> A". Returns null when no cycle exists.
239
+ *
240
+ * Complexity: O(V + E). We bail on the first cycle found — multi-cycle reporting
241
+ * is not worth the complexity for an ergonomics-focused message.
242
+ */
243
+ function findCycle(nodes: TaskNode[]): string[] | null {
244
+ const byId = new Map<string, TaskNode>();
245
+ for (const n of nodes) byId.set(n.id, n);
246
+
247
+ const WHITE = 0;
248
+ const GREY = 1;
249
+ const BLACK = 2;
250
+ const color = new Map<string, number>();
251
+ for (const n of nodes) color.set(n.id, WHITE);
252
+
253
+ const stack: string[] = [];
254
+
255
+ function dfs(id: string): string[] | null {
256
+ color.set(id, GREY);
257
+ stack.push(id);
258
+ const node = byId.get(id);
259
+ if (node) {
260
+ for (const dep of node.deps) {
261
+ const c = color.get(dep);
262
+ if (c === GREY) {
263
+ // Found a back-edge. Slice the cycle out of the stack.
264
+ const start = stack.indexOf(dep);
265
+ return [...stack.slice(start), dep];
266
+ }
267
+ if (c === WHITE) {
268
+ const found = dfs(dep);
269
+ if (found) return found;
270
+ }
271
+ }
272
+ }
273
+ color.set(id, BLACK);
274
+ stack.pop();
275
+ return null;
276
+ }
277
+
278
+ for (const n of nodes) {
279
+ if (color.get(n.id) === WHITE) {
280
+ const found = dfs(n.id);
281
+ if (found) return found;
282
+ }
283
+ }
284
+ return null;
285
+ }
286
+
287
+ // ---------------------------------------------------------------------------
288
+ // YAML serialization (minimal, fit-for-purpose)
289
+ // ---------------------------------------------------------------------------
290
+ //
291
+ // We support only the exact TaskGraph shape above:
292
+ // - scalars: strings, numbers, booleans, null;
293
+ // - sequences (arrays) of scalars or objects;
294
+ // - mappings (objects) keyed by identifier-safe strings.
295
+ //
296
+ // Strings are ALWAYS double-quoted with JSON-safe escaping, which is a valid
297
+ // (if pedestrian) YAML 1.2 syntax. This sidesteps the YAML quoting subtleties
298
+ // that bite hand-rolled emitters (colon-in-string, leading-dash, etc.).
299
+ //
300
+ // Multiline strings, anchors, tags, flow style, and other YAML niceties are
301
+ // deliberately out of scope. If a future contract field needs them, add a
302
+ // dedicated test and extend here — or pull in js-yaml.
303
+
304
+ /** Emit a TaskGraph as YAML text. */
305
+ export function toYaml(g: TaskGraph): string {
306
+ return emitMapping(g as unknown as Record<string, unknown>, 0) + "\n";
307
+ }
308
+
309
+ function emitMapping(obj: Record<string, unknown>, indent: number): string {
310
+ const pad = " ".repeat(indent);
311
+ const keys = Object.keys(obj);
312
+ if (keys.length === 0) return `${pad}{}`;
313
+ const lines: string[] = [];
314
+ for (const k of keys) {
315
+ const v = obj[k];
316
+ if (v === undefined) continue;
317
+ lines.push(`${pad}${k}:${emitValueInline(v, indent)}`);
318
+ }
319
+ return lines.join("\n");
320
+ }
321
+
322
+ function emitValueInline(v: unknown, indent: number): string {
323
+ if (v === null) return " null";
324
+ if (typeof v === "string") return ` ${encodeScalarString(v)}`;
325
+ if (typeof v === "number" || typeof v === "boolean") return ` ${String(v)}`;
326
+ if (Array.isArray(v)) {
327
+ if (v.length === 0) return " []";
328
+ const pad = " ".repeat(indent + 1);
329
+ const items = v.map((item) => {
330
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
331
+ const inner = emitMapping(item as Record<string, unknown>, indent + 2);
332
+ // First key sits on the dash line; rest live one level deeper.
333
+ const innerLines = inner.split("\n");
334
+ if (innerLines.length === 0) return `${pad}- {}`;
335
+ const first = innerLines[0]?.trimStart() ?? "";
336
+ const rest = innerLines.slice(1).join("\n");
337
+ return rest ? `${pad}- ${first}\n${rest}` : `${pad}- ${first}`;
338
+ }
339
+ return `${pad}-${emitValueInline(item, indent + 1)}`;
340
+ });
341
+ return "\n" + items.join("\n");
342
+ }
343
+ if (typeof v === "object") {
344
+ const inner = emitMapping(v as Record<string, unknown>, indent + 1);
345
+ return "\n" + inner;
346
+ }
347
+ // Fallback: treat unknown types as JSON.
348
+ return ` ${JSON.stringify(v)}`;
349
+ }
350
+
351
+ function encodeScalarString(s: string): string {
352
+ // JSON.stringify gives us a perfectly valid YAML double-quoted scalar.
353
+ return JSON.stringify(s);
354
+ }
355
+
356
+ /** Parse YAML text back into a TaskGraph. Re-runs `validateTaskGraph`. */
357
+ export function fromYaml(s: string): { ok: true; graph: TaskGraph } | { ok: false; errors: string[] } {
358
+ let parsed: unknown;
359
+ try {
360
+ parsed = parseYaml(s);
361
+ } catch (e) {
362
+ return { ok: false, errors: [`yaml-parse: ${(e as Error).message}`] };
363
+ }
364
+ return validateTaskGraph(parsed);
365
+ }
366
+
367
+ // ---------------------------------------------------------------------------
368
+ // Minimal YAML parser (indent-driven)
369
+ // ---------------------------------------------------------------------------
370
+ //
371
+ // Supports exactly what `toYaml` emits:
372
+ // - 2-space indentation;
373
+ // - `key: value` lines (value may be a JSON-quoted scalar, number, bool,
374
+ // null, `[]`, or `{}`, or absent if the value is a nested block);
375
+ // - `- value` sequence items (value may be a scalar OR an inline `key: value`
376
+ // starting a new mapping continued on indented lines below).
377
+ //
378
+ // Round-trips with toYaml. NOT a general-purpose YAML 1.2 parser.
379
+
380
+ interface YamlLine {
381
+ indent: number;
382
+ text: string;
383
+ raw: string;
384
+ lineNo: number;
385
+ }
386
+
387
+ function parseYaml(input: string): unknown {
388
+ const rawLines = input.split(/\r?\n/);
389
+ const lines: YamlLine[] = [];
390
+ rawLines.forEach((raw, idx) => {
391
+ // Strip comments (only when `#` is preceded by whitespace or at SOL — same
392
+ // rule as YAML proper). Quoted strings cannot contain raw `#` since we
393
+ // double-quote everything.
394
+ const stripped = stripComment(raw);
395
+ if (stripped.trim().length === 0) return;
396
+ const indent = countLeadingSpaces(stripped);
397
+ lines.push({ indent, text: stripped.trim(), raw, lineNo: idx + 1 });
398
+ });
399
+ if (lines.length === 0) return {};
400
+ const { value } = parseBlock(lines, 0, lines[0]!.indent);
401
+ return value;
402
+ }
403
+
404
+ function stripComment(line: string): string {
405
+ let inString = false;
406
+ for (let i = 0; i < line.length; i++) {
407
+ const ch = line[i];
408
+ if (ch === '"' && line[i - 1] !== "\\") inString = !inString;
409
+ if (ch === "#" && !inString && (i === 0 || /\s/.test(line[i - 1] ?? ""))) {
410
+ return line.slice(0, i);
411
+ }
412
+ }
413
+ return line;
414
+ }
415
+
416
+ function countLeadingSpaces(s: string): number {
417
+ let i = 0;
418
+ while (i < s.length && s[i] === " ") i++;
419
+ return i;
420
+ }
421
+
422
+ /**
423
+ * Parse a block of lines starting at `start` whose indent equals `indent`.
424
+ * Returns the parsed value and the index of the first line that is NOT part
425
+ * of this block (i.e. has a smaller indent than `indent`).
426
+ */
427
+ function parseBlock(lines: YamlLine[], start: number, indent: number): { value: unknown; next: number } {
428
+ if (start >= lines.length) return { value: null, next: start };
429
+ const first = lines[start]!;
430
+ if (first.text.startsWith("- ")) return parseSequence(lines, start, indent);
431
+ return parseMapping(lines, start, indent);
432
+ }
433
+
434
+ function parseMapping(lines: YamlLine[], start: number, indent: number): { value: Record<string, unknown>; next: number } {
435
+ const obj: Record<string, unknown> = {};
436
+ let i = start;
437
+ while (i < lines.length) {
438
+ const line = lines[i]!;
439
+ if (line.indent < indent) break;
440
+ if (line.indent > indent) throw new Error(`unexpected indent at line ${line.lineNo}`);
441
+ const sepIdx = findKeySeparator(line.text);
442
+ if (sepIdx < 0) throw new Error(`mapping line missing ':' at line ${line.lineNo}: ${line.raw}`);
443
+ const key = line.text.slice(0, sepIdx).trim();
444
+ const rest = line.text.slice(sepIdx + 1).trim();
445
+ if (rest.length > 0) {
446
+ // Inline scalar / flow-style.
447
+ obj[key] = parseScalar(rest);
448
+ i++;
449
+ continue;
450
+ }
451
+ // Block value on subsequent indented lines.
452
+ if (i + 1 >= lines.length || lines[i + 1]!.indent <= indent) {
453
+ obj[key] = null;
454
+ i++;
455
+ continue;
456
+ }
457
+ const childIndent = lines[i + 1]!.indent;
458
+ const { value: child, next } = parseBlock(lines, i + 1, childIndent);
459
+ obj[key] = child;
460
+ i = next;
461
+ }
462
+ return { value: obj, next: i };
463
+ }
464
+
465
+ function findKeySeparator(text: string): number {
466
+ // Skip past any JSON-quoted key region (we never emit quoted keys, but be safe).
467
+ let inString = false;
468
+ for (let i = 0; i < text.length; i++) {
469
+ const ch = text[i];
470
+ if (ch === '"' && text[i - 1] !== "\\") inString = !inString;
471
+ if (ch === ":" && !inString) return i;
472
+ }
473
+ return -1;
474
+ }
475
+
476
+ function parseSequence(lines: YamlLine[], start: number, indent: number): { value: unknown[]; next: number } {
477
+ const arr: unknown[] = [];
478
+ let i = start;
479
+ while (i < lines.length) {
480
+ const line = lines[i]!;
481
+ if (line.indent < indent) break;
482
+ if (line.indent > indent) throw new Error(`unexpected indent in sequence at line ${line.lineNo}`);
483
+ if (!line.text.startsWith("- ") && line.text !== "-") {
484
+ throw new Error(`expected sequence item at line ${line.lineNo}: ${line.raw}`);
485
+ }
486
+ const after = line.text === "-" ? "" : line.text.slice(2).trim();
487
+ if (after.length === 0) {
488
+ // The block lives entirely on the following lines.
489
+ if (i + 1 >= lines.length || lines[i + 1]!.indent <= indent) {
490
+ arr.push(null);
491
+ i++;
492
+ continue;
493
+ }
494
+ const childIndent = lines[i + 1]!.indent;
495
+ const { value: child, next } = parseBlock(lines, i + 1, childIndent);
496
+ arr.push(child);
497
+ i = next;
498
+ continue;
499
+ }
500
+ // Check whether the dash-line itself opens a mapping (e.g. `- id: foo`).
501
+ const sepIdx = findKeySeparator(after);
502
+ if (sepIdx >= 0) {
503
+ // Reconstruct a virtual mapping block: this line is the first key, and
504
+ // continuation lines (indented two more) belong to the same mapping.
505
+ const virtualIndent = indent + 2;
506
+ const virtualLines: YamlLine[] = [];
507
+ virtualLines.push({ indent: virtualIndent, text: after, raw: line.raw, lineNo: line.lineNo });
508
+ let j = i + 1;
509
+ while (j < lines.length && lines[j]!.indent >= virtualIndent && !(lines[j]!.indent === indent && lines[j]!.text.startsWith("- "))) {
510
+ virtualLines.push(lines[j]!);
511
+ j++;
512
+ }
513
+ const { value: child } = parseMapping(virtualLines, 0, virtualIndent);
514
+ arr.push(child);
515
+ i = j;
516
+ continue;
517
+ }
518
+ // Inline scalar sequence item.
519
+ arr.push(parseScalar(after));
520
+ i++;
521
+ }
522
+ return { value: arr, next: i };
523
+ }
524
+
525
+ function parseScalar(text: string): unknown {
526
+ const t = text.trim();
527
+ if (t === "null" || t === "~") return null;
528
+ if (t === "true") return true;
529
+ if (t === "false") return false;
530
+ if (t === "[]") return [];
531
+ if (t === "{}") return {};
532
+ if (t.startsWith('"')) {
533
+ // JSON-quoted string.
534
+ try {
535
+ return JSON.parse(t);
536
+ } catch {
537
+ throw new Error(`invalid quoted string: ${t}`);
538
+ }
539
+ }
540
+ if (/^-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?$/.test(t)) {
541
+ return Number(t);
542
+ }
543
+ // Plain unquoted string fallback.
544
+ return t;
545
+ }