@bastani/atomic 0.6.8 → 0.7.0-1

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 (765) hide show
  1. package/bin/atomic +65 -0
  2. package/package.json +17 -82
  3. package/postinstall.mjs +47 -0
  4. package/.agents/skills/ado-commit/SKILL.md +0 -94
  5. package/.agents/skills/ado-create-pr/SKILL.md +0 -211
  6. package/.agents/skills/advanced-evaluation/SKILL.md +0 -404
  7. package/.agents/skills/advanced-evaluation/references/bias-mitigation.md +0 -288
  8. package/.agents/skills/advanced-evaluation/references/evaluation-pipeline.md +0 -43
  9. package/.agents/skills/advanced-evaluation/references/implementation-patterns.md +0 -315
  10. package/.agents/skills/advanced-evaluation/references/metrics-guide.md +0 -331
  11. package/.agents/skills/advanced-evaluation/scripts/evaluation_example.py +0 -392
  12. package/.agents/skills/ast-grep/SKILL.md +0 -325
  13. package/.agents/skills/ast-grep/references/rule_reference.md +0 -297
  14. package/.agents/skills/bdi-mental-states/SKILL.md +0 -313
  15. package/.agents/skills/bdi-mental-states/references/bdi-ontology-core.md +0 -207
  16. package/.agents/skills/bdi-mental-states/references/framework-integration.md +0 -582
  17. package/.agents/skills/bdi-mental-states/references/rdf-examples.md +0 -315
  18. package/.agents/skills/bdi-mental-states/references/sparql-competency.md +0 -420
  19. package/.agents/skills/bun/SKILL.md +0 -233
  20. package/.agents/skills/context-compression/SKILL.md +0 -274
  21. package/.agents/skills/context-compression/references/evaluation-framework.md +0 -213
  22. package/.agents/skills/context-compression/scripts/compression_evaluator.py +0 -862
  23. package/.agents/skills/context-compression/tests/test_compression_evaluator.py +0 -56
  24. package/.agents/skills/context-degradation/SKILL.md +0 -208
  25. package/.agents/skills/context-degradation/references/patterns.md +0 -314
  26. package/.agents/skills/context-degradation/scripts/degradation_detector.py +0 -614
  27. package/.agents/skills/context-fundamentals/SKILL.md +0 -203
  28. package/.agents/skills/context-fundamentals/references/context-components.md +0 -283
  29. package/.agents/skills/context-fundamentals/scripts/context_manager.py +0 -533
  30. package/.agents/skills/context-optimization/SKILL.md +0 -197
  31. package/.agents/skills/context-optimization/references/optimization_techniques.md +0 -272
  32. package/.agents/skills/context-optimization/scripts/compaction.py +0 -562
  33. package/.agents/skills/create-spec/SKILL.md +0 -249
  34. package/.agents/skills/docx/LICENSE.txt +0 -30
  35. package/.agents/skills/docx/SKILL.md +0 -592
  36. package/.agents/skills/docx/scripts/__init__.py +0 -1
  37. package/.agents/skills/docx/scripts/accept_changes.py +0 -135
  38. package/.agents/skills/docx/scripts/comment.py +0 -318
  39. package/.agents/skills/docx/scripts/office/helpers/__init__.py +0 -0
  40. package/.agents/skills/docx/scripts/office/helpers/merge_runs.py +0 -199
  41. package/.agents/skills/docx/scripts/office/helpers/simplify_redlines.py +0 -197
  42. package/.agents/skills/docx/scripts/office/pack.py +0 -159
  43. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  44. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  45. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  46. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  47. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  48. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  49. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  50. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  51. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  52. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  53. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  54. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  55. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  56. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  57. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  58. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  59. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  60. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  61. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  62. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  63. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  64. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  65. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  66. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  67. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  68. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  69. package/.agents/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  70. package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  71. package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  72. package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  73. package/.agents/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  74. package/.agents/skills/docx/scripts/office/schemas/mce/mc.xsd +0 -75
  75. package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
  76. package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
  77. package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
  78. package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
  79. package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
  80. package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  81. package/.agents/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
  82. package/.agents/skills/docx/scripts/office/soffice.py +0 -183
  83. package/.agents/skills/docx/scripts/office/unpack.py +0 -132
  84. package/.agents/skills/docx/scripts/office/validate.py +0 -111
  85. package/.agents/skills/docx/scripts/office/validators/__init__.py +0 -15
  86. package/.agents/skills/docx/scripts/office/validators/base.py +0 -847
  87. package/.agents/skills/docx/scripts/office/validators/docx.py +0 -446
  88. package/.agents/skills/docx/scripts/office/validators/pptx.py +0 -275
  89. package/.agents/skills/docx/scripts/office/validators/redlining.py +0 -247
  90. package/.agents/skills/docx/scripts/templates/comments.xml +0 -3
  91. package/.agents/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  92. package/.agents/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  93. package/.agents/skills/docx/scripts/templates/commentsIds.xml +0 -3
  94. package/.agents/skills/docx/scripts/templates/people.xml +0 -3
  95. package/.agents/skills/evaluation/SKILL.md +0 -253
  96. package/.agents/skills/evaluation/references/metrics.md +0 -339
  97. package/.agents/skills/evaluation/scripts/evaluator.py +0 -627
  98. package/.agents/skills/explain-code/SKILL.md +0 -232
  99. package/.agents/skills/filesystem-context/SKILL.md +0 -289
  100. package/.agents/skills/filesystem-context/references/implementation-patterns.md +0 -549
  101. package/.agents/skills/filesystem-context/scripts/filesystem_context.py +0 -425
  102. package/.agents/skills/find-skills/SKILL.md +0 -144
  103. package/.agents/skills/gh-commit/SKILL.md +0 -245
  104. package/.agents/skills/gh-create-pr/SKILL.md +0 -95
  105. package/.agents/skills/hosted-agents/SKILL.md +0 -262
  106. package/.agents/skills/hosted-agents/references/infrastructure-patterns.md +0 -700
  107. package/.agents/skills/hosted-agents/scripts/sandbox_manager.py +0 -590
  108. package/.agents/skills/impeccable/SKILL.md +0 -178
  109. package/.agents/skills/impeccable/agents/openai.yaml +0 -4
  110. package/.agents/skills/impeccable/reference/adapt.md +0 -190
  111. package/.agents/skills/impeccable/reference/animate.md +0 -175
  112. package/.agents/skills/impeccable/reference/audit.md +0 -134
  113. package/.agents/skills/impeccable/reference/bolder.md +0 -113
  114. package/.agents/skills/impeccable/reference/brand.md +0 -114
  115. package/.agents/skills/impeccable/reference/clarify.md +0 -174
  116. package/.agents/skills/impeccable/reference/cognitive-load.md +0 -106
  117. package/.agents/skills/impeccable/reference/color-and-contrast.md +0 -105
  118. package/.agents/skills/impeccable/reference/colorize.md +0 -154
  119. package/.agents/skills/impeccable/reference/craft.md +0 -193
  120. package/.agents/skills/impeccable/reference/critique.md +0 -213
  121. package/.agents/skills/impeccable/reference/delight.md +0 -302
  122. package/.agents/skills/impeccable/reference/distill.md +0 -111
  123. package/.agents/skills/impeccable/reference/document.md +0 -427
  124. package/.agents/skills/impeccable/reference/extract.md +0 -70
  125. package/.agents/skills/impeccable/reference/harden.md +0 -347
  126. package/.agents/skills/impeccable/reference/heuristics-scoring.md +0 -234
  127. package/.agents/skills/impeccable/reference/interaction-design.md +0 -195
  128. package/.agents/skills/impeccable/reference/layout.md +0 -141
  129. package/.agents/skills/impeccable/reference/live.md +0 -594
  130. package/.agents/skills/impeccable/reference/motion-design.md +0 -109
  131. package/.agents/skills/impeccable/reference/onboard.md +0 -234
  132. package/.agents/skills/impeccable/reference/optimize.md +0 -258
  133. package/.agents/skills/impeccable/reference/overdrive.md +0 -130
  134. package/.agents/skills/impeccable/reference/personas.md +0 -178
  135. package/.agents/skills/impeccable/reference/polish.md +0 -232
  136. package/.agents/skills/impeccable/reference/product.md +0 -62
  137. package/.agents/skills/impeccable/reference/quieter.md +0 -99
  138. package/.agents/skills/impeccable/reference/responsive-design.md +0 -114
  139. package/.agents/skills/impeccable/reference/shape.md +0 -151
  140. package/.agents/skills/impeccable/reference/spatial-design.md +0 -100
  141. package/.agents/skills/impeccable/reference/teach.md +0 -156
  142. package/.agents/skills/impeccable/reference/typeset.md +0 -124
  143. package/.agents/skills/impeccable/reference/typography.md +0 -159
  144. package/.agents/skills/impeccable/reference/ux-writing.md +0 -107
  145. package/.agents/skills/impeccable/scripts/cleanup-deprecated.mjs +0 -284
  146. package/.agents/skills/impeccable/scripts/command-metadata.json +0 -94
  147. package/.agents/skills/impeccable/scripts/design-parser.mjs +0 -820
  148. package/.agents/skills/impeccable/scripts/detect-csp.mjs +0 -198
  149. package/.agents/skills/impeccable/scripts/is-generated.mjs +0 -69
  150. package/.agents/skills/impeccable/scripts/live-accept.mjs +0 -595
  151. package/.agents/skills/impeccable/scripts/live-browser.js +0 -4781
  152. package/.agents/skills/impeccable/scripts/live-inject.mjs +0 -445
  153. package/.agents/skills/impeccable/scripts/live-poll.mjs +0 -186
  154. package/.agents/skills/impeccable/scripts/live-server.mjs +0 -694
  155. package/.agents/skills/impeccable/scripts/live-wrap.mjs +0 -571
  156. package/.agents/skills/impeccable/scripts/live.mjs +0 -247
  157. package/.agents/skills/impeccable/scripts/load-context.mjs +0 -141
  158. package/.agents/skills/impeccable/scripts/modern-screenshot.umd.js +0 -14
  159. package/.agents/skills/impeccable/scripts/pin.mjs +0 -214
  160. package/.agents/skills/init/SKILL.md +0 -140
  161. package/.agents/skills/liteparse/SKILL.md +0 -223
  162. package/.agents/skills/memory-systems/SKILL.md +0 -221
  163. package/.agents/skills/memory-systems/references/implementation.md +0 -551
  164. package/.agents/skills/memory-systems/scripts/memory_store.py +0 -616
  165. package/.agents/skills/multi-agent-patterns/SKILL.md +0 -259
  166. package/.agents/skills/multi-agent-patterns/references/frameworks.md +0 -433
  167. package/.agents/skills/multi-agent-patterns/scripts/coordination.py +0 -613
  168. package/.agents/skills/opentui/SKILL.md +0 -202
  169. package/.agents/skills/opentui/references/animation/REFERENCE.md +0 -431
  170. package/.agents/skills/opentui/references/components/REFERENCE.md +0 -144
  171. package/.agents/skills/opentui/references/components/code-diff.md +0 -672
  172. package/.agents/skills/opentui/references/components/containers.md +0 -417
  173. package/.agents/skills/opentui/references/components/inputs.md +0 -531
  174. package/.agents/skills/opentui/references/components/text-display.md +0 -386
  175. package/.agents/skills/opentui/references/core/REFERENCE.md +0 -145
  176. package/.agents/skills/opentui/references/core/api.md +0 -543
  177. package/.agents/skills/opentui/references/core/configuration.md +0 -168
  178. package/.agents/skills/opentui/references/core/gotchas.md +0 -393
  179. package/.agents/skills/opentui/references/core/patterns.md +0 -449
  180. package/.agents/skills/opentui/references/keyboard/REFERENCE.md +0 -617
  181. package/.agents/skills/opentui/references/layout/REFERENCE.md +0 -337
  182. package/.agents/skills/opentui/references/layout/patterns.md +0 -444
  183. package/.agents/skills/opentui/references/react/REFERENCE.md +0 -174
  184. package/.agents/skills/opentui/references/react/api.md +0 -436
  185. package/.agents/skills/opentui/references/react/configuration.md +0 -302
  186. package/.agents/skills/opentui/references/react/gotchas.md +0 -443
  187. package/.agents/skills/opentui/references/react/patterns.md +0 -501
  188. package/.agents/skills/opentui/references/solid/REFERENCE.md +0 -201
  189. package/.agents/skills/opentui/references/solid/api.md +0 -564
  190. package/.agents/skills/opentui/references/solid/configuration.md +0 -316
  191. package/.agents/skills/opentui/references/solid/gotchas.md +0 -427
  192. package/.agents/skills/opentui/references/solid/patterns.md +0 -560
  193. package/.agents/skills/opentui/references/testing/REFERENCE.md +0 -614
  194. package/.agents/skills/pdf/LICENSE.txt +0 -30
  195. package/.agents/skills/pdf/SKILL.md +0 -316
  196. package/.agents/skills/pdf/forms.md +0 -294
  197. package/.agents/skills/pdf/reference.md +0 -612
  198. package/.agents/skills/pdf/scripts/check_bounding_boxes.py +0 -65
  199. package/.agents/skills/pdf/scripts/check_fillable_fields.py +0 -11
  200. package/.agents/skills/pdf/scripts/convert_pdf_to_images.py +0 -33
  201. package/.agents/skills/pdf/scripts/create_validation_image.py +0 -37
  202. package/.agents/skills/pdf/scripts/extract_form_field_info.py +0 -122
  203. package/.agents/skills/pdf/scripts/extract_form_structure.py +0 -115
  204. package/.agents/skills/pdf/scripts/fill_fillable_fields.py +0 -98
  205. package/.agents/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -107
  206. package/.agents/skills/playwright-cli/SKILL.md +0 -390
  207. package/.agents/skills/playwright-cli/references/element-attributes.md +0 -23
  208. package/.agents/skills/playwright-cli/references/playwright-tests.md +0 -39
  209. package/.agents/skills/playwright-cli/references/request-mocking.md +0 -87
  210. package/.agents/skills/playwright-cli/references/running-code.md +0 -241
  211. package/.agents/skills/playwright-cli/references/session-management.md +0 -225
  212. package/.agents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
  213. package/.agents/skills/playwright-cli/references/storage-state.md +0 -275
  214. package/.agents/skills/playwright-cli/references/test-generation.md +0 -134
  215. package/.agents/skills/playwright-cli/references/tracing.md +0 -139
  216. package/.agents/skills/playwright-cli/references/video-recording.md +0 -143
  217. package/.agents/skills/pptx/LICENSE.txt +0 -30
  218. package/.agents/skills/pptx/SKILL.md +0 -234
  219. package/.agents/skills/pptx/editing.md +0 -205
  220. package/.agents/skills/pptx/pptxgenjs.md +0 -420
  221. package/.agents/skills/pptx/scripts/__init__.py +0 -0
  222. package/.agents/skills/pptx/scripts/add_slide.py +0 -195
  223. package/.agents/skills/pptx/scripts/clean.py +0 -286
  224. package/.agents/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  225. package/.agents/skills/pptx/scripts/office/helpers/merge_runs.py +0 -199
  226. package/.agents/skills/pptx/scripts/office/helpers/simplify_redlines.py +0 -197
  227. package/.agents/skills/pptx/scripts/office/pack.py +0 -159
  228. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  229. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  230. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  231. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  232. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  233. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  234. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  235. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  236. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  237. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  238. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  239. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  240. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  241. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  242. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  243. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  244. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  245. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  246. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  247. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  248. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  249. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  250. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  251. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  252. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  253. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  254. package/.agents/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  255. package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  256. package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  257. package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  258. package/.agents/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  259. package/.agents/skills/pptx/scripts/office/schemas/mce/mc.xsd +0 -75
  260. package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
  261. package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
  262. package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
  263. package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
  264. package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
  265. package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  266. package/.agents/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
  267. package/.agents/skills/pptx/scripts/office/soffice.py +0 -183
  268. package/.agents/skills/pptx/scripts/office/unpack.py +0 -132
  269. package/.agents/skills/pptx/scripts/office/validate.py +0 -111
  270. package/.agents/skills/pptx/scripts/office/validators/__init__.py +0 -15
  271. package/.agents/skills/pptx/scripts/office/validators/base.py +0 -847
  272. package/.agents/skills/pptx/scripts/office/validators/docx.py +0 -446
  273. package/.agents/skills/pptx/scripts/office/validators/pptx.py +0 -275
  274. package/.agents/skills/pptx/scripts/office/validators/redlining.py +0 -247
  275. package/.agents/skills/pptx/scripts/thumbnail.py +0 -289
  276. package/.agents/skills/project-development/SKILL.md +0 -293
  277. package/.agents/skills/project-development/references/case-studies.md +0 -388
  278. package/.agents/skills/project-development/references/pipeline-patterns.md +0 -610
  279. package/.agents/skills/project-development/scripts/pipeline_template.py +0 -796
  280. package/.agents/skills/prompt-engineer/SKILL.md +0 -265
  281. package/.agents/skills/prompt-engineer/references/advanced_patterns.md +0 -271
  282. package/.agents/skills/prompt-engineer/references/core_prompting.md +0 -137
  283. package/.agents/skills/prompt-engineer/references/quality_improvement.md +0 -193
  284. package/.agents/skills/research-codebase/SKILL.md +0 -229
  285. package/.agents/skills/ripgrep/SKILL.md +0 -384
  286. package/.agents/skills/skill-creator/LICENSE.txt +0 -202
  287. package/.agents/skills/skill-creator/SKILL.md +0 -487
  288. package/.agents/skills/skill-creator/agents/analyzer.md +0 -274
  289. package/.agents/skills/skill-creator/agents/comparator.md +0 -202
  290. package/.agents/skills/skill-creator/agents/grader.md +0 -223
  291. package/.agents/skills/skill-creator/assets/eval_review.html +0 -146
  292. package/.agents/skills/skill-creator/eval-viewer/generate_review.py +0 -471
  293. package/.agents/skills/skill-creator/eval-viewer/viewer.html +0 -1325
  294. package/.agents/skills/skill-creator/references/schemas.md +0 -430
  295. package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
  296. package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
  297. package/.agents/skills/skill-creator/scripts/generate_report.py +0 -326
  298. package/.agents/skills/skill-creator/scripts/improve_description.py +0 -247
  299. package/.agents/skills/skill-creator/scripts/package_skill.py +0 -136
  300. package/.agents/skills/skill-creator/scripts/quick_validate.py +0 -103
  301. package/.agents/skills/skill-creator/scripts/run_eval.py +0 -310
  302. package/.agents/skills/skill-creator/scripts/run_loop.py +0 -328
  303. package/.agents/skills/skill-creator/scripts/utils.py +0 -47
  304. package/.agents/skills/sl-commit/SKILL.md +0 -53
  305. package/.agents/skills/sl-submit-diff/SKILL.md +0 -57
  306. package/.agents/skills/tdd/SKILL.md +0 -111
  307. package/.agents/skills/tdd/deep-modules.md +0 -33
  308. package/.agents/skills/tdd/interface-design.md +0 -31
  309. package/.agents/skills/tdd/mocking.md +0 -59
  310. package/.agents/skills/tdd/refactoring.md +0 -10
  311. package/.agents/skills/tdd/tests.md +0 -61
  312. package/.agents/skills/tool-design/SKILL.md +0 -273
  313. package/.agents/skills/tool-design/references/architectural_reduction.md +0 -210
  314. package/.agents/skills/tool-design/references/best_practices.md +0 -176
  315. package/.agents/skills/tool-design/scripts/description_generator.py +0 -528
  316. package/.agents/skills/typescript-advanced-types/SKILL.md +0 -720
  317. package/.agents/skills/typescript-expert/SKILL.md +0 -434
  318. package/.agents/skills/typescript-expert/references/tsconfig-strict.json +0 -92
  319. package/.agents/skills/typescript-expert/references/typescript-cheatsheet.md +0 -383
  320. package/.agents/skills/typescript-expert/references/utility-types.ts +0 -335
  321. package/.agents/skills/typescript-expert/scripts/ts_diagnostic.py +0 -203
  322. package/.agents/skills/typescript-react-reviewer/SKILL.md +0 -201
  323. package/.agents/skills/typescript-react-reviewer/references/antipatterns.md +0 -510
  324. package/.agents/skills/typescript-react-reviewer/references/checklist.md +0 -267
  325. package/.agents/skills/typescript-react-reviewer/references/react19-patterns.md +0 -305
  326. package/.agents/skills/workflow-creator/SKILL.md +0 -553
  327. package/.agents/skills/workflow-creator/references/agent-sessions.md +0 -891
  328. package/.agents/skills/workflow-creator/references/agent-setup-recipe.md +0 -266
  329. package/.agents/skills/workflow-creator/references/computation-and-validation.md +0 -201
  330. package/.agents/skills/workflow-creator/references/control-flow.md +0 -470
  331. package/.agents/skills/workflow-creator/references/failure-modes.md +0 -1014
  332. package/.agents/skills/workflow-creator/references/getting-started.md +0 -392
  333. package/.agents/skills/workflow-creator/references/registry-and-validation.md +0 -141
  334. package/.agents/skills/workflow-creator/references/running-workflows.md +0 -418
  335. package/.agents/skills/workflow-creator/references/session-config.md +0 -431
  336. package/.agents/skills/workflow-creator/references/state-and-data-flow.md +0 -356
  337. package/.agents/skills/workflow-creator/references/user-input.md +0 -234
  338. package/.agents/skills/workflow-creator/references/workflow-inputs.md +0 -392
  339. package/.agents/skills/xlsx/LICENSE.txt +0 -30
  340. package/.agents/skills/xlsx/SKILL.md +0 -294
  341. package/.agents/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  342. package/.agents/skills/xlsx/scripts/office/helpers/merge_runs.py +0 -199
  343. package/.agents/skills/xlsx/scripts/office/helpers/simplify_redlines.py +0 -197
  344. package/.agents/skills/xlsx/scripts/office/pack.py +0 -159
  345. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  346. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  347. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  348. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  349. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  350. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  351. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  352. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  353. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  354. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  355. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  356. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  357. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  358. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  359. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  360. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  361. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  362. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  363. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  364. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  365. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  366. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  367. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  368. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  369. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  370. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  371. package/.agents/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  372. package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  373. package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  374. package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  375. package/.agents/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  376. package/.agents/skills/xlsx/scripts/office/schemas/mce/mc.xsd +0 -75
  377. package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
  378. package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
  379. package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
  380. package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
  381. package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
  382. package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  383. package/.agents/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
  384. package/.agents/skills/xlsx/scripts/office/soffice.py +0 -183
  385. package/.agents/skills/xlsx/scripts/office/unpack.py +0 -132
  386. package/.agents/skills/xlsx/scripts/office/validate.py +0 -111
  387. package/.agents/skills/xlsx/scripts/office/validators/__init__.py +0 -15
  388. package/.agents/skills/xlsx/scripts/office/validators/base.py +0 -847
  389. package/.agents/skills/xlsx/scripts/office/validators/docx.py +0 -446
  390. package/.agents/skills/xlsx/scripts/office/validators/pptx.py +0 -275
  391. package/.agents/skills/xlsx/scripts/office/validators/redlining.py +0 -247
  392. package/.agents/skills/xlsx/scripts/recalc.py +0 -184
  393. package/.claude/agents/code-simplifier.md +0 -52
  394. package/.claude/agents/codebase-analyzer.md +0 -166
  395. package/.claude/agents/codebase-locator.md +0 -122
  396. package/.claude/agents/codebase-online-researcher.md +0 -148
  397. package/.claude/agents/codebase-pattern-finder.md +0 -247
  398. package/.claude/agents/codebase-research-analyzer.md +0 -179
  399. package/.claude/agents/codebase-research-locator.md +0 -145
  400. package/.claude/agents/debugger.md +0 -91
  401. package/.claude/agents/orchestrator.md +0 -19
  402. package/.claude/agents/planner.md +0 -295
  403. package/.claude/agents/reviewer.md +0 -98
  404. package/.claude/agents/worker.md +0 -165
  405. package/.claude/settings.json +0 -27
  406. package/.github/agents/code-simplifier.md +0 -52
  407. package/.github/agents/codebase-analyzer.md +0 -166
  408. package/.github/agents/codebase-locator.md +0 -122
  409. package/.github/agents/codebase-online-researcher.md +0 -146
  410. package/.github/agents/codebase-pattern-finder.md +0 -247
  411. package/.github/agents/codebase-research-analyzer.md +0 -179
  412. package/.github/agents/codebase-research-locator.md +0 -145
  413. package/.github/agents/debugger.md +0 -98
  414. package/.github/agents/orchestrator.md +0 -27
  415. package/.github/agents/planner.md +0 -305
  416. package/.github/agents/reviewer.md +0 -95
  417. package/.github/agents/worker.md +0 -237
  418. package/.github/lsp.json +0 -93
  419. package/.mcp.json +0 -20
  420. package/.opencode/agents/code-simplifier.md +0 -62
  421. package/.opencode/agents/codebase-analyzer.md +0 -171
  422. package/.opencode/agents/codebase-locator.md +0 -127
  423. package/.opencode/agents/codebase-online-researcher.md +0 -152
  424. package/.opencode/agents/codebase-pattern-finder.md +0 -252
  425. package/.opencode/agents/codebase-research-analyzer.md +0 -183
  426. package/.opencode/agents/codebase-research-locator.md +0 -149
  427. package/.opencode/agents/debugger.md +0 -99
  428. package/.opencode/agents/orchestrator.md +0 -27
  429. package/.opencode/agents/planner.md +0 -309
  430. package/.opencode/agents/reviewer.md +0 -103
  431. package/.opencode/agents/worker.md +0 -165
  432. package/.opencode/opencode.json +0 -25
  433. package/README.md +0 -1624
  434. package/assets/settings.schema.json +0 -51
  435. package/dist/commands/cli/claude-inflight-hook.d.ts +0 -100
  436. package/dist/commands/cli/claude-inflight-hook.d.ts.map +0 -1
  437. package/dist/commands/cli/claude-stop-hook.d.ts +0 -80
  438. package/dist/commands/cli/claude-stop-hook.d.ts.map +0 -1
  439. package/dist/lib/atomic-temp.d.ts +0 -8
  440. package/dist/lib/atomic-temp.d.ts.map +0 -1
  441. package/dist/lib/path-root-guard.d.ts +0 -4
  442. package/dist/lib/path-root-guard.d.ts.map +0 -1
  443. package/dist/lib/spawn.d.ts +0 -102
  444. package/dist/lib/spawn.d.ts.map +0 -1
  445. package/dist/lib/terminal-env.d.ts +0 -9
  446. package/dist/lib/terminal-env.d.ts.map +0 -1
  447. package/dist/sdk/components/attached-statusline.d.ts +0 -26
  448. package/dist/sdk/components/attached-statusline.d.ts.map +0 -1
  449. package/dist/sdk/components/color-utils.d.ts +0 -4
  450. package/dist/sdk/components/color-utils.d.ts.map +0 -1
  451. package/dist/sdk/components/compact-switcher.d.ts +0 -10
  452. package/dist/sdk/components/compact-switcher.d.ts.map +0 -1
  453. package/dist/sdk/components/connectors.d.ts +0 -16
  454. package/dist/sdk/components/connectors.d.ts.map +0 -1
  455. package/dist/sdk/components/edge.d.ts +0 -4
  456. package/dist/sdk/components/edge.d.ts.map +0 -1
  457. package/dist/sdk/components/error-boundary.d.ts +0 -23
  458. package/dist/sdk/components/error-boundary.d.ts.map +0 -1
  459. package/dist/sdk/components/graph-theme.d.ts +0 -18
  460. package/dist/sdk/components/graph-theme.d.ts.map +0 -1
  461. package/dist/sdk/components/header.d.ts +0 -3
  462. package/dist/sdk/components/header.d.ts.map +0 -1
  463. package/dist/sdk/components/hooks.d.ts +0 -15
  464. package/dist/sdk/components/hooks.d.ts.map +0 -1
  465. package/dist/sdk/components/layout.d.ts +0 -27
  466. package/dist/sdk/components/layout.d.ts.map +0 -1
  467. package/dist/sdk/components/node-card.d.ts +0 -10
  468. package/dist/sdk/components/node-card.d.ts.map +0 -1
  469. package/dist/sdk/components/orchestrator-panel-contexts.d.ts +0 -16
  470. package/dist/sdk/components/orchestrator-panel-contexts.d.ts.map +0 -1
  471. package/dist/sdk/components/orchestrator-panel-store.d.ts +0 -52
  472. package/dist/sdk/components/orchestrator-panel-store.d.ts.map +0 -1
  473. package/dist/sdk/components/orchestrator-panel-types.d.ts +0 -18
  474. package/dist/sdk/components/orchestrator-panel-types.d.ts.map +0 -1
  475. package/dist/sdk/components/orchestrator-panel.d.ts +0 -86
  476. package/dist/sdk/components/orchestrator-panel.d.ts.map +0 -1
  477. package/dist/sdk/components/renderer-background.d.ts +0 -9
  478. package/dist/sdk/components/renderer-background.d.ts.map +0 -1
  479. package/dist/sdk/components/session-graph-panel.d.ts +0 -7
  480. package/dist/sdk/components/session-graph-panel.d.ts.map +0 -1
  481. package/dist/sdk/components/status-helpers.d.ts +0 -6
  482. package/dist/sdk/components/status-helpers.d.ts.map +0 -1
  483. package/dist/sdk/components/statusline.d.ts +0 -5
  484. package/dist/sdk/components/statusline.d.ts.map +0 -1
  485. package/dist/sdk/components/tui-diagnostics.d.ts +0 -56
  486. package/dist/sdk/components/tui-diagnostics.d.ts.map +0 -1
  487. package/dist/sdk/components/workflow-picker-panel.d.ts +0 -126
  488. package/dist/sdk/components/workflow-picker-panel.d.ts.map +0 -1
  489. package/dist/sdk/define-workflow.d.ts +0 -107
  490. package/dist/sdk/define-workflow.d.ts.map +0 -1
  491. package/dist/sdk/errors.d.ts +0 -46
  492. package/dist/sdk/errors.d.ts.map +0 -1
  493. package/dist/sdk/index.d.ts +0 -26
  494. package/dist/sdk/index.d.ts.map +0 -1
  495. package/dist/sdk/primitives/inputs.d.ts +0 -36
  496. package/dist/sdk/primitives/inputs.d.ts.map +0 -1
  497. package/dist/sdk/primitives/metadata.d.ts +0 -40
  498. package/dist/sdk/primitives/metadata.d.ts.map +0 -1
  499. package/dist/sdk/primitives/run.d.ts +0 -57
  500. package/dist/sdk/primitives/run.d.ts.map +0 -1
  501. package/dist/sdk/primitives/sessions.d.ts +0 -128
  502. package/dist/sdk/primitives/sessions.d.ts.map +0 -1
  503. package/dist/sdk/providers/claude.d.ts +0 -392
  504. package/dist/sdk/providers/claude.d.ts.map +0 -1
  505. package/dist/sdk/providers/copilot.d.ts +0 -55
  506. package/dist/sdk/providers/copilot.d.ts.map +0 -1
  507. package/dist/sdk/providers/opencode.d.ts +0 -27
  508. package/dist/sdk/providers/opencode.d.ts.map +0 -1
  509. package/dist/sdk/registry.d.ts +0 -27
  510. package/dist/sdk/registry.d.ts.map +0 -1
  511. package/dist/sdk/runtime/attached-footer.d.ts +0 -31
  512. package/dist/sdk/runtime/attached-footer.d.ts.map +0 -1
  513. package/dist/sdk/runtime/cc-debounce.d.ts +0 -29
  514. package/dist/sdk/runtime/cc-debounce.d.ts.map +0 -1
  515. package/dist/sdk/runtime/executor-env.d.ts +0 -20
  516. package/dist/sdk/runtime/executor-env.d.ts.map +0 -1
  517. package/dist/sdk/runtime/executor.d.ts +0 -265
  518. package/dist/sdk/runtime/executor.d.ts.map +0 -1
  519. package/dist/sdk/runtime/graph-inference.d.ts +0 -35
  520. package/dist/sdk/runtime/graph-inference.d.ts.map +0 -1
  521. package/dist/sdk/runtime/orchestrator-entry.d.ts +0 -26
  522. package/dist/sdk/runtime/orchestrator-entry.d.ts.map +0 -1
  523. package/dist/sdk/runtime/panel.d.ts +0 -9
  524. package/dist/sdk/runtime/panel.d.ts.map +0 -1
  525. package/dist/sdk/runtime/port-discovery.d.ts +0 -71
  526. package/dist/sdk/runtime/port-discovery.d.ts.map +0 -1
  527. package/dist/sdk/runtime/status-writer.d.ts +0 -101
  528. package/dist/sdk/runtime/status-writer.d.ts.map +0 -1
  529. package/dist/sdk/runtime/theme.d.ts +0 -33
  530. package/dist/sdk/runtime/theme.d.ts.map +0 -1
  531. package/dist/sdk/runtime/tmux.d.ts +0 -307
  532. package/dist/sdk/runtime/tmux.d.ts.map +0 -1
  533. package/dist/sdk/runtime/version-compat.d.ts +0 -28
  534. package/dist/sdk/runtime/version-compat.d.ts.map +0 -1
  535. package/dist/sdk/types.d.ts +0 -435
  536. package/dist/sdk/types.d.ts.map +0 -1
  537. package/dist/sdk/worker-shared.d.ts +0 -42
  538. package/dist/sdk/worker-shared.d.ts.map +0 -1
  539. package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts +0 -81
  540. package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts.map +0 -1
  541. package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts +0 -37
  542. package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts.map +0 -1
  543. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/batching.d.ts +0 -43
  544. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/batching.d.ts.map +0 -1
  545. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts +0 -14
  546. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts.map +0 -1
  547. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts +0 -136
  548. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts.map +0 -1
  549. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts +0 -58
  550. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts.map +0 -1
  551. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scratch.d.ts +0 -43
  552. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scratch.d.ts.map +0 -1
  553. package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts +0 -37
  554. package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts.map +0 -1
  555. package/dist/sdk/workflows/builtin/open-claude-design/claude/index.d.ts +0 -68
  556. package/dist/sdk/workflows/builtin/open-claude-design/claude/index.d.ts.map +0 -1
  557. package/dist/sdk/workflows/builtin/open-claude-design/copilot/index.d.ts +0 -56
  558. package/dist/sdk/workflows/builtin/open-claude-design/copilot/index.d.ts.map +0 -1
  559. package/dist/sdk/workflows/builtin/open-claude-design/helpers/constants.d.ts +0 -72
  560. package/dist/sdk/workflows/builtin/open-claude-design/helpers/constants.d.ts.map +0 -1
  561. package/dist/sdk/workflows/builtin/open-claude-design/helpers/design-system.d.ts +0 -46
  562. package/dist/sdk/workflows/builtin/open-claude-design/helpers/design-system.d.ts.map +0 -1
  563. package/dist/sdk/workflows/builtin/open-claude-design/helpers/export.d.ts +0 -32
  564. package/dist/sdk/workflows/builtin/open-claude-design/helpers/export.d.ts.map +0 -1
  565. package/dist/sdk/workflows/builtin/open-claude-design/helpers/import.d.ts +0 -33
  566. package/dist/sdk/workflows/builtin/open-claude-design/helpers/import.d.ts.map +0 -1
  567. package/dist/sdk/workflows/builtin/open-claude-design/helpers/prompts.d.ts +0 -106
  568. package/dist/sdk/workflows/builtin/open-claude-design/helpers/prompts.d.ts.map +0 -1
  569. package/dist/sdk/workflows/builtin/open-claude-design/helpers/scan.d.ts +0 -50
  570. package/dist/sdk/workflows/builtin/open-claude-design/helpers/scan.d.ts.map +0 -1
  571. package/dist/sdk/workflows/builtin/open-claude-design/helpers/validation.d.ts +0 -12
  572. package/dist/sdk/workflows/builtin/open-claude-design/helpers/validation.d.ts.map +0 -1
  573. package/dist/sdk/workflows/builtin/open-claude-design/opencode/index.d.ts +0 -58
  574. package/dist/sdk/workflows/builtin/open-claude-design/opencode/index.d.ts.map +0 -1
  575. package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts +0 -37
  576. package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts.map +0 -1
  577. package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts +0 -34
  578. package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts.map +0 -1
  579. package/dist/sdk/workflows/builtin/ralph/helpers/copilot-reviewer.d.ts +0 -25
  580. package/dist/sdk/workflows/builtin/ralph/helpers/copilot-reviewer.d.ts.map +0 -1
  581. package/dist/sdk/workflows/builtin/ralph/helpers/git.d.ts +0 -69
  582. package/dist/sdk/workflows/builtin/ralph/helpers/git.d.ts.map +0 -1
  583. package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts +0 -266
  584. package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts.map +0 -1
  585. package/dist/sdk/workflows/builtin/ralph/helpers/review.d.ts +0 -24
  586. package/dist/sdk/workflows/builtin/ralph/helpers/review.d.ts.map +0 -1
  587. package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts +0 -33
  588. package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts.map +0 -1
  589. package/dist/sdk/workflows/index.d.ts +0 -32
  590. package/dist/sdk/workflows/index.d.ts.map +0 -1
  591. package/dist/services/config/additional-instructions.d.ts +0 -67
  592. package/dist/services/config/additional-instructions.d.ts.map +0 -1
  593. package/dist/services/config/atomic-config.d.ts +0 -42
  594. package/dist/services/config/atomic-config.d.ts.map +0 -1
  595. package/dist/services/config/definitions.d.ts +0 -52
  596. package/dist/services/config/definitions.d.ts.map +0 -1
  597. package/dist/services/config/index.d.ts +0 -7
  598. package/dist/services/config/index.d.ts.map +0 -1
  599. package/dist/services/config/scm-sync.d.ts +0 -37
  600. package/dist/services/config/scm-sync.d.ts.map +0 -1
  601. package/dist/services/config/settings-schema.d.ts +0 -2
  602. package/dist/services/config/settings-schema.d.ts.map +0 -1
  603. package/dist/services/system/copy.d.ts +0 -84
  604. package/dist/services/system/copy.d.ts.map +0 -1
  605. package/dist/services/system/detect.d.ts +0 -75
  606. package/dist/services/system/detect.d.ts.map +0 -1
  607. package/dist/theme/colors.d.ts +0 -35
  608. package/dist/theme/colors.d.ts.map +0 -1
  609. package/src/cli.ts +0 -397
  610. package/src/commands/builtin-registry.ts +0 -37
  611. package/src/commands/cli/chat/index.test.ts +0 -252
  612. package/src/commands/cli/chat/index.ts +0 -430
  613. package/src/commands/cli/chat.ts +0 -8
  614. package/src/commands/cli/claude-ask-hook.test.ts +0 -128
  615. package/src/commands/cli/claude-ask-hook.ts +0 -84
  616. package/src/commands/cli/claude-inflight-hook.test.ts +0 -598
  617. package/src/commands/cli/claude-inflight-hook.ts +0 -359
  618. package/src/commands/cli/claude-session-start-hook.ts +0 -61
  619. package/src/commands/cli/claude-stop-hook.test.ts +0 -317
  620. package/src/commands/cli/claude-stop-hook.ts +0 -441
  621. package/src/commands/cli/completions.ts +0 -24
  622. package/src/commands/cli/config.ts +0 -80
  623. package/src/commands/cli/footer.tsx +0 -248
  624. package/src/commands/cli/init/index.ts +0 -41
  625. package/src/commands/cli/init/onboarding.ts +0 -61
  626. package/src/commands/cli/init.ts +0 -8
  627. package/src/commands/cli/management-commands.ts +0 -112
  628. package/src/commands/cli/session.test.ts +0 -830
  629. package/src/commands/cli/session.ts +0 -447
  630. package/src/commands/cli/workflow-command.test.ts +0 -618
  631. package/src/commands/cli/workflow-inputs.test.ts +0 -353
  632. package/src/commands/cli/workflow-inputs.ts +0 -266
  633. package/src/commands/cli/workflow-list.test.ts +0 -235
  634. package/src/commands/cli/workflow-list.ts +0 -0
  635. package/src/commands/cli/workflow-status.test.ts +0 -451
  636. package/src/commands/cli/workflow-status.ts +0 -330
  637. package/src/commands/cli/workflow.ts +0 -196
  638. package/src/completions/bash.ts +0 -102
  639. package/src/completions/fish.ts +0 -136
  640. package/src/completions/index.ts +0 -7
  641. package/src/completions/powershell.ts +0 -195
  642. package/src/completions/zsh.ts +0 -150
  643. package/src/lib/atomic-temp.test.ts +0 -86
  644. package/src/lib/atomic-temp.ts +0 -62
  645. package/src/lib/common-ignore.ts +0 -46
  646. package/src/lib/merge.ts +0 -103
  647. package/src/lib/path-root-guard.ts +0 -38
  648. package/src/lib/spawn.test.ts +0 -109
  649. package/src/lib/spawn.ts +0 -678
  650. package/src/lib/terminal-env.test.ts +0 -343
  651. package/src/lib/terminal-env.ts +0 -100
  652. package/src/scripts/bump-version.ts +0 -94
  653. package/src/scripts/bundle-configs.ts +0 -116
  654. package/src/scripts/clean-dist.test.ts +0 -53
  655. package/src/scripts/clean-dist.ts +0 -37
  656. package/src/scripts/constants-base.ts +0 -14
  657. package/src/scripts/constants.ts +0 -35
  658. package/src/sdk/components/attached-statusline.tsx +0 -86
  659. package/src/sdk/components/color-utils.ts +0 -20
  660. package/src/sdk/components/compact-switcher.tsx +0 -78
  661. package/src/sdk/components/connectors.test.ts +0 -707
  662. package/src/sdk/components/connectors.ts +0 -160
  663. package/src/sdk/components/edge.tsx +0 -13
  664. package/src/sdk/components/error-boundary.tsx +0 -38
  665. package/src/sdk/components/graph-theme.ts +0 -37
  666. package/src/sdk/components/header.tsx +0 -85
  667. package/src/sdk/components/hooks.ts +0 -21
  668. package/src/sdk/components/layout.test.ts +0 -1245
  669. package/src/sdk/components/layout.ts +0 -223
  670. package/src/sdk/components/node-card.tsx +0 -91
  671. package/src/sdk/components/orchestrator-panel-contexts.ts +0 -35
  672. package/src/sdk/components/orchestrator-panel-store.test.ts +0 -847
  673. package/src/sdk/components/orchestrator-panel-store.ts +0 -187
  674. package/src/sdk/components/orchestrator-panel-types.ts +0 -23
  675. package/src/sdk/components/orchestrator-panel.tsx +0 -262
  676. package/src/sdk/components/renderer-background.ts +0 -49
  677. package/src/sdk/components/session-graph-panel.tsx +0 -471
  678. package/src/sdk/components/status-helpers.ts +0 -33
  679. package/src/sdk/components/statusline.tsx +0 -68
  680. package/src/sdk/components/tui-diagnostics.ts +0 -273
  681. package/src/sdk/components/workflow-picker-panel.tsx +0 -1613
  682. package/src/sdk/define-workflow.test.ts +0 -354
  683. package/src/sdk/define-workflow.ts +0 -275
  684. package/src/sdk/errors.test.ts +0 -83
  685. package/src/sdk/errors.ts +0 -77
  686. package/src/sdk/index.test.ts +0 -92
  687. package/src/sdk/index.ts +0 -101
  688. package/src/sdk/primitives/inputs.ts +0 -48
  689. package/src/sdk/primitives/metadata.ts +0 -63
  690. package/src/sdk/primitives/run.ts +0 -81
  691. package/src/sdk/primitives/sessions.test.ts +0 -594
  692. package/src/sdk/primitives/sessions.ts +0 -328
  693. package/src/sdk/providers/claude.ts +0 -1450
  694. package/src/sdk/providers/copilot.test.ts +0 -365
  695. package/src/sdk/providers/copilot.ts +0 -185
  696. package/src/sdk/providers/headless-hil-policy.test.ts +0 -211
  697. package/src/sdk/providers/opencode.ts +0 -88
  698. package/src/sdk/registry.ts +0 -132
  699. package/src/sdk/runtime/attached-footer.ts +0 -155
  700. package/src/sdk/runtime/cc-debounce.ts +0 -104
  701. package/src/sdk/runtime/executor-env.ts +0 -45
  702. package/src/sdk/runtime/executor.test.ts +0 -1321
  703. package/src/sdk/runtime/executor.ts +0 -2136
  704. package/src/sdk/runtime/graph-inference.ts +0 -50
  705. package/src/sdk/runtime/orchestrator-entry.ts +0 -110
  706. package/src/sdk/runtime/panel.tsx +0 -9
  707. package/src/sdk/runtime/port-discovery.test.ts +0 -573
  708. package/src/sdk/runtime/port-discovery.ts +0 -496
  709. package/src/sdk/runtime/status-writer.test.ts +0 -245
  710. package/src/sdk/runtime/status-writer.ts +0 -201
  711. package/src/sdk/runtime/theme.ts +0 -71
  712. package/src/sdk/runtime/tmux.conf +0 -112
  713. package/src/sdk/runtime/tmux.ts +0 -785
  714. package/src/sdk/runtime/version-compat.ts +0 -68
  715. package/src/sdk/types.ts +0 -548
  716. package/src/sdk/worker-shared.test.ts +0 -163
  717. package/src/sdk/worker-shared.ts +0 -155
  718. package/src/sdk/workflows/builtin/deep-research-codebase/claude/index.ts +0 -569
  719. package/src/sdk/workflows/builtin/deep-research-codebase/copilot/index.ts +0 -481
  720. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/batching.ts +0 -65
  721. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.ts +0 -24
  722. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/ignore-by-default.d.ts +0 -8
  723. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.ts +0 -958
  724. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/scout.ts +0 -505
  725. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/scratch.ts +0 -115
  726. package/src/sdk/workflows/builtin/deep-research-codebase/opencode/index.ts +0 -530
  727. package/src/sdk/workflows/builtin/open-claude-design/claude/index.ts +0 -500
  728. package/src/sdk/workflows/builtin/open-claude-design/copilot/index.ts +0 -508
  729. package/src/sdk/workflows/builtin/open-claude-design/helpers/constants.ts +0 -159
  730. package/src/sdk/workflows/builtin/open-claude-design/helpers/design-system.ts +0 -88
  731. package/src/sdk/workflows/builtin/open-claude-design/helpers/export.ts +0 -193
  732. package/src/sdk/workflows/builtin/open-claude-design/helpers/import.ts +0 -52
  733. package/src/sdk/workflows/builtin/open-claude-design/helpers/prompts.ts +0 -1110
  734. package/src/sdk/workflows/builtin/open-claude-design/helpers/scan.ts +0 -117
  735. package/src/sdk/workflows/builtin/open-claude-design/helpers/validation.ts +0 -38
  736. package/src/sdk/workflows/builtin/open-claude-design/opencode/index.ts +0 -610
  737. package/src/sdk/workflows/builtin/ralph/claude/index.ts +0 -272
  738. package/src/sdk/workflows/builtin/ralph/copilot/index.ts +0 -298
  739. package/src/sdk/workflows/builtin/ralph/helpers/copilot-reviewer.ts +0 -105
  740. package/src/sdk/workflows/builtin/ralph/helpers/git.ts +0 -201
  741. package/src/sdk/workflows/builtin/ralph/helpers/prompts.ts +0 -1108
  742. package/src/sdk/workflows/builtin/ralph/helpers/review.ts +0 -33
  743. package/src/sdk/workflows/builtin/ralph/opencode/index.ts +0 -290
  744. package/src/sdk/workflows/index.ts +0 -116
  745. package/src/services/config/additional-instructions.ts +0 -273
  746. package/src/services/config/atomic-config.ts +0 -210
  747. package/src/services/config/atomic-global-config.ts +0 -348
  748. package/src/services/config/config-path.ts +0 -19
  749. package/src/services/config/definitions.ts +0 -125
  750. package/src/services/config/index.ts +0 -7
  751. package/src/services/config/scm-sync.ts +0 -185
  752. package/src/services/config/settings-schema.ts +0 -2
  753. package/src/services/config/settings.ts +0 -144
  754. package/src/services/system/agents.ts +0 -95
  755. package/src/services/system/auth.test.ts +0 -343
  756. package/src/services/system/auth.ts +0 -140
  757. package/src/services/system/auto-sync.ts +0 -128
  758. package/src/services/system/copy.ts +0 -392
  759. package/src/services/system/detect.ts +0 -161
  760. package/src/services/system/file-lock.ts +0 -289
  761. package/src/services/system/install-ui.ts +0 -296
  762. package/src/services/system/skills.ts +0 -58
  763. package/src/theme/colors.ts +0 -96
  764. package/src/theme/logo.ts +0 -123
  765. package/src/version.ts +0 -7
@@ -1,2136 +0,0 @@
1
- /**
2
- * Workflow runtime executor.
3
- *
4
- * Architecture:
5
- * 1. `executeWorkflow()` is called by the CLI command (e.g. atomic) or by
6
- * the SDK's `runWorkflow()` primitive
7
- * 2. It creates a tmux session with an orchestrator pane that runs the
8
- * SDK-owned `orchestrator-entry.ts` with three positional args:
9
- * `<workflowSource> <agent> <inputsB64>`
10
- * 3. The CLI then attaches to the tmux session (user sees it live)
11
- * 4. The orchestrator pane imports the workflow module by `source`,
12
- * calls `runOrchestrator(definition, inputs)`, which then calls
13
- * `definition.run(workflowCtx)` — the user's callback uses
14
- * `ctx.stage()` to spawn agent sessions
15
- *
16
- * The dev's CLI is never re-imported. The SDK orchestrator entry script
17
- * is the only re-exec target, so there is no orchestrator-mode env var
18
- * re-entry signal and no boilerplate in user code.
19
- */
20
-
21
- import { join } from "node:path";
22
- import { homedir } from "node:os";
23
- import { writeFile } from "node:fs/promises";
24
- import { statSync, accessSync, constants as fsConstants } from "node:fs";
25
- import type {
26
- WorkflowDefinition,
27
- WorkflowContext,
28
- WorkflowInput,
29
- SessionContext,
30
- SessionRunOptions,
31
- SessionHandle,
32
- SessionRef,
33
- AgentType,
34
- Transcript,
35
- SavedMessage,
36
- SaveTranscript,
37
- StageClientOptions,
38
- StageSessionOptions,
39
- ProviderClient,
40
- ProviderSession,
41
- } from "../types.ts";
42
- import { type ProviderOverrides } from "../../services/config/definitions.ts";
43
- import { getProviderOverrides } from "../../services/config/atomic-config.ts";
44
- import { getCopilotScmDisableFlags } from "../../services/config/scm-sync.ts";
45
- import { reconcileOpencodeInstructions } from "../../services/config/additional-instructions.ts";
46
- import { ensureDir } from "../../services/system/copy.ts";
47
- import type { SessionEvent } from "@github/copilot-sdk";
48
- import type { SessionPromptResponse } from "@opencode-ai/sdk/v2";
49
- import type { SessionMessage } from "@anthropic-ai/claude-agent-sdk";
50
- import * as tmux from "./tmux.ts";
51
- import { spawnMuxAttach } from "./tmux.ts";
52
- import {
53
- getListeningPortForPid,
54
- PORT_DISCOVERY_TIMEOUT_MS,
55
- } from "./port-discovery.ts";
56
- import { spawnAttachedFooter } from "./attached-footer.ts";
57
- import {
58
- clearClaudeSession,
59
- ClaudeClientWrapper,
60
- ClaudeSessionWrapper,
61
- HeadlessClaudeClientWrapper,
62
- HeadlessClaudeSessionWrapper,
63
- } from "../providers/claude.ts";
64
- import { withHeadlessOpencodeEnv } from "../providers/opencode.ts";
65
- import { resolveCopilotCliPath } from "../providers/copilot.ts";
66
- import { OrchestratorPanel } from "./panel.tsx";
67
- import { GraphFrontierTracker } from "./graph-inference.ts";
68
- import { buildSnapshot, writeSnapshot } from "./status-writer.ts";
69
- import { errorMessage } from "../errors.ts";
70
- import { createPainter } from "../../theme/colors.ts";
71
- import { atomicTempEnv } from "../../lib/atomic-temp.ts";
72
-
73
- /** Maximum time (ms) for the SDK probe to succeed after port is discovered. */
74
- export const SERVER_PROBE_TIMEOUT_MS = 60_000;
75
-
76
- /** Agent CLI configuration for spawning in tmux panes. */
77
- const AGENT_CLI: Record<
78
- AgentType,
79
- { cmd: string; chatFlags: string[]; envVars: Record<string, string> }
80
- > = {
81
- copilot: {
82
- cmd: "copilot",
83
- chatFlags: ["--add-dir", ".", "--yolo", "--experimental"],
84
- envVars: {
85
- COPILOT_ALLOW_ALL: "true",
86
- },
87
- },
88
- opencode: { cmd: "opencode", chatFlags: [], envVars: {} },
89
- claude: {
90
- cmd: "claude",
91
- chatFlags: [
92
- "--allow-dangerously-skip-permissions",
93
- "--dangerously-skip-permissions",
94
- ],
95
- envVars: {
96
- // Enables session_state_changed events in the session JSONL transcript,
97
- // which the idle detection in claude.ts watches for to know when the
98
- // agent has finished processing a prompt.
99
- CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: "1",
100
- },
101
- },
102
- };
103
-
104
- /** Thrown when the user aborts a running workflow via `q` or `Ctrl+C`. */
105
- class WorkflowAbortError extends Error {
106
- constructor() {
107
- super("Workflow aborted by user");
108
- this.name = "WorkflowAbortError";
109
- }
110
- }
111
-
112
- /** Compile-time exhaustiveness guard for discriminated unions. */
113
- function assertNever(value: never): never {
114
- throw new Error(`Unhandled agent type: ${String(value)}`);
115
- }
116
-
117
- // Re-export for backward compatibility (tests import from here)
118
- export { errorMessage } from "../errors.ts";
119
-
120
- /** Runtime guard for deserialized SavedMessage objects. */
121
- function isValidSavedMessage(msg: unknown): msg is SavedMessage {
122
- if (!msg || typeof msg !== "object") return false;
123
- const m = msg as Record<string, unknown>;
124
- return (
125
- m.provider === "copilot" ||
126
- m.provider === "opencode" ||
127
- m.provider === "claude"
128
- );
129
- }
130
-
131
- export interface WorkflowRunOptions {
132
- /** The compiled workflow definition */
133
- definition: WorkflowDefinition;
134
- /** Agent type */
135
- agent: AgentType;
136
- /**
137
- * Structured inputs for this run. Free-form workflows model their
138
- * single positional prompt as `{ prompt: "..." }` so workflow
139
- * authors can read `ctx.inputs.prompt` uniformly regardless of
140
- * whether the workflow declares a schema. Empty record is valid.
141
- */
142
- inputs?: Record<string, string>;
143
- /** Project root (defaults to cwd) */
144
- projectRoot?: string;
145
- /**
146
- * When true, create the tmux session and return immediately instead
147
- * of attaching. The orchestrator keeps running in the background on
148
- * the atomic tmux socket; users can attach later with
149
- * `atomic workflow session connect <name>`.
150
- */
151
- detach?: boolean;
152
- }
153
-
154
- interface SessionResult {
155
- name: string;
156
- sessionId: string;
157
- sessionDir: string;
158
- paneId: string;
159
- }
160
-
161
- /** A session that has been spawned but may not have completed yet. */
162
- interface ActiveSession {
163
- name: string;
164
- paneId: string;
165
- /** Settles when the session finishes. Resolves on success, rejects on failure. */
166
- done: Promise<void>;
167
- }
168
-
169
- function generateId(): string {
170
- return crypto.randomUUID().slice(0, 8);
171
- }
172
-
173
- function getSessionsBaseDir(): string {
174
- return join(homedir(), ".atomic", "sessions");
175
- }
176
-
177
- /**
178
- * Resolve a non-JS Copilot CLI binary on PATH.
179
- *
180
- * Under Bun, `@github/copilot-sdk` spawns its bundled JS entry via `node`
181
- * (see `getNodeExecPath` in the SDK). If `node` isn't installed — common in
182
- * minimal containers — the spawn fails silently with ENOENT and the SDK's
183
- * write to the child's stdin surfaces as "Cannot call write after a stream
184
- * was destroyed" from vscode-jsonrpc. Pointing the SDK at a standalone
185
- * `copilot` binary (the npm-installed ELF executable) sidesteps the
186
- * node-vs-bun problem because the SDK execs it directly when the path does
187
- * not end in `.js`.
188
- *
189
- * Returns undefined if no suitable binary is found.
190
- */
191
- export function discoverCopilotBinary(): string | undefined {
192
- const pathVar = process.env.PATH;
193
- if (!pathVar) return undefined;
194
- // Windows: only `copilot.exe` is probed. Bun's global install writes a
195
- // real `.exe` shim, so this covers the Bun-container scenario this guard
196
- // exists for. Pre-existing npm-installed shims (`copilot.cmd`/`.ps1`)
197
- // aren't handled — the entire override is gated on `process.versions.bun`.
198
- const exe = process.platform === "win32" ? "copilot.exe" : "copilot";
199
- const sep = process.platform === "win32" ? ";" : ":";
200
- for (const dir of pathVar.split(sep)) {
201
- if (!dir) continue;
202
- const candidate = join(dir, exe);
203
- if (!isExecutableFile(candidate)) continue;
204
- return candidate;
205
- }
206
- return undefined;
207
- }
208
-
209
- /**
210
- * True when we need to override the SDK's default CLI path — i.e. running
211
- * under Bun, the user hasn't set COPILOT_CLI_PATH, and `node` is not
212
- * available to execute the SDK's bundled JS entry.
213
- *
214
- * Pure predicate on the current env; safe to call repeatedly.
215
- */
216
- export function shouldOverrideCopilotCliPath(): boolean {
217
- if (!process.versions.bun) return false;
218
- if (process.env.COPILOT_CLI_PATH) return false;
219
- if (isNodeOnPath()) return false;
220
- return discoverCopilotBinary() !== undefined;
221
- }
222
-
223
- function isExecutableFile(path: string): boolean {
224
- try {
225
- if (!statSync(path).isFile()) return false;
226
- if (process.platform === "win32") return true;
227
- accessSync(path, fsConstants.X_OK);
228
- return true;
229
- } catch {
230
- return false;
231
- }
232
- }
233
-
234
- function isNodeOnPath(): boolean {
235
- const pathVar = process.env.PATH;
236
- if (!pathVar) return false;
237
- const exe = process.platform === "win32" ? "node.exe" : "node";
238
- const sep = process.platform === "win32" ? ";" : ":";
239
- for (const dir of pathVar.split(sep)) {
240
- if (!dir) continue;
241
- if (isExecutableFile(join(dir, exe))) return true;
242
- }
243
- return false;
244
- }
245
-
246
- /**
247
- * Set safe env defaults for the orchestrator process before any SDK is
248
- * loaded. Idempotent — subsequent calls no-op once `COPILOT_CLI_PATH`
249
- * is set. Call as early as possible so headless Copilot subprocesses
250
- * inherit the resolved env.
251
- */
252
- export function applyContainerEnvDefaults(): void {
253
- if (!process.versions.bun) return;
254
- if (process.env.COPILOT_CLI_PATH) return;
255
- if (isNodeOnPath()) return;
256
- const bin = discoverCopilotBinary();
257
- if (bin) process.env.COPILOT_CLI_PATH = bin;
258
- }
259
-
260
- /**
261
- * Resolve a CLI binary name to its absolute path using the parent atomic
262
- * process's PATH. tmux's child shell can have a stripped or differently
263
- * ordered PATH from the user's interactive shell — most visibly when atomic
264
- * is launched from a globally-installed bin wrapper rather than `bun run dev`.
265
- * Resolving here, where we still have the full interactive PATH, mirrors
266
- * how `attached-footer.ts` injects `process.execPath` + an absolute cli.ts
267
- * path so the footer always spawns regardless of the child shell's PATH.
268
- *
269
- * Falls back to the bare name when the binary isn't found on PATH so behavior
270
- * stays unchanged for callers running entirely inside a normal interactive shell.
271
- */
272
- function resolveCliBinary(cmd: string): string {
273
- return Bun.which(cmd, { PATH: process.env.PATH ?? "" }) ?? cmd;
274
- }
275
-
276
- /** Wrap a path in bash double quotes only when it contains shell-significant characters. */
277
- function quotePathIfNeeded(path: string): string {
278
- return /[\s'"$`!\\]/.test(path) ? `"${escBash(path)}"` : path;
279
- }
280
-
281
- export function buildPaneCommand(
282
- agent: AgentType,
283
- overrides: ProviderOverrides = {},
284
- extraChatFlags: string[] = [],
285
- ): { command: string; envVars: Record<string, string> } {
286
- const {
287
- cmd,
288
- chatFlags: defaultFlags,
289
- envVars: defaultEnvVars,
290
- } = AGENT_CLI[agent];
291
- const chatFlags = overrides.chatFlags ?? defaultFlags;
292
- const claudeTempEnv = agent === "claude" ? atomicTempEnv() : {};
293
- const envVars = overrides.envVars
294
- ? { ...defaultEnvVars, ...overrides.envVars }
295
- : defaultEnvVars;
296
- const mergedEnvVars = { ...envVars, ...claudeTempEnv, ...overrides.envVars };
297
-
298
- const resolvedCmd = quotePathIfNeeded(resolveCliBinary(cmd));
299
-
300
- switch (agent) {
301
- case "copilot": {
302
- // Prefer the copilot binary resolved via resolveCopilotCliPath so that
303
- // COPILOT_CLI_PATH (set by applyContainerEnvDefaults in Bun-without-node
304
- // environments) is honoured in the tmux pane command, keeping the pane
305
- // binary consistent with the SDK subprocess binary.
306
- const copilotBin = resolveCopilotCliPath() ?? resolveCliBinary(cmd);
307
- return {
308
- command: [
309
- quotePathIfNeeded(copilotBin),
310
- "--ui-server",
311
- "--port",
312
- "0",
313
- ...chatFlags,
314
- ...extraChatFlags,
315
- ].join(" "),
316
- envVars: mergedEnvVars,
317
- };
318
- }
319
- case "opencode":
320
- return {
321
- command: [resolvedCmd, "--port", "0", ...chatFlags].join(" "),
322
- envVars: mergedEnvVars,
323
- };
324
- case "claude": {
325
- // Claude is started via createClaudeSession() in the workflow's run().
326
- // Resolve $SHELL (or the platform default) to an absolute path for the
327
- // same reason the agent CLIs are resolved above.
328
- const fallback = process.platform === "win32" ? "pwsh" : "sh";
329
- const shellCandidate = process.env.SHELL || fallback;
330
- const resolvedShell =
331
- shellCandidate.includes("/") || shellCandidate.includes("\\")
332
- ? shellCandidate
333
- : resolveCliBinary(shellCandidate);
334
- return {
335
- command: quotePathIfNeeded(resolvedShell),
336
- envVars: mergedEnvVars,
337
- };
338
- }
339
- default:
340
- return assertNever(agent);
341
- }
342
- }
343
-
344
- export async function waitForServer(
345
- agent: AgentType,
346
- paneId: string,
347
- ): Promise<string> {
348
- if (agent === "claude") return "";
349
-
350
- const portDeadline = Date.now() + PORT_DISCOVERY_TIMEOUT_MS;
351
-
352
- // 1. Wait for the agent process to start and the TUI to render.
353
- while (Date.now() < portDeadline) {
354
- const content = tmux.capturePane(paneId);
355
- const lines = content.split("\n").filter((l) => l.trim().length > 0);
356
- if (lines.length >= 3) break;
357
- await Bun.sleep(1_000);
358
- }
359
-
360
- // 2. Discover the listening port via the agent's PID.
361
- const panePid = tmux.getPanePid(paneId);
362
- if (!panePid) {
363
- throw new Error(`failed to resolve agent PID for pane ${paneId}`);
364
- }
365
- const remainingMs = Math.max(0, portDeadline - Date.now());
366
- const port = await getListeningPortForPid(panePid, {
367
- timeoutMs: remainingMs,
368
- });
369
- if (port === null) {
370
- throw new Error(
371
- `agent (${agent}) did not bind a TCP port within ${PORT_DISCOVERY_TIMEOUT_MS}ms ` +
372
- `(pane ${paneId}, pid ${panePid})`,
373
- );
374
- }
375
- const serverUrl = `localhost:${port}`;
376
-
377
- // 3. Verify the SDK can actually connect.
378
- if (agent === "copilot") {
379
- const probeDeadline = Date.now() + SERVER_PROBE_TIMEOUT_MS;
380
- const { CopilotClient } = await import("@github/copilot-sdk");
381
- while (Date.now() < probeDeadline) {
382
- try {
383
- const probe = new CopilotClient({ cliUrl: serverUrl });
384
- await probe.start();
385
- await probe.listSessions();
386
- await probe.stop();
387
- return serverUrl;
388
- } catch {
389
- await Bun.sleep(1_000);
390
- }
391
- }
392
- throw new Error(
393
- `copilot SDK probe did not respond at ${serverUrl} within ${SERVER_PROBE_TIMEOUT_MS}ms`,
394
- );
395
- }
396
-
397
- // OpenCode: short settle delay, then return.
398
- await Bun.sleep(1_000);
399
- return serverUrl;
400
- }
401
-
402
- /**
403
- * Escape a string for safe interpolation inside a bash double-quoted string.
404
- *
405
- * In bash `"..."` strings only `$`, `` ` ``, `\`, `"`, and `!` are special.
406
- * Single quotes are literal inside double quotes and need no escaping.
407
- * Null bytes are stripped because bash strings cannot contain them.
408
- */
409
- export function escBash(s: string): string {
410
- return s
411
- .replace(/\x00/g, "")
412
- .replace(/[\n\r]+/g, " ")
413
- .replace(/[\\"$`!]/g, "\\$&");
414
- }
415
-
416
- /**
417
- * Escape a string for safe interpolation inside a PowerShell double-quoted string.
418
- *
419
- * In PowerShell `"..."` strings, backtick is the escape character and `$` triggers
420
- * variable expansion. Null bytes are stripped for safety.
421
- */
422
- export function escPwsh(s: string): string {
423
- return s
424
- .replace(/\x00/g, "")
425
- .replace(/[`"$]/g, "`$&")
426
- .replace(/\n/g, "`n")
427
- .replace(/\r/g, "`r");
428
- }
429
-
430
- /**
431
- * Coerce raw string inputs to their declared runtime types. Integer inputs
432
- * become `number`; every other declared type passes through as `string`.
433
- * Unknown keys (not in the schema) are preserved as strings.
434
- *
435
- * Invalid integer strings fall back to the key being dropped — validation
436
- * already runs upstream (in `validateInputsAgainstSchema`), so reaching
437
- * this path with garbage means the executor was invoked out-of-band.
438
- */
439
- export function coerceInputsBySchema(
440
- inputs: Record<string, string>,
441
- schema: readonly WorkflowInput[],
442
- ): Record<string, string | number> {
443
- const byName = new Map(schema.map((f) => [f.name, f]));
444
- const out: Record<string, string | number> = {};
445
- for (const [k, v] of Object.entries(inputs)) {
446
- const field = byName.get(k);
447
- if (field?.type === "integer") {
448
- const parsed = Number.parseInt(v, 10);
449
- if (Number.isFinite(parsed) && Number.isInteger(parsed)) {
450
- out[k] = parsed;
451
- }
452
- continue;
453
- }
454
- out[k] = v;
455
- }
456
- return out;
457
- }
458
-
459
- // ============================================================================
460
- // Entry point called by the CLI command
461
- // ============================================================================
462
-
463
- /**
464
- * Called by `atomic workflow -n <name> -a <agent> <prompt>`.
465
- *
466
- * Always creates a tmux session in the atomic socket with the
467
- * orchestrator as the initial pane, then attaches so the user sees
468
- * everything live — even when invoked from inside another tmux session.
469
- */
470
- export async function executeWorkflow(
471
- options: WorkflowRunOptions,
472
- ): Promise<{ id: string; tmuxSessionName: string }> {
473
- const {
474
- definition,
475
- agent,
476
- inputs = {},
477
- projectRoot = process.cwd(),
478
- detach = false,
479
- } = options;
480
-
481
- // OpenCode reads its `instructions` array from `.opencode/opencode.json`
482
- // at server-start time — both for the interactive tmux-pane path and the
483
- // headless `createOpencode({ port: 0 })` path. Reconcile here, before
484
- // either spawn, so the resolved AGENTS.md is the source of truth on
485
- // every workflow run. Best-effort: a malformed config shouldn't block.
486
- if (agent === "opencode") {
487
- try {
488
- await reconcileOpencodeInstructions(projectRoot);
489
- } catch {
490
- /* swallow */
491
- }
492
- }
493
-
494
- const workflowRunId = generateId();
495
- const tmuxSessionName = `atomic-wf-${agent}-${definition.name}-${workflowRunId}`;
496
- const sessionsBaseDir = join(getSessionsBaseDir(), workflowRunId);
497
- await ensureDir(sessionsBaseDir);
498
-
499
- // Write a launcher script for the orchestrator pane.
500
- // Runs the SDK-owned orchestrator entry script with positional args:
501
- // bun <orchestrator-entry.ts> <workflowSource> <agent> <inputsB64>
502
- // The dev's own CLI is never re-execed.
503
- const isWin = process.platform === "win32";
504
- const launcherExt = isWin ? "ps1" : "sh";
505
- const launcherPath = join(sessionsBaseDir, `orchestrator.${launcherExt}`);
506
- const logPath = join(sessionsBaseDir, "orchestrator.log");
507
- const launcherEnvVars = {
508
- ...(agent === "claude" ? atomicTempEnv() : {}),
509
- ...terminalCapabilityEnv(),
510
- ...workflowDiagnosticsEnv(),
511
- };
512
-
513
- // Inputs are passed through as base64-encoded JSON so long multiline
514
- // text values survive shell quoting without any further escaping.
515
- // Free-form workflows ride the same pipe — their single positional
516
- // prompt is stored under the `prompt` key so workflow authors always
517
- // read the user's prompt via `ctx.inputs.prompt`.
518
- const inputsB64 = Buffer.from(JSON.stringify(inputs)).toString("base64");
519
-
520
- // Resolve the SDK's orchestrator entry script (sibling of this file).
521
- const orchestratorEntry = join(import.meta.dir, "orchestrator-entry.ts");
522
- const workflowSource = definition.source;
523
-
524
- // Resolve the bun binary to an absolute path here — `process.execPath` is
525
- // the exact bun interpreter currently running atomic, so we don't depend on
526
- // bare `bun` being on the tmux child shell's PATH (the same reason
527
- // `attached-footer.ts` uses it).
528
- const bunBinary = process.execPath;
529
-
530
- const launcherScript = isWin
531
- ? [
532
- `Set-Location "${escPwsh(projectRoot)}"`,
533
- ...Object.entries(launcherEnvVars).map(
534
- ([key, value]) => `$env:${key} = "${escPwsh(value)}"`,
535
- ),
536
- `$env:ATOMIC_WF_ID = "${escPwsh(workflowRunId)}"`,
537
- `$env:ATOMIC_WF_TMUX = "${escPwsh(tmuxSessionName)}"`,
538
- `$env:ATOMIC_WF_AGENT = "${escPwsh(agent)}"`,
539
- `$env:ATOMIC_WF_CWD = "${escPwsh(projectRoot)}"`,
540
- `& "${escPwsh(bunBinary)}" run "${escPwsh(orchestratorEntry)}" "${escPwsh(workflowSource)}" "${escPwsh(agent)}" "${escPwsh(inputsB64)}" 2>"${escPwsh(logPath)}"`,
541
- ].join("\n")
542
- : [
543
- "#!/bin/bash",
544
- `cd "${escBash(projectRoot)}"`,
545
- ...Object.entries(launcherEnvVars).map(
546
- ([key, value]) => `export ${key}="${escBash(value)}"`,
547
- ),
548
- `export ATOMIC_WF_ID="${escBash(workflowRunId)}"`,
549
- `export ATOMIC_WF_TMUX="${escBash(tmuxSessionName)}"`,
550
- `export ATOMIC_WF_AGENT="${escBash(agent)}"`,
551
- `export ATOMIC_WF_CWD="${escBash(projectRoot)}"`,
552
- `"${escBash(bunBinary)}" run "${escBash(orchestratorEntry)}" "${escBash(workflowSource)}" "${escBash(agent)}" "${escBash(inputsB64)}" 2>"${escBash(logPath)}"`,
553
- ].join("\n");
554
-
555
- await writeFile(launcherPath, launcherScript, { mode: 0o755 });
556
-
557
- const shellCmd = isWin
558
- ? `pwsh -NoProfile -File "${escPwsh(launcherPath)}"`
559
- : `bash "${escBash(launcherPath)}"`;
560
- tmux.createSession(tmuxSessionName, shellCmd, "orchestrator", undefined, launcherEnvVars);
561
- tmux.setSessionEnv(tmuxSessionName, "ATOMIC_AGENT", agent);
562
-
563
- if (detach) {
564
- // Session is already running detached on the atomic socket (tmux
565
- // new-session -d). Print connection hints and return so the caller
566
- // can exit cleanly without blocking on the orchestrator.
567
- printDetachedBanner(tmuxSessionName);
568
- return { id: workflowRunId, tmuxSessionName };
569
- }
570
-
571
- if (tmux.isInsideAtomicSocket()) {
572
- // Already on the atomic server — just switch to the new session.
573
- tmux.switchClient(tmuxSessionName);
574
- } else if (tmux.isInsideTmux()) {
575
- // Inside a different tmux server — detach and replace the client
576
- // with an attach to the atomic socket (no nesting).
577
- tmux.detachAndAttachAtomic(tmuxSessionName);
578
- } else {
579
- const attachProc = spawnMuxAttach(tmuxSessionName);
580
- await attachProc.exited;
581
- }
582
-
583
- return { id: workflowRunId, tmuxSessionName };
584
- }
585
-
586
- function workflowDiagnosticsEnv(): Record<string, string> {
587
- const keys = [
588
- "ATOMIC_TUI_DIAGNOSTICS",
589
- "ATOMIC_TUI_DIAGNOSTICS_DIR",
590
- "ATOMIC_TUI_DIAGNOSTICS_INTERVAL_MS",
591
- "ATOMIC_TUI_DIAGNOSTICS_MAX",
592
- "ATOMIC_TUI_DIAGNOSTICS_OPENTUI_DUMP",
593
- ];
594
- const env: Record<string, string> = {};
595
- for (const key of keys) {
596
- const value = process.env[key];
597
- if (value !== undefined) env[key] = value;
598
- }
599
- return env;
600
- }
601
-
602
- function terminalCapabilityEnv(): Record<string, string> {
603
- const env: Record<string, string> = {};
604
- const colorterm = process.env.COLORTERM;
605
- if (colorterm !== undefined) env.COLORTERM = colorterm;
606
- return env;
607
- }
608
-
609
- /**
610
- * Print a short banner telling the user the workflow is running in the
611
- * background and how to attach to it. Written to stdout so scripts can
612
- * capture the session name with a simple redirect.
613
- */
614
- function printDetachedBanner(tmuxSessionName: string): void {
615
- const paint = createPainter();
616
- process.stdout.write(
617
- "\n" +
618
- " " +
619
- paint("success", "✓") +
620
- " " +
621
- paint("text", "workflow started in background", { bold: true }) +
622
- "\n" +
623
- " " +
624
- paint("dim", "session: ") +
625
- paint("accent", tmuxSessionName) +
626
- "\n" +
627
- "\n" +
628
- " " +
629
- paint("dim", "attach: ") +
630
- paint("accent", `atomic workflow session connect ${tmuxSessionName}`) +
631
- "\n" +
632
- " " +
633
- paint("dim", "list: ") +
634
- paint("accent", "atomic workflow session list") +
635
- "\n" +
636
- " " +
637
- paint("dim", "kill: ") +
638
- paint("accent", `atomic workflow session kill ${tmuxSessionName}`) +
639
- "\n" +
640
- "\n",
641
- );
642
- }
643
-
644
- // ============================================================================
645
- // Session execution helpers
646
- // ============================================================================
647
-
648
- /**
649
- * Resolve the provider-specific session identifier for use as
650
- * `SessionContext.sessionId`:
651
- * - Claude interactive: `ClaudeSessionWrapper.sessionId` — the Claude UUID
652
- * set when `createClaudeSession` ran.
653
- * - Claude headless: `HeadlessClaudeSessionWrapper.sessionId` — the SDK
654
- * `session_id` from the most recently completed `query()` (empty string
655
- * until the first query returns).
656
- * - Copilot: `CopilotSession.sessionId`.
657
- * - OpenCode: `Session.id`.
658
- *
659
- * Returns an empty string for unknown shapes rather than throwing so
660
- * early-init readers of `s.sessionId` (e.g. logging) don't crash.
661
- */
662
- function resolveProviderSessionId(
663
- agent: AgentType,
664
- providerSession: unknown,
665
- ): string {
666
- if (!providerSession || typeof providerSession !== "object") return "";
667
- const obj = providerSession as Record<string, unknown>;
668
- if (agent === "opencode") {
669
- return typeof obj["id"] === "string" ? (obj["id"] as string) : "";
670
- }
671
- // claude and copilot both expose `sessionId` as a string.
672
- return typeof obj["sessionId"] === "string"
673
- ? (obj["sessionId"] as string)
674
- : "";
675
- }
676
-
677
- /** Type guard for objects with a string `content` property (Copilot assistant.message data). */
678
- export function hasContent(value: unknown): value is { content: string } {
679
- return (
680
- typeof value === "object" &&
681
- value !== null &&
682
- "content" in value &&
683
- typeof (value as { content: unknown }).content === "string"
684
- );
685
- }
686
-
687
- /**
688
- * Character budget cap for tool-call `input` payloads embedded in the
689
- * transcript. Tool call arguments can grow (diffs, large SQL strings, whole
690
- * files passed inline), and the transcript's primary consumer is a
691
- * downstream LLM that must `Read` this file as context for its own turn —
692
- * so we cap the per-call JSON at a predictable size. The suffix
693
- * `[+N chars]` preserves the dropped length for humans reviewing the file.
694
- *
695
- * Tool _results_ are intentionally NOT included in the transcript. File
696
- * contents, shell output, and search results inflate the transcript
697
- * dramatically and lead to context rot on the next stage. A reader (human
698
- * or model) can still reconstruct what the tool returned by looking at
699
- * the assistant's subsequent text — which is the whole point of the
700
- * assistant summarising its own work.
701
- */
702
- const TRANSCRIPT_TOOL_INPUT_BUDGET = 800;
703
-
704
- function truncateForTranscript(text: string, max: number): string {
705
- if (text.length <= max) return text;
706
- return text.slice(0, max) + ` … [+${text.length - max} chars]`;
707
- }
708
-
709
- /** Render a tool_use `input` object as a JSON-ish block, capped to budget. */
710
- function renderToolInput(input: unknown): string {
711
- let json: string;
712
- try {
713
- json = JSON.stringify(input, null, 2);
714
- } catch {
715
- json = String(input);
716
- }
717
- return truncateForTranscript(json, TRANSCRIPT_TOOL_INPUT_BUDGET);
718
- }
719
-
720
- /**
721
- * Render a Claude transcript as readable Markdown.
722
- *
723
- * Captures the user/agent interaction chronologically:
724
- * - User messages (string content) → `### User`
725
- * - Assistant text blocks → `### Assistant`
726
- * - Assistant `tool_use` blocks → `**→ \`Name\`**` + JSON input
727
- *
728
- * Intentionally omitted:
729
- * - `tool_result` blocks — their payloads (file contents, shell output,
730
- * stringified diffs) dominate the transcript and lead to context rot on
731
- * the next stage. The assistant's subsequent text response already
732
- * summarises what the tool returned; re-including the raw output
733
- * duplicates that information at high token cost.
734
- * - `thinking` blocks — verbose internal reasoning rarely useful when the
735
- * transcript is re-ingested as context elsewhere.
736
- * - `system` / `summary` / other non-message types.
737
- */
738
- function renderClaudeTranscript(
739
- messages: ReadonlyArray<{ type: string; message: unknown }>,
740
- ): string {
741
- const sections: string[] = [];
742
-
743
- for (const msg of messages) {
744
- if (msg.type !== "user" && msg.type !== "assistant") continue;
745
-
746
- // `message` shape is one of:
747
- // - a plain string (legacy path),
748
- // - `{ role, content: string }` (API-style plain text turn),
749
- // - `{ role, content: Block[] }` (tool-use / tool-result turns).
750
- // Normalise the first two into a single string; handle the third below.
751
- const rawMessage = msg.message;
752
- let plainText: string | null = null;
753
- let arrayContent: unknown[] | null = null;
754
-
755
- if (typeof rawMessage === "string") {
756
- plainText = rawMessage;
757
- } else if (rawMessage && typeof rawMessage === "object") {
758
- const content = (rawMessage as { content?: unknown }).content;
759
- if (typeof content === "string") {
760
- plainText = content;
761
- } else if (Array.isArray(content)) {
762
- arrayContent = content;
763
- }
764
- }
765
-
766
- if (plainText !== null) {
767
- const trimmed = plainText.trim();
768
- if (trimmed) {
769
- const header = msg.type === "user" ? "### User" : "### Assistant";
770
- sections.push(`${header}\n\n${trimmed}`);
771
- }
772
- continue;
773
- }
774
-
775
- if (arrayContent === null) continue;
776
- const content = arrayContent;
777
-
778
- if (msg.type === "assistant") {
779
- // Group all blocks from a single assistant message under one header
780
- // so text and tool calls read as one coherent turn.
781
- const parts: string[] = [];
782
- for (const block of content) {
783
- if (!block || typeof block !== "object") continue;
784
- const b = block as Record<string, unknown>;
785
- if (b["type"] === "text" && typeof b["text"] === "string") {
786
- const txt = (b["text"] as string).trim();
787
- if (txt) parts.push(txt);
788
- } else if (b["type"] === "tool_use") {
789
- const name =
790
- typeof b["name"] === "string" ? (b["name"] as string) : "tool";
791
- const input = renderToolInput(b["input"]);
792
- parts.push(`**→ \`${name}\`**\n\n\`\`\`json\n${input}\n\`\`\``);
793
- }
794
- // Skip "thinking" blocks.
795
- }
796
- if (parts.length > 0) {
797
- sections.push(`### Assistant\n\n${parts.join("\n\n")}`);
798
- }
799
- continue;
800
- }
801
-
802
- // msg.type === "user" with array content — usually a batch of tool_results
803
- // responding to the previous assistant turn's tool_use blocks. We skip
804
- // the tool_result payloads entirely (see function docstring for why) and
805
- // only surface any inline `text` blocks, which is where a real follow-up
806
- // user turn would land.
807
- for (const block of content) {
808
- if (!block || typeof block !== "object") continue;
809
- const b = block as Record<string, unknown>;
810
- if (b["type"] === "text" && typeof b["text"] === "string") {
811
- const txt = (b["text"] as string).trim();
812
- if (txt) sections.push(`### User\n\n${txt}`);
813
- }
814
- }
815
- }
816
-
817
- return sections.join("\n\n");
818
- }
819
-
820
- /**
821
- * Render a Copilot transcript as readable Markdown.
822
- *
823
- * Preserves the existing `assistant.message → content` extraction and adds
824
- * `user.message` rendering plus any `toolCalls` attached to an assistant
825
- * message. All other event types (`session.start`, `session.idle`, plain
826
- * telemetry, etc.) are skipped.
827
- */
828
- function renderCopilotTranscript(
829
- events: ReadonlyArray<{ type?: unknown; data?: unknown }>,
830
- ): string {
831
- const sections: string[] = [];
832
-
833
- for (const evt of events) {
834
- if (evt.type === "assistant.message") {
835
- const data = evt.data;
836
- if (!hasContent(data)) continue;
837
- const parts: string[] = [];
838
- const text = data.content.trim();
839
- if (text) parts.push(text);
840
-
841
- // toolCalls is an array on `assistant.message` data when present.
842
- const toolCalls = (data as Record<string, unknown>)["toolCalls"];
843
- if (Array.isArray(toolCalls)) {
844
- for (const call of toolCalls) {
845
- if (!call || typeof call !== "object") continue;
846
- const c = call as Record<string, unknown>;
847
- const name =
848
- typeof c["name"] === "string"
849
- ? (c["name"] as string)
850
- : typeof c["toolName"] === "string"
851
- ? (c["toolName"] as string)
852
- : "tool";
853
- const args = c["arguments"] ?? c["input"] ?? c["parameters"];
854
- parts.push(
855
- `**→ \`${name}\`**\n\n\`\`\`json\n${renderToolInput(args)}\n\`\`\``,
856
- );
857
- }
858
- }
859
-
860
- if (parts.length > 0) {
861
- sections.push(`### Assistant\n\n${parts.join("\n\n")}`);
862
- }
863
- continue;
864
- }
865
-
866
- if (evt.type === "user.message") {
867
- const data = evt.data;
868
- if (hasContent(data)) {
869
- const text = data.content.trim();
870
- if (text) sections.push(`### User\n\n${text}`);
871
- }
872
- }
873
- // All other event types are intentionally skipped.
874
- }
875
-
876
- return sections.join("\n\n");
877
- }
878
-
879
- /**
880
- * Render an OpenCode prompt response as readable Markdown.
881
- *
882
- * OpenCode hands us `{ info, parts }`; `parts` is a discriminated union where
883
- * `text` parts carry the assistant reply and `tool` parts carry tool
884
- * invocations. `reasoning` and `subtask` parts are internal and omitted.
885
- */
886
- function renderOpencodeTranscript(response: {
887
- parts?: ReadonlyArray<
888
- { type?: unknown; text?: unknown } & Record<string, unknown>
889
- >;
890
- }): string {
891
- if (!response.parts) return "";
892
- const parts: string[] = [];
893
- for (const part of response.parts) {
894
- if (!part || typeof part !== "object") continue;
895
- if (part.type === "text" && typeof part.text === "string") {
896
- const txt = part.text.trim();
897
- if (txt) parts.push(txt);
898
- } else if (part.type === "tool") {
899
- const name =
900
- typeof part["tool"] === "string"
901
- ? (part["tool"] as string)
902
- : typeof part["name"] === "string"
903
- ? (part["name"] as string)
904
- : "tool";
905
- const state = part["state"];
906
- const args =
907
- state && typeof state === "object"
908
- ? ((state as Record<string, unknown>)["input"] ??
909
- (state as Record<string, unknown>)["args"])
910
- : undefined;
911
- parts.push(
912
- `**→ \`${name}\`**\n\n\`\`\`json\n${renderToolInput(args)}\n\`\`\``,
913
- );
914
- // Tool outputs are intentionally omitted — see the comment on
915
- // `TRANSCRIPT_TOOL_INPUT_BUDGET` for the context-rot rationale.
916
- }
917
- }
918
- if (parts.length === 0) return "";
919
- return `### Assistant\n\n${parts.join("\n\n")}`;
920
- }
921
-
922
- export function renderMessagesToText(messages: SavedMessage[]): string {
923
- // Claude messages already come in as a flat chronological list — render
924
- // the whole slice at once so the helper can cross-reference tool_use_ids
925
- // against tool_result blocks. Copilot and OpenCode keep their existing
926
- // per-message rendering.
927
- const sections: string[] = [];
928
- const claudeBatch: Array<{ type: string; message: unknown }> = [];
929
-
930
- const flushClaude = (): void => {
931
- if (claudeBatch.length === 0) return;
932
- const rendered = renderClaudeTranscript(claudeBatch);
933
- if (rendered) sections.push(rendered);
934
- claudeBatch.length = 0;
935
- };
936
-
937
- for (const m of messages) {
938
- if (m.provider === "claude") {
939
- claudeBatch.push(m.data as unknown as { type: string; message: unknown });
940
- continue;
941
- }
942
- flushClaude();
943
- if (m.provider === "copilot") {
944
- const rendered = renderCopilotTranscript([
945
- m.data as unknown as { type?: unknown; data?: unknown },
946
- ]);
947
- if (rendered) sections.push(rendered);
948
- } else if (m.provider === "opencode") {
949
- const rendered = renderOpencodeTranscript(
950
- m.data as unknown as {
951
- parts?: ReadonlyArray<
952
- { type?: unknown; text?: unknown } & Record<string, unknown>
953
- >;
954
- },
955
- );
956
- if (rendered) sections.push(rendered);
957
- }
958
- }
959
- flushClaude();
960
-
961
- return sections.join("\n\n");
962
- }
963
-
964
- /** Resolve a SessionRef (string or SessionHandle) to the session name. */
965
- function resolveRef(ref: SessionRef): string {
966
- return typeof ref === "string" ? ref : ref.name;
967
- }
968
-
969
- /**
970
- * Minimal Copilot session surface required by `wrapCopilotSend()`.
971
- * Uses a generic `on` signature to remain compatible with both the real
972
- * CopilotSession and lightweight test mocks.
973
- */
974
- export interface CopilotSendSessionSurface {
975
- on(
976
- eventType: string,
977
- handler: (event: { data?: unknown }) => void,
978
- ): () => void;
979
- }
980
-
981
- /**
982
- * Wraps a Copilot session's `send()` to block until `session.idle` fires.
983
- *
984
- * Copilot's `send()` is fire-and-forget — it returns immediately after
985
- * queuing the message. This wrapper blocks the returned promise until the
986
- * session emits `session.idle` (turn complete) or `session.error`.
987
- *
988
- * HIL detection for Copilot is handled separately by
989
- * `watchCopilotSessionForHIL()`, which subscribes to the session's
990
- * `tool.execution_start` / `tool.execution_complete` events for the
991
- * `ask_user` built-in tool. Those events fire regardless of whether
992
- * an `onUserInputRequest` handler is registered, so we can detect HIL
993
- * via native SDK events while the CLI continues to handle user input
994
- * locally in the tmux pane.
995
- *
996
- * Exported for unit testing.
997
- */
998
- export function wrapCopilotSend<O, R>(
999
- session: CopilotSendSessionSurface,
1000
- nativeSend: (options: O) => Promise<R>,
1001
- ): (options: O) => Promise<R> {
1002
- return async (options: O): Promise<R> => {
1003
- const idle = new Promise<void>((resolve, reject) => {
1004
- let unsubIdle: (() => void) | undefined;
1005
- let unsubError: (() => void) | undefined;
1006
- const cleanup = () => {
1007
- unsubIdle?.();
1008
- unsubError?.();
1009
- };
1010
- unsubIdle = session.on("session.idle", () => {
1011
- cleanup();
1012
- resolve();
1013
- });
1014
- unsubError = session.on("session.error", (event) => {
1015
- cleanup();
1016
- const data = event.data as { message?: string } | undefined;
1017
- reject(new Error(data?.message ?? "Copilot session error"));
1018
- });
1019
- });
1020
- const result = await nativeSend(options);
1021
- await idle;
1022
- return result;
1023
- };
1024
- }
1025
-
1026
- /**
1027
- * Minimal shape of an event as produced by the OpenCode v2 SDK event stream.
1028
- * Using a structural interface rather than the SDK's generated union type keeps
1029
- * this helper independently unit-testable with plain objects.
1030
- *
1031
- * `sessionID` is optional because many OpenCode event types (e.g.
1032
- * `file.edited`, `session.compacted`) carry properties without that field.
1033
- * The `watchOpencodeStreamForHIL` implementation guards with a runtime check.
1034
- */
1035
- export interface OpenCodeHILEvent {
1036
- type: string;
1037
- properties: { sessionID?: string; [key: string]: unknown };
1038
- }
1039
-
1040
- /**
1041
- * Consume an OpenCode SSE event stream and call `onHIL` whenever the session
1042
- * with `sessionId` enters or exits a human-in-the-loop (HIL) state:
1043
- *
1044
- * - `question.asked` → `onHIL(true)` (agent awaiting user input)
1045
- * - `question.replied` → `onHIL(false)` (user answered, agent resumes)
1046
- * - `question.rejected` → `onHIL(false)` (user dismissed, agent resumes)
1047
- *
1048
- * Events for other sessions are silently ignored. The function returns when
1049
- * the stream is exhausted (i.e. the server closes the connection).
1050
- *
1051
- * NOTE: OpenCode does not emit any bus event for MCP-server-initiated
1052
- * elicitation requests — its MCP client never registers an
1053
- * `ElicitRequestSchema` handler, so such requests are auto-rejected by the
1054
- * MCP SDK at the protocol layer before reaching any OpenCode-level code.
1055
- * As a result, the workflow UI **cannot** mark an OpenCode session as
1056
- * "awaiting input" for MCP elicitation; this is an upstream limitation that
1057
- * Atomic cannot work around. If a future OpenCode release surfaces MCP
1058
- * elicitation as a bus event, extend the switch below (or add a sibling
1059
- * watcher) to map it onto `onHIL`.
1060
- *
1061
- * Exported for unit testing.
1062
- */
1063
- export async function watchOpencodeStreamForHIL(
1064
- stream: AsyncIterable<OpenCodeHILEvent>,
1065
- sessionId: string,
1066
- onHIL: (waiting: boolean) => void,
1067
- ): Promise<void> {
1068
- for await (const event of stream) {
1069
- if (
1070
- event.type === "question.asked" &&
1071
- event.properties.sessionID === sessionId
1072
- ) {
1073
- onHIL(true);
1074
- } else if (
1075
- (event.type === "question.replied" ||
1076
- event.type === "question.rejected") &&
1077
- event.properties.sessionID === sessionId
1078
- ) {
1079
- onHIL(false);
1080
- }
1081
- }
1082
- }
1083
-
1084
- /**
1085
- * Minimal Copilot session surface required by `watchCopilotSessionForHIL()`.
1086
- * A structural `on()` signature keeps this helper independently unit-testable
1087
- * with plain objects and compatible with both the real CopilotSession and
1088
- * test mocks.
1089
- */
1090
- export interface CopilotHILSessionSurface {
1091
- on(
1092
- eventType: string,
1093
- handler: (event: { data?: unknown }) => void,
1094
- ): () => void;
1095
- }
1096
-
1097
- /**
1098
- * Subscribe to a Copilot session's tool-execution events to track HIL state
1099
- * for the `ask_user` built-in tool:
1100
- *
1101
- * - `tool.execution_start` with `toolName === "ask_user"` → `onHIL(true)`
1102
- * - `tool.execution_complete` with matching `toolCallId` → `onHIL(false)`
1103
- *
1104
- * These events fire regardless of whether an `onUserInputRequest` handler is
1105
- * registered, so we can detect HIL without providing one — letting the CLI
1106
- * keep its native tmux-pane dialog.
1107
- *
1108
- * Overlapping `ask_user` invocations are tracked by `toolCallId` so
1109
- * `onHIL(false)` only fires after the last active request resolves.
1110
- *
1111
- * Returns an unsubscribe function that removes both listeners.
1112
- *
1113
- * Exported for unit testing.
1114
- */
1115
- export function watchCopilotSessionForHIL(
1116
- session: CopilotHILSessionSurface,
1117
- onHIL: (waiting: boolean) => void,
1118
- ): () => void {
1119
- const active = new Set<string>();
1120
- const unsubStart = session.on("tool.execution_start", (event) => {
1121
- const data = event.data as
1122
- | { toolName?: string; toolCallId?: string }
1123
- | undefined;
1124
- if (data?.toolName === "ask_user" && data.toolCallId) {
1125
- const wasEmpty = active.size === 0;
1126
- active.add(data.toolCallId);
1127
- if (wasEmpty) onHIL(true);
1128
- }
1129
- });
1130
- const unsubComplete = session.on("tool.execution_complete", (event) => {
1131
- const data = event.data as { toolCallId?: string } | undefined;
1132
- if (
1133
- data?.toolCallId &&
1134
- active.delete(data.toolCallId) &&
1135
- active.size === 0
1136
- ) {
1137
- onHIL(false);
1138
- }
1139
- });
1140
- return () => {
1141
- unsubStart();
1142
- unsubComplete();
1143
- };
1144
- }
1145
-
1146
- /**
1147
- * Subscribe to a Copilot session's elicitation events to track HIL state for
1148
- * `session.ui.elicitation()`, `session.ui.select()`, `session.ui.input()`, and
1149
- * MCP-server-initiated elicitation requests:
1150
- *
1151
- * - `elicitation.requested` → `onHIL(true)` (set transitions empty→non-empty)
1152
- * - `elicitation.completed` → `onHIL(false)` (set transitions non-empty→empty)
1153
- *
1154
- * Overlapping elicitation requests are tracked by `requestId` so
1155
- * `onHIL(false)` only fires after the last in-flight request completes.
1156
- *
1157
- * Returns an unsubscribe function that removes both listeners.
1158
- *
1159
- * Exported for unit testing.
1160
- */
1161
- export function watchCopilotSessionForElicitation(
1162
- session: CopilotHILSessionSurface,
1163
- onHIL: (waiting: boolean) => void,
1164
- ): () => void {
1165
- const active = new Set<string>();
1166
- const unsubRequested = session.on("elicitation.requested", (event) => {
1167
- const data = event.data as { requestId?: string } | undefined;
1168
- if (data?.requestId) {
1169
- const wasEmpty = active.size === 0;
1170
- active.add(data.requestId);
1171
- if (wasEmpty) onHIL(true);
1172
- }
1173
- });
1174
- const unsubCompleted = session.on("elicitation.completed", (event) => {
1175
- const data = event.data as { requestId?: string } | undefined;
1176
- if (data?.requestId && active.delete(data.requestId) && active.size === 0) {
1177
- onHIL(false);
1178
- }
1179
- });
1180
- return () => {
1181
- unsubRequested();
1182
- unsubCompleted();
1183
- };
1184
- }
1185
-
1186
- // ============================================================================
1187
- // Shared transcript / message readers
1188
- // ============================================================================
1189
-
1190
- /**
1191
- * Create a `transcript(ref)` function bound to a completed-session registry.
1192
- * Used by both the top-level WorkflowContext and per-session SessionContext
1193
- * so the implementation is defined once.
1194
- */
1195
- function createTranscriptReader(
1196
- completedRegistry: Map<string, SessionResult>,
1197
- ): (ref: SessionRef) => Promise<Transcript> {
1198
- return async (ref) => {
1199
- const refName = resolveRef(ref);
1200
- const prev = completedRegistry.get(refName);
1201
- if (!prev) {
1202
- const available = [...completedRegistry.keys()].join(", ") || "(none)";
1203
- throw new Error(
1204
- `No transcript for "${refName}". Available: ${available}`,
1205
- );
1206
- }
1207
- const filePath = join(prev.sessionDir, "inbox.md");
1208
- const content = await Bun.file(filePath).text();
1209
- return { path: filePath, content };
1210
- };
1211
- }
1212
-
1213
- /**
1214
- * Create a `getMessages(ref)` function bound to a completed-session registry.
1215
- * Used by both the top-level WorkflowContext and per-session SessionContext.
1216
- */
1217
- function createMessagesReader(
1218
- completedRegistry: Map<string, SessionResult>,
1219
- ): (ref: SessionRef) => Promise<SavedMessage[]> {
1220
- return async (ref) => {
1221
- const refName = resolveRef(ref);
1222
- const prev = completedRegistry.get(refName);
1223
- if (!prev) {
1224
- const available = [...completedRegistry.keys()].join(", ") || "(none)";
1225
- throw new Error(`No messages for "${refName}". Available: ${available}`);
1226
- }
1227
- const filePath = join(prev.sessionDir, "messages.json");
1228
- const raw = await Bun.file(filePath).text();
1229
- const parsed: unknown = JSON.parse(raw);
1230
- if (!Array.isArray(parsed)) {
1231
- throw new Error(`Invalid messages file for "${refName}": expected array`);
1232
- }
1233
- return parsed.filter(isValidSavedMessage);
1234
- };
1235
- }
1236
-
1237
- // ============================================================================
1238
- // Session runner — implements ctx.stage() lifecycle
1239
- // ============================================================================
1240
-
1241
- /** Shared state passed to session runners by the orchestrator. */
1242
- interface SharedRunnerState {
1243
- tmuxSessionName: string;
1244
- sessionsBaseDir: string;
1245
- /**
1246
- * The project root the workflow is operating against. Threaded through to
1247
- * provider initialization so headless paths resolve project-scoped config
1248
- * (e.g. `additional-instructions`) from the workflow's actual root rather
1249
- * than `process.cwd()`, which can drift when workflows are invoked
1250
- * programmatically or from a subdirectory.
1251
- */
1252
- projectRoot: string;
1253
- agent: AgentType;
1254
- /**
1255
- * Structured inputs for this workflow run. Free-form workflows use
1256
- * `{ prompt: "..." }`; structured workflows use their declared field
1257
- * names. Workflow authors read both shapes via `ctx.inputs` — integer
1258
- * inputs are parsed to `number`, everything else stays a `string`.
1259
- */
1260
- inputs: Record<string, string | number>;
1261
- /** User-configured provider overrides (global + local merged). */
1262
- providerOverrides: ProviderOverrides;
1263
- /**
1264
- * Extra CLI flags appended to the agent's chat flags, derived from
1265
- * the project's scm selection. Currently only populated for Copilot
1266
- * (which has no on-disk MCP toggle — see `getCopilotScmDisableFlags`).
1267
- */
1268
- extraChatFlags: string[];
1269
- panel: OrchestratorPanel;
1270
- /** Sessions that have been spawned (for name uniqueness + cleanup). */
1271
- activeRegistry: Map<string, ActiveSession>;
1272
- /** Sessions that completed successfully (for transcript reads). */
1273
- completedRegistry: Map<string, SessionResult>;
1274
- /** Sessions that already failed before completing successfully. */
1275
- failedRegistry: Set<string>;
1276
- }
1277
-
1278
- /**
1279
- * Append tool names to a Copilot `excludedTools` list without duplicating
1280
- * entries the caller already supplied. Exported for unit testing.
1281
- */
1282
- export function mergeExcludedTools(
1283
- existing: string[] | undefined,
1284
- extras: string[],
1285
- ): string[] {
1286
- const merged = [...(existing ?? [])];
1287
- for (const tool of extras) {
1288
- if (!merged.includes(tool)) merged.push(tool);
1289
- }
1290
- return merged;
1291
- }
1292
-
1293
- type ExternalCopilotClientOptions = Omit<
1294
- StageClientOptions<"copilot">,
1295
- "gitHubToken" | "useLoggedInUser"
1296
- >;
1297
-
1298
- interface ExternalCopilotOptions {
1299
- clientOptions: ExternalCopilotClientOptions;
1300
- sessionGitHubToken?: string;
1301
- }
1302
-
1303
- /**
1304
- * Copilot SDK 0.3.0 rejects client-level auth options when connecting to an
1305
- * existing `cliUrl`. Visible stages use an already-running TUI server, so move
1306
- * token auth to the session-level option that 0.3.0 introduced for this case.
1307
- */
1308
- export function normalizeExternalCopilotOptions(
1309
- clientOptions: StageClientOptions<"copilot">,
1310
- sessionGitHubToken?: string,
1311
- ): ExternalCopilotOptions {
1312
- const {
1313
- gitHubToken: clientGitHubToken,
1314
- useLoggedInUser,
1315
- ...externalClientOptions
1316
- } = clientOptions;
1317
-
1318
- if (useLoggedInUser !== undefined) {
1319
- throw new Error(
1320
- "Copilot client option `useLoggedInUser` cannot be used for visible stages because they connect to an existing Copilot CLI server. Configure authentication on the server process instead.",
1321
- );
1322
- }
1323
-
1324
- const normalized: ExternalCopilotOptions = {
1325
- clientOptions: externalClientOptions,
1326
- };
1327
- if (sessionGitHubToken !== undefined) {
1328
- normalized.sessionGitHubToken = sessionGitHubToken;
1329
- } else if (clientGitHubToken !== undefined) {
1330
- normalized.sessionGitHubToken = clientGitHubToken;
1331
- }
1332
- return normalized;
1333
- }
1334
-
1335
- /**
1336
- * Create the provider-specific client and session for a stage.
1337
- * Called by the session runner after server readiness is confirmed.
1338
- *
1339
- * Generic over `A` so callers receive typed `ProviderClient<A>` /
1340
- * `ProviderSession<A>` without unsafe casts. The internal `switch`
1341
- * branches know the concrete types being constructed, so the `as`
1342
- * assertions here are producer-side (correct by construction) rather
1343
- * than consumer-side (trusting the caller to guess right).
1344
- */
1345
- async function initProviderClientAndSession<A extends AgentType>(
1346
- agent: A,
1347
- serverUrl: string,
1348
- paneId: string,
1349
- projectRoot: string,
1350
- clientOpts: StageClientOptions<A>,
1351
- sessionOpts: StageSessionOptions<A>,
1352
- headless = false,
1353
- onHIL?: (waiting: boolean) => void,
1354
- ): Promise<{
1355
- client: ProviderClient<A>;
1356
- session: ProviderSession<A>;
1357
- /** Optional cleanup for SDK-managed resources (e.g. headless OpenCode server). */
1358
- cleanup?: () => void;
1359
- }> {
1360
- type Result = {
1361
- client: ProviderClient<A>;
1362
- session: ProviderSession<A>;
1363
- cleanup?: () => void;
1364
- };
1365
-
1366
- switch (agent) {
1367
- case "copilot": {
1368
- const { CopilotClient, approveAll } = await import("@github/copilot-sdk");
1369
- const { copilotSdkLaunchOptions, mergeCopilotSystemMessage } =
1370
- await import("../providers/copilot.ts");
1371
- const { resolveAdditionalInstructionsContent } =
1372
- await import("../../services/config/additional-instructions.ts");
1373
- const copilotClientOpts = clientOpts as StageClientOptions<"copilot">;
1374
- const copilotSessionOpts = sessionOpts as StageSessionOptions<"copilot">;
1375
- // Headless: let the SDK spawn its own CLI process (no cliUrl).
1376
- // Non-headless: connect to the CLI server running in a tmux pane.
1377
- // `env` is only meaningful in the headless path — the SDK ignores
1378
- // it when `cliUrl` is set — but layering in `copilotSdkLaunchOptions`
1379
- // when the caller didn't supply their own env keeps the
1380
- // SQLite `ExperimentalWarning` from leaking through the SDK's
1381
- // `[CLI subprocess]` stderr forwarder.
1382
- let externalCopilotOptions: ExternalCopilotOptions | undefined;
1383
- let client: InstanceType<typeof CopilotClient>;
1384
- if (headless) {
1385
- client = new CopilotClient({
1386
- ...copilotSdkLaunchOptions(),
1387
- ...copilotClientOpts,
1388
- });
1389
- } else {
1390
- externalCopilotOptions = normalizeExternalCopilotOptions(
1391
- copilotClientOpts,
1392
- copilotSessionOpts.gitHubToken,
1393
- );
1394
- client = new CopilotClient({
1395
- ...externalCopilotOptions.clientOptions,
1396
- cliUrl: serverUrl,
1397
- });
1398
- }
1399
- await client.start();
1400
- // In headless stages, add `ask_user` to the session's excludedTools so
1401
- // the agent cannot call the interactive question tool — there is no
1402
- // human attached to answer and the SDK would otherwise sit blocked.
1403
- const additionalInstructions =
1404
- await resolveAdditionalInstructionsContent(projectRoot);
1405
- const sessionConfig = {
1406
- onPermissionRequest: approveAll,
1407
- ...copilotSessionOpts,
1408
- ...(externalCopilotOptions?.sessionGitHubToken !== undefined
1409
- ? { gitHubToken: externalCopilotOptions.sessionGitHubToken }
1410
- : {}),
1411
- ...(headless
1412
- ? {
1413
- excludedTools: mergeExcludedTools(
1414
- copilotSessionOpts.excludedTools,
1415
- ["ask_user"],
1416
- ),
1417
- }
1418
- : {}),
1419
- ...(additionalInstructions
1420
- ? {
1421
- systemMessage: mergeCopilotSystemMessage(
1422
- copilotSessionOpts.systemMessage,
1423
- additionalInstructions,
1424
- ),
1425
- }
1426
- : {}),
1427
- };
1428
- const session = await client.createSession(sessionConfig);
1429
- if (!headless) {
1430
- await client.setForegroundSessionId(session.sessionId);
1431
- }
1432
- return { client, session } as Result;
1433
- }
1434
- case "opencode": {
1435
- const ocSessionOpts = sessionOpts as StageSessionOptions<"opencode">;
1436
- if (headless) {
1437
- const { createOpencode } = await import("@opencode-ai/sdk/v2");
1438
- // Scope OPENCODE_CLIENT=sdk around the SDK spawn so the subprocess
1439
- // inherits it at fork time. OpenCode only registers its interactive
1440
- // `question` tool when OPENCODE_CLIENT is "app"/"cli"/"desktop", so
1441
- // identifying as "sdk" keeps the tool out of the registry entirely
1442
- // — otherwise an unattended stage can hang forever on question.asked
1443
- // (the tool's execute calls Question.ask directly and never consults
1444
- // the session permission ruleset).
1445
- return await withHeadlessOpencodeEnv(async () => {
1446
- const oc = await createOpencode({ port: 0 });
1447
- const sessionResult = await oc.client.session.create({
1448
- permission: [{ permission: "*", pattern: "*", action: "allow" }],
1449
- ...ocSessionOpts,
1450
- });
1451
- return {
1452
- client: oc.client,
1453
- session: sessionResult.data!,
1454
- cleanup: () => oc.server.close(),
1455
- } as Result;
1456
- });
1457
- }
1458
- const { createOpencodeClient } = await import("@opencode-ai/sdk/v2");
1459
- const ocClientOpts = clientOpts as StageClientOptions<"opencode">;
1460
- const client = createOpencodeClient({
1461
- ...ocClientOpts,
1462
- baseUrl: serverUrl,
1463
- });
1464
- const sessionResult = await client.session.create(ocSessionOpts);
1465
- await client.tui.selectSession({ sessionID: sessionResult.data!.id });
1466
- return { client, session: sessionResult.data! } as Result;
1467
- }
1468
- case "claude": {
1469
- if (headless) {
1470
- // Headless Claude stages use the Agent SDK directly — no tmux pane.
1471
- // Each query gets its own SDK-assigned session_id; the wrapper
1472
- // tracks the latest one and exposes it as `sessionId`.
1473
- const client = new HeadlessClaudeClientWrapper();
1474
- await client.start();
1475
- const session = new HeadlessClaudeSessionWrapper(projectRoot);
1476
- // Cast through `unknown` — `HeadlessClaudeClientWrapper` intentionally
1477
- // omits the interactive-only fields (`paneId`, `sessionDir`, etc.)
1478
- // that `ClaudeClientWrapper` has; both satisfy the same runtime
1479
- // contract used by workflow code.
1480
- return { client, session } as unknown as Result;
1481
- }
1482
- const claudeClientOpts = clientOpts as StageClientOptions<"claude">;
1483
- const client = new ClaudeClientWrapper(paneId, claudeClientOpts);
1484
- // `start()` now returns the Claude session UUID, which we pass through
1485
- // to the session wrapper so `s.sessionId` is the Claude UUID (not the
1486
- // atomic short ID). This fixes the parallel-workflow bug where save
1487
- // used to look up "the newest Claude session globally" and could
1488
- // attribute one branch's transcript to another.
1489
- const claudeSessionId = await client.start();
1490
- const session = new ClaudeSessionWrapper(paneId, claudeSessionId, onHIL);
1491
- return { client, session } as Result;
1492
- }
1493
- default:
1494
- return assertNever(agent);
1495
- }
1496
- }
1497
-
1498
- /**
1499
- * Clean up provider-specific resources after a stage callback completes.
1500
- * Errors are silently caught — cleanup must not mask callback errors.
1501
- *
1502
- * The `switch (agent)` already narrows the type, so we call
1503
- * disconnect/stop directly without redundant `instanceof` checks or
1504
- * dynamic imports.
1505
- */
1506
- async function cleanupProvider<A extends AgentType>(
1507
- agent: A,
1508
- providerClient: ProviderClient<A>,
1509
- providerSession: ProviderSession<A>,
1510
- paneId: string,
1511
- ): Promise<void> {
1512
- switch (agent) {
1513
- case "copilot": {
1514
- const session = providerSession as ProviderSession<"copilot">;
1515
- const client = providerClient as ProviderClient<"copilot">;
1516
- try {
1517
- await session.disconnect();
1518
- } catch (e) {
1519
- console.warn(
1520
- `[cleanup] copilot session disconnect failed: ${errorMessage(e)}`,
1521
- );
1522
- }
1523
- try {
1524
- await client.stop();
1525
- } catch (e) {
1526
- console.warn(
1527
- `[cleanup] copilot client stop failed: ${errorMessage(e)}`,
1528
- );
1529
- }
1530
- break;
1531
- }
1532
- case "opencode":
1533
- // Stateless HTTP client — no cleanup needed
1534
- break;
1535
- case "claude":
1536
- // Headless Claude stages have no tmux pane to clear.
1537
- if (!paneId.startsWith("headless-")) {
1538
- try {
1539
- await clearClaudeSession(paneId);
1540
- } catch (e) {
1541
- console.warn(
1542
- `[cleanup] claude session clear failed: ${errorMessage(e)}`,
1543
- );
1544
- }
1545
- }
1546
- break;
1547
- default:
1548
- assertNever(agent);
1549
- }
1550
- }
1551
-
1552
- /**
1553
- * Create a `ctx.stage()` function bound to a parent name for graph edges.
1554
- *
1555
- * Graph topology is auto-inferred from JavaScript's execution order:
1556
- * - **Sequential** (`await`): the completed stage is in the frontier when the
1557
- * next stage spawns → parent-child edge.
1558
- * - **Parallel** (`Promise.all`): both calls fire in the same synchronous
1559
- * frame → frontier is empty for the second call → sibling edges.
1560
- * - **Fan-in**: after `Promise.all` resolves, all parallel stages are in the
1561
- * frontier → the next stage depends on all of them.
1562
- *
1563
- * The returned function manages the full session lifecycle:
1564
- * spawn → init client/session → run callback → flush saves → cleanup → complete/error.
1565
- */
1566
- function createSessionRunner(
1567
- shared: SharedRunnerState,
1568
- parentName: string,
1569
- ): <T = void>(
1570
- options: SessionRunOptions,
1571
- clientOpts: StageClientOptions<AgentType>,
1572
- sessionOpts: StageSessionOptions<AgentType>,
1573
- run: (ctx: SessionContext) => Promise<T>,
1574
- ) => Promise<SessionHandle<T>> {
1575
- const graphTracker = new GraphFrontierTracker(parentName);
1576
-
1577
- return async <T = void>(
1578
- options: SessionRunOptions,
1579
- clientOpts: StageClientOptions<AgentType>,
1580
- sessionOpts: StageSessionOptions<AgentType>,
1581
- run: (ctx: SessionContext) => Promise<T>,
1582
- ): Promise<SessionHandle<T>> => {
1583
- const { name } = options;
1584
-
1585
- // ── 1. Validate name uniqueness (synchronous, before any await) ──
1586
- if (!name || name.trim() === "") {
1587
- throw new Error("Session name is required.");
1588
- }
1589
- if (
1590
- shared.activeRegistry.has(name) ||
1591
- shared.completedRegistry.has(name) ||
1592
- shared.failedRegistry.has(name)
1593
- ) {
1594
- throw new Error(`Duplicate session name: "${name}"`);
1595
- }
1596
-
1597
- const isHeadless = options.headless === true;
1598
-
1599
- // ── 2. Auto-infer graph parents from frontier (synchronous) ──
1600
- // Headless stages are invisible in the graph — they must not consume or
1601
- // update the frontier, otherwise the next visible stage gets orphaned
1602
- // parent refs that don't exist in the panel.
1603
- const graphParents = isHeadless ? [] : graphTracker.onSpawn();
1604
-
1605
- // ── 3. Create done promise so dependent sessions can await this one ──
1606
- let resolveDone!: () => void;
1607
- let rejectDone!: (err: unknown) => void;
1608
- const donePromise = new Promise<void>((resolve, reject) => {
1609
- resolveDone = resolve;
1610
- rejectDone = reject;
1611
- });
1612
- // Prevent "unhandled rejection" noise when no dependent awaits us.
1613
- donePromise.catch(() => {});
1614
-
1615
- // ── 4. Register in active registry (synchronous) ──
1616
- // Placeholder paneId — filled in after tmux window creation.
1617
- shared.activeRegistry.set(name, { name, paneId: "", done: donePromise });
1618
-
1619
- const sessionId = generateId();
1620
- let paneId = "";
1621
- let panelSessionAdded = false;
1622
-
1623
- try {
1624
- // ── 6. Build pane command (OS allocates port via --port 0) ──
1625
- const { command: paneCmd, envVars: paneEnvVars } = buildPaneCommand(
1626
- shared.agent,
1627
- shared.providerOverrides,
1628
- shared.extraChatFlags,
1629
- );
1630
-
1631
- // ── 7. Create tmux window or headless execution ──
1632
- let serverUrl: string;
1633
- if (isHeadless) {
1634
- // Headless stages use their SDKs directly — no tmux window.
1635
- // Claude Agent SDK runs in-process; Copilot SDK spawns its own CLI;
1636
- // OpenCode SDK starts both server and client via createOpencode().
1637
- paneId = `headless-${name}-${sessionId}`;
1638
- shared.activeRegistry.set(name, { name, paneId, done: donePromise });
1639
- serverUrl = "";
1640
-
1641
- shared.panel.backgroundTaskStarted();
1642
- panelSessionAdded = true;
1643
- } else {
1644
- // Standard tmux window for visible stages.
1645
- paneId = tmux.createWindow(
1646
- shared.tmuxSessionName,
1647
- name,
1648
- paneCmd,
1649
- undefined,
1650
- paneEnvVars,
1651
- );
1652
- shared.activeRegistry.set(name, { name, paneId, done: donePromise });
1653
-
1654
- spawnAttachedFooter(name, paneId);
1655
-
1656
- serverUrl = await waitForServer(shared.agent, paneId);
1657
-
1658
- shared.panel.addSession(name, graphParents);
1659
- panelSessionAdded = true;
1660
- }
1661
-
1662
- // ── 9. Create session directory ──
1663
- const sessionDirName = `${name}-${sessionId}`;
1664
- const sessionDir = join(shared.sessionsBaseDir, sessionDirName);
1665
- await ensureDir(sessionDir);
1666
-
1667
- const messagesPath = join(sessionDir, "messages.json");
1668
- const inboxPath = join(sessionDir, "inbox.md");
1669
-
1670
- // ── Message wrapping (Claude/Copilot/OpenCode) ──
1671
- async function wrapMessages(
1672
- arg: SessionEvent[] | SessionPromptResponse | string,
1673
- ): Promise<SavedMessage[]> {
1674
- if (typeof arg === "string") {
1675
- // `arg` is the Claude session UUID — either `s.sessionId` from an
1676
- // interactive `ClaudeSessionWrapper` (set at `createClaudeSession`
1677
- // time) or the SDK-emitted `session_id` tracked inside
1678
- // `HeadlessClaudeSessionWrapper.query`. Using it directly removes
1679
- // the "pick the globally newest Claude session" heuristic that
1680
- // misattributed transcripts across parallel branches.
1681
- if (!arg) {
1682
- throw new Error(
1683
- "wrapMessages: empty Claude session id. Call s.save(s.sessionId) " +
1684
- "only after a successful s.session.query() (headless wrappers " +
1685
- "only know their session_id once a query completes).",
1686
- );
1687
- }
1688
- const { getSessionMessages } =
1689
- await import("@anthropic-ai/claude-agent-sdk");
1690
- const msgs: SessionMessage[] = await getSessionMessages(arg, {
1691
- dir: process.cwd(),
1692
- });
1693
- return msgs.map((m) => ({ provider: "claude" as const, data: m }));
1694
- }
1695
-
1696
- if (!Array.isArray(arg) && "info" in arg && "parts" in arg) {
1697
- return [
1698
- {
1699
- provider: "opencode" as const,
1700
- data: arg as SessionPromptResponse,
1701
- },
1702
- ];
1703
- }
1704
-
1705
- if (Array.isArray(arg)) {
1706
- return (arg as SessionEvent[]).map((m) => ({
1707
- provider: "copilot" as const,
1708
- data: m,
1709
- }));
1710
- }
1711
-
1712
- return [];
1713
- }
1714
-
1715
- // ── Save function ──
1716
- const pendingSaves: Promise<void>[] = [];
1717
-
1718
- const save: SaveTranscript = ((
1719
- arg: SessionEvent[] | SessionPromptResponse | string,
1720
- ) => {
1721
- const p = (async () => {
1722
- const wrapped = await wrapMessages(arg);
1723
- await Bun.write(messagesPath, JSON.stringify(wrapped, null, 2));
1724
- const text = renderMessagesToText(wrapped);
1725
- await Bun.write(inboxPath, text);
1726
- })();
1727
- pendingSaves.push(p);
1728
- return p;
1729
- }) as SaveTranscript;
1730
-
1731
- // ── Transcript/messages access (reads only from completedRegistry) ──
1732
- const transcriptFn = createTranscriptReader(shared.completedRegistry);
1733
- const getMessagesFn = createMessagesReader(shared.completedRegistry);
1734
-
1735
- // ── HIL (human-in-the-loop) callback ──
1736
- // Unified callback passed to provider-specific HIL detection so that any
1737
- // provider can signal when the agent is waiting for user input or has
1738
- // resumed processing. Both `name` and `shared.panel` are guaranteed to
1739
- // be in scope here: `name` is validated above and `shared.panel` is
1740
- // always present on the shared runner state.
1741
- const onHIL = (waiting: boolean) => {
1742
- if (waiting) shared.panel.sessionAwaitingInput(name);
1743
- else shared.panel.sessionResumed(name);
1744
- };
1745
-
1746
- // ── 12. Auto-create provider client and session ──
1747
- const {
1748
- client: providerClient,
1749
- session: providerSession,
1750
- cleanup: providerCleanup,
1751
- } = await initProviderClientAndSession(
1752
- shared.agent,
1753
- serverUrl,
1754
- paneId,
1755
- shared.projectRoot,
1756
- clientOpts,
1757
- sessionOpts,
1758
- isHeadless,
1759
- onHIL,
1760
- );
1761
-
1762
- // ── 12a. Copilot: wrap send() to await session.idle ──
1763
- // Copilot's send() is fire-and-forget — it returns immediately after
1764
- // queuing the message. Without this wrapper, stage callbacks complete
1765
- // before the agent finishes processing, causing getMessages() to
1766
- // return incomplete data and the stage to be marked done prematurely.
1767
- // We intercept send() to block until the session emits "session.idle",
1768
- // matching the blocking behavior of Claude's query() and OpenCode's
1769
- // session.prompt().
1770
- //
1771
- // Compatible with sendAndWait(): the SDK's _dispatchEvent broadcasts
1772
- // to all handlers (typed + wildcard), so both this wrapper's listener
1773
- // and sendAndWait's internal wildcard handler observe the same event.
1774
- // Unsubscribe fn for the Copilot HIL event listeners; invoked in the
1775
- // `finally` block so the handlers are removed when the stage ends.
1776
- let hilUnsubscribe: (() => void) | undefined;
1777
- let copilotElicitationUnsubscribe: (() => void) | undefined;
1778
-
1779
- if (shared.agent === "copilot") {
1780
- const copilotSession = providerSession as ProviderSession<"copilot">;
1781
- const nativeSend = copilotSession.send.bind(copilotSession);
1782
- copilotSession.send = wrapCopilotSend(copilotSession, nativeSend);
1783
-
1784
- // Copilot HIL detection via native SDK events.
1785
- //
1786
- // `tool.execution_start` / `tool.execution_complete` fire for the
1787
- // `ask_user` built-in tool regardless of whether `onUserInputRequest`
1788
- // is registered, so we can detect HIL via the SDK's event stream and
1789
- // still let the CLI render its native tmux-pane dialog.
1790
- hilUnsubscribe = watchCopilotSessionForHIL(copilotSession, onHIL);
1791
-
1792
- // Copilot elicitation HIL detection via native SDK events.
1793
- //
1794
- // `elicitation.requested` / `elicitation.completed` fire when the
1795
- // agent calls `session.ui.elicitation()`, `session.ui.select()`,
1796
- // `session.ui.input()`, or an MCP server issues an elicitation
1797
- // request. These events are distinct from the `ask_user` tool and
1798
- // require a separate watcher so the UI can show the "waiting for
1799
- // response" indicator in all HIL scenarios.
1800
- copilotElicitationUnsubscribe = watchCopilotSessionForElicitation(
1801
- copilotSession,
1802
- onHIL,
1803
- );
1804
- }
1805
-
1806
- // ── 12b. OpenCode: SSE event stream for HIL detection ──
1807
- //
1808
- // `client.event.subscribe()` yields `question.asked`, `question.replied`,
1809
- // and `question.rejected` events in real time. The subscription is
1810
- // **awaited** before the stage callback runs so the stream is guaranteed
1811
- // to be open when the first prompt fires.
1812
- if (shared.agent === "opencode") {
1813
- const ocClient = providerClient as ProviderClient<"opencode">;
1814
- const ocSession = providerSession as ProviderSession<"opencode">;
1815
- const ocSessionId = ocSession.id;
1816
-
1817
- try {
1818
- const { stream } = await ocClient.event.subscribe();
1819
- watchOpencodeStreamForHIL(stream, ocSessionId, onHIL).catch((err) => {
1820
- console.warn(
1821
- `[opencode] HIL event stream disconnected for session ${ocSessionId}: ${errorMessage(err)}`,
1822
- );
1823
- });
1824
- } catch (err) {
1825
- console.warn(
1826
- `[opencode] HIL event stream failed to subscribe for session ${ocSessionId}: ${errorMessage(err)}`,
1827
- );
1828
- }
1829
- }
1830
-
1831
- // ── 13. Construct SessionContext ──
1832
- // Free-form workflows read their prompt via `s.inputs.prompt`;
1833
- // structured workflows read their declared fields the same way.
1834
- // A single uniform access pattern means workflow code never has
1835
- // to branch on "is this workflow structured or free-form".
1836
- //
1837
- // `s.sessionId` is the provider-specific session identifier — the
1838
- // Claude session UUID, the Copilot session id, or the OpenCode
1839
- // session id. This is what workflows pass to `s.save(s.sessionId)`
1840
- // to disambiguate their own transcript when several sessions run
1841
- // in parallel under the same workflow.
1842
- //
1843
- // Exposed as a getter (not a snapshot) because headless Claude stages
1844
- // don't know their SDK-assigned `session_id` until the first `query()`
1845
- // completes — `HeadlessClaudeSessionWrapper._lastSessionId` starts empty
1846
- // and is populated when the SDK emits a `result` event. A snapshot
1847
- // captured at stage creation would leave `s.sessionId === ""` forever,
1848
- // so `s.save(s.sessionId)` would always throw "empty Claude session id"
1849
- // even though the query completed successfully.
1850
- const ctx: SessionContext = {
1851
- client: providerClient,
1852
- session: providerSession,
1853
- inputs: shared.inputs as SessionContext["inputs"],
1854
- agent: shared.agent,
1855
- sessionDir,
1856
- paneId,
1857
- get sessionId() {
1858
- return resolveProviderSessionId(shared.agent, providerSession);
1859
- },
1860
- save,
1861
- transcript: transcriptFn,
1862
- getMessages: getMessagesFn,
1863
- stage: createSessionRunner(shared, name) as SessionContext["stage"],
1864
- };
1865
-
1866
- // ── Write session metadata ──
1867
- await Bun.write(
1868
- join(sessionDir, "metadata.json"),
1869
- JSON.stringify(
1870
- {
1871
- name,
1872
- description: options.description ?? "",
1873
- agent: shared.agent,
1874
- paneId,
1875
- serverUrl,
1876
- port: serverUrl ? Number(serverUrl.split(":").pop()) : 0,
1877
- startedAt: new Date().toISOString(),
1878
- },
1879
- null,
1880
- 2,
1881
- ),
1882
- );
1883
-
1884
- // ── 14. Run user callback ──
1885
- let callbackResult: T;
1886
- try {
1887
- callbackResult = await run(ctx);
1888
- if (pendingSaves.length > 0) await Promise.all(pendingSaves);
1889
- } catch (error) {
1890
- const message = errorMessage(error);
1891
- await Bun.write(join(sessionDir, "error.txt"), message).catch(() => {});
1892
- if (!isHeadless) shared.panel.sessionError(name, message);
1893
- throw error;
1894
- } finally {
1895
- // ── 14a. Stop background HIL watcher (if any) ──
1896
- hilUnsubscribe?.();
1897
- copilotElicitationUnsubscribe?.();
1898
-
1899
- // ── 14b. Auto-cleanup provider resources ──
1900
- await cleanupProvider(
1901
- shared.agent,
1902
- providerClient,
1903
- providerSession,
1904
- paneId,
1905
- );
1906
- if (providerCleanup) {
1907
- try {
1908
- providerCleanup();
1909
- } catch {}
1910
- }
1911
- }
1912
-
1913
- // ── 15. Mark session complete ──
1914
- if (isHeadless) {
1915
- shared.panel.backgroundTaskFinished();
1916
- } else {
1917
- shared.panel.sessionSuccess(name);
1918
- }
1919
- const result: SessionResult = { name, sessionId, sessionDir, paneId };
1920
- shared.completedRegistry.set(name, result);
1921
- shared.activeRegistry.delete(name);
1922
- resolveDone();
1923
-
1924
- // Update frontier so the next stage in this scope chains from us.
1925
- // Headless stages are transparent — they don't touch the frontier.
1926
- if (!isHeadless) graphTracker.onSettle(name);
1927
- return { name, id: sessionId, result: callbackResult! };
1928
- } catch (error) {
1929
- const message = errorMessage(error);
1930
- if (panelSessionAdded) {
1931
- if (isHeadless) {
1932
- shared.panel.backgroundTaskFinished();
1933
- } else {
1934
- shared.panel.sessionError(name, message);
1935
- }
1936
- }
1937
- // Kill the tmux window if one was created (visible stages and headless OpenCode).
1938
- // Headless Claude/Copilot have virtual paneIds ("headless-...") — no window to kill.
1939
- if (paneId && !paneId.startsWith("headless-")) {
1940
- try {
1941
- tmux.killWindow(shared.tmuxSessionName, name);
1942
- } catch {}
1943
- }
1944
- // Ensure the done promise settles and the active entry is cleared.
1945
- shared.activeRegistry.delete(name);
1946
- shared.failedRegistry.add(name);
1947
- rejectDone(error);
1948
- // Update frontier even on failure — if the caller catches and
1949
- // continues, the next stage should still chain from this one.
1950
- // Headless stages are transparent — they don't touch the frontier.
1951
- if (!isHeadless) graphTracker.onSettle(name);
1952
- throw error;
1953
- }
1954
- };
1955
- }
1956
-
1957
- // ============================================================================
1958
- // Orchestrator logic — runs inside a tmux pane
1959
- // ============================================================================
1960
-
1961
- export { validateOrchestratorEnv } from "./executor-env.ts";
1962
- import { validateOrchestratorEnv } from "./executor-env.ts";
1963
-
1964
- /**
1965
- * Run the orchestrator for a compiled workflow definition.
1966
- *
1967
- * Called by the SDK's `orchestrator-entry.ts` after it imports the
1968
- * workflow module by `source` and decodes the inputs payload from argv.
1969
- * The runtime environment (`ATOMIC_WF_ID`, `ATOMIC_WF_TMUX`,
1970
- * `ATOMIC_WF_AGENT`, `ATOMIC_WF_CWD`) is set by the launcher script that
1971
- * `executeWorkflow()` writes — those vars describe *where* this
1972
- * orchestrator is running, not what to do.
1973
- */
1974
- export async function runOrchestrator(
1975
- definition: WorkflowDefinition,
1976
- inputs: Record<string, string> = {},
1977
- ): Promise<void> {
1978
- const { workflowRunId, tmuxSessionName, agent, cwd } =
1979
- validateOrchestratorEnv();
1980
- // A bare prompt string is still useful for the panel header and the
1981
- // session-dir metadata.json — both just want something displayable.
1982
- // Free-form workflows store their single positional prompt under the
1983
- // `prompt` key so workflow authors always read it via
1984
- // `ctx.inputs.prompt`.
1985
- const prompt = inputs.prompt ?? "";
1986
-
1987
- process.chdir(cwd);
1988
-
1989
- const providerOverrides = await getProviderOverrides(agent, cwd);
1990
- const extraChatFlags =
1991
- agent === "copilot" ? await getCopilotScmDisableFlags(cwd) : [];
1992
- const sessionsBaseDir = join(getSessionsBaseDir(), workflowRunId);
1993
- await ensureDir(sessionsBaseDir);
1994
-
1995
- const panel = await OrchestratorPanel.create({
1996
- tmuxSession: tmuxSessionName,
1997
- });
1998
-
1999
- // Mirror panel-store mutations to <sessionDir>/status.json so
2000
- // out-of-process consumers (e.g. `atomic workflow status`) can read
2001
- // the live workflow state without IPC into the orchestrator.
2002
- // Writes are debounced via a "pending" flag so a burst of mutations
2003
- // collapses into a single file write.
2004
- let snapshotPending = false;
2005
- const persistSnapshot = (): void => {
2006
- if (snapshotPending) return;
2007
- snapshotPending = true;
2008
- queueMicrotask(() => {
2009
- snapshotPending = false;
2010
- const snap = panel.getSnapshot();
2011
- void writeSnapshot(
2012
- sessionsBaseDir,
2013
- buildSnapshot({
2014
- workflowRunId,
2015
- tmuxSession: tmuxSessionName,
2016
- ...snap,
2017
- }),
2018
- );
2019
- });
2020
- };
2021
- const unsubscribePanel = panel.subscribe(persistSnapshot);
2022
- // Seed an initial snapshot so the file exists before any session starts.
2023
- persistSnapshot();
2024
-
2025
- // Idempotent shutdown guard
2026
- let shutdownCalled = false;
2027
- const shutdown = (exitCode = 0) => {
2028
- if (shutdownCalled) return;
2029
- shutdownCalled = true;
2030
- unsubscribePanel();
2031
- // Final snapshot reflecting terminal state (completed/error/aborted).
2032
- void writeSnapshot(
2033
- sessionsBaseDir,
2034
- buildSnapshot({
2035
- workflowRunId,
2036
- tmuxSession: tmuxSessionName,
2037
- ...panel.getSnapshot(),
2038
- }),
2039
- );
2040
- panel.destroy();
2041
- try {
2042
- tmux.killSession(tmuxSessionName);
2043
- } catch {}
2044
- process.exitCode = exitCode;
2045
- };
2046
-
2047
- // Wire SIGINT so the terminal is always restored.
2048
- // SIGTERM and other signals are handled by OpenTUI's exitSignals.
2049
- const signalHandler = () => shutdown(1);
2050
- process.on("SIGINT", signalHandler);
2051
-
2052
- // Shared state for all session runners
2053
- const shared: SharedRunnerState = {
2054
- tmuxSessionName,
2055
- sessionsBaseDir,
2056
- projectRoot: cwd,
2057
- agent,
2058
- inputs,
2059
- providerOverrides,
2060
- extraChatFlags,
2061
- panel,
2062
- activeRegistry: new Map(),
2063
- completedRegistry: new Map(),
2064
- failedRegistry: new Set(),
2065
- };
2066
-
2067
- try {
2068
- // Parse integer inputs to numbers so `ctx.inputs.<name>` matches the
2069
- // declared type. Mutate shared.inputs so per-stage SessionContexts see
2070
- // the same shape.
2071
- shared.inputs = coerceInputsBySchema(inputs, definition.inputs);
2072
-
2073
- await Bun.write(
2074
- join(sessionsBaseDir, "metadata.json"),
2075
- JSON.stringify(
2076
- {
2077
- workflowName: definition.name,
2078
- agent,
2079
- prompt,
2080
- projectRoot: cwd,
2081
- startedAt: new Date().toISOString(),
2082
- },
2083
- null,
2084
- 2,
2085
- ),
2086
- );
2087
-
2088
- // Initialize panel with just the orchestrator node (sessions added dynamically)
2089
- panel.showWorkflowInfo(definition.name, agent, [], prompt);
2090
-
2091
- // Build the WorkflowContext — top-level context for the .run() callback
2092
- const sessionRunner = createSessionRunner(shared, "orchestrator");
2093
-
2094
- const workflowCtx: WorkflowContext = {
2095
- inputs: shared.inputs as WorkflowContext["inputs"],
2096
- agent,
2097
- stage: sessionRunner as WorkflowContext["stage"],
2098
- transcript: createTranscriptReader(shared.completedRegistry),
2099
- getMessages: createMessagesReader(shared.completedRegistry),
2100
- };
2101
-
2102
- // Run the workflow, racing against user abort (q / Ctrl+C)
2103
- const abortPromise = panel.waitForAbort().then(() => {
2104
- throw new WorkflowAbortError();
2105
- });
2106
- await Promise.race([definition.run(workflowCtx), abortPromise]);
2107
-
2108
- panel.showCompletion(definition.name, sessionsBaseDir);
2109
- await panel.waitForExit();
2110
- shutdown(0);
2111
- } catch (error) {
2112
- // Kill any active tmux windows that didn't complete.
2113
- // Headless Claude/Copilot have virtual paneIds ("headless-...") — their
2114
- // SDK-managed processes are cleaned up by cleanupProvider().
2115
- for (const [, active] of shared.activeRegistry) {
2116
- try {
2117
- if (active.paneId && !active.paneId.startsWith("headless-")) {
2118
- tmux.killWindow(tmuxSessionName, active.name);
2119
- }
2120
- } catch {}
2121
- }
2122
-
2123
- if (error instanceof WorkflowAbortError) {
2124
- shutdown(0);
2125
- } else {
2126
- const message = errorMessage(error);
2127
- try {
2128
- panel.showFatalError(message);
2129
- await panel.waitForExit();
2130
- } catch {}
2131
- shutdown(1);
2132
- }
2133
- } finally {
2134
- process.off("SIGINT", signalHandler);
2135
- }
2136
- }