create-harness-vibe-coding 0.8.19 → 0.9.3

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 (656) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README-CN.md +4 -2
  3. package/README.md +23 -4
  4. package/package.json +17 -2
  5. package/src/generator.js +19 -6
  6. package/src/index.js +531 -38
  7. package/src/prompts.js +4 -4
  8. package/src/ui/dist/assets/AgentsRoute-BGGs90wH.js +7 -0
  9. package/src/ui/dist/assets/Assistant-Bold-gm-uSS1B.woff2 +0 -0
  10. package/src/ui/dist/assets/Assistant-Medium-DrcxCXg3.woff2 +0 -0
  11. package/src/ui/dist/assets/Assistant-Regular-DVxZuzxb.woff2 +0 -0
  12. package/src/ui/dist/assets/Assistant-SemiBold-SCI4bEL9.woff2 +0 -0
  13. package/src/ui/dist/assets/Combination-DhBLEmof.js +42 -0
  14. package/src/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  15. package/src/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  16. package/src/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  17. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  18. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  19. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  20. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  21. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  22. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  23. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  24. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  25. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  26. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  27. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  28. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  29. package/src/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  30. package/src/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  31. package/src/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  32. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  33. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  34. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  35. package/src/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  36. package/src/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  37. package/src/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  38. package/src/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  39. package/src/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  40. package/src/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  41. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  42. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  43. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  44. package/src/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  45. package/src/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  46. package/src/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  47. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  48. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  49. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  50. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  51. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  52. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  53. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  54. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  55. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  56. package/src/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  57. package/src/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  58. package/src/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  59. package/src/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  60. package/src/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  61. package/src/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  62. package/src/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  63. package/src/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  64. package/src/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  65. package/src/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  66. package/src/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  67. package/src/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  68. package/src/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  69. package/src/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  70. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  71. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  72. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  73. package/src/ui/dist/assets/RolesRoute-DnodCSz0.js +2 -0
  74. package/src/ui/dist/assets/RuntimePicker-CucD8TGn.js +7 -0
  75. package/src/ui/dist/assets/SettingsRoute-DquMWjac.js +2 -0
  76. package/src/ui/dist/assets/TaskList-Bxc0Uktx.js +7 -0
  77. package/src/ui/dist/assets/TerminalDrawer-CDusYltc.js +36 -0
  78. package/src/ui/dist/assets/WorkflowRoute-BN8yHmUs.css +1 -0
  79. package/src/ui/dist/assets/WorkflowRoute-D-0wVDpY.js +334 -0
  80. package/src/ui/dist/assets/abnfDiagram-VRR7QNED-zkRKkaxO.js +2 -0
  81. package/src/ui/dist/assets/apl-B4CMkyY2.js +2 -0
  82. package/src/ui/dist/assets/ar-SA-G6X2FPQ2-BiHaArx5.js +11 -0
  83. package/src/ui/dist/assets/arc-CBnLtOFO.js +2 -0
  84. package/src/ui/dist/assets/architectureDiagram-ZJ3FMSHR-DWXRXsh1.js +37 -0
  85. package/src/ui/dist/assets/asciiarmor-Df11BRmG.js +2 -0
  86. package/src/ui/dist/assets/asn1-EdZsLKOL.js +2 -0
  87. package/src/ui/dist/assets/asterisk-B-8jnY81.js +2 -0
  88. package/src/ui/dist/assets/az-AZ-76LH7QW2-CiT05voO.js +2 -0
  89. package/src/ui/dist/assets/bg-BG-XCXSNQG7-L-UM9tNf.js +6 -0
  90. package/src/ui/dist/assets/blockDiagram-677ZJIJ3-BIyh3Bry.js +133 -0
  91. package/src/ui/dist/assets/bn-BD-2XOGV67Q-NdgbA0bC.js +6 -0
  92. package/src/ui/dist/assets/brainfuck-C4LP7Hcl.js +2 -0
  93. package/src/ui/dist/assets/c4Diagram-LMCZKHZV-CA54vWWM.js +11 -0
  94. package/src/ui/dist/assets/ca-ES-6MX7JW3Y-BKZZsQ5J.js +9 -0
  95. package/src/ui/dist/assets/channel-Da_-zcAu.js +2 -0
  96. package/src/ui/dist/assets/chunk-2Q5K7J3B-DE4cxr1v.js +2 -0
  97. package/src/ui/dist/assets/chunk-32BRIVSS-i7FwbEU9.js +2 -0
  98. package/src/ui/dist/assets/chunk-5VM5RSS4-BhIcOYSu.js +16 -0
  99. package/src/ui/dist/assets/chunk-EX3LRPZG-Ci9zZRIs.js +232 -0
  100. package/src/ui/dist/assets/chunk-JWPE2WC7-BFdkcz-G.js +2 -0
  101. package/src/ui/dist/assets/chunk-MOJQB5TN-Dzmapl_S.js +89 -0
  102. package/src/ui/dist/assets/chunk-RYQCIY6F-RTmp7FOy.js +2 -0
  103. package/src/ui/dist/assets/chunk-V7JOEXUC-DInU3v3D.js +207 -0
  104. package/src/ui/dist/assets/chunk-VR4S4FIN-BHoyGBQO.js +2 -0
  105. package/src/ui/dist/assets/chunk-XXDRQBXY-DfeLZNZx.js +2 -0
  106. package/src/ui/dist/assets/classDiagram-OUVF2IWQ-FKbUS0bu.js +2 -0
  107. package/src/ui/dist/assets/classDiagram-v2-EOCWNBFH-FKbUS0bu.js +2 -0
  108. package/src/ui/dist/assets/clike-B9uivgTg.js +2 -0
  109. package/src/ui/dist/assets/clojure-BMjYHr_A.js +2 -0
  110. package/src/ui/dist/assets/cmake-BQqOBYOt.js +2 -0
  111. package/src/ui/dist/assets/cobol-CWcv1MsR.js +2 -0
  112. package/src/ui/dist/assets/coffeescript-S37ZYGWr.js +2 -0
  113. package/src/ui/dist/assets/commonlisp-DBKNyK5s.js +2 -0
  114. package/src/ui/dist/assets/cose-bilkent-JH36ORCC-_5PUJ615.js +2 -0
  115. package/src/ui/dist/assets/crystal-SjHAIU92.js +2 -0
  116. package/src/ui/dist/assets/cs-CZ-2BRQDIVT-DnkfnhDU.js +12 -0
  117. package/src/ui/dist/assets/css-BnMrqG3P.js +2 -0
  118. package/src/ui/dist/assets/cynefin-VYW2F7L2-CmkcFv-b.js +179 -0
  119. package/src/ui/dist/assets/cynefinDiagram-TSTJHNR4-D72VZn8H.js +63 -0
  120. package/src/ui/dist/assets/cypher-C_CwsFkJ.js +2 -0
  121. package/src/ui/dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
  122. package/src/ui/dist/assets/d-pRatUO7H.js +2 -0
  123. package/src/ui/dist/assets/da-DK-5WZEPLOC-C54tQUgd.js +6 -0
  124. package/src/ui/dist/assets/dagre-VKFMJZFB-0BsAkrmo.js +5 -0
  125. package/src/ui/dist/assets/de-DE-XR44H4JA-DGfZlR10.js +9 -0
  126. package/src/ui/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  127. package/src/ui/dist/assets/diagram-FQU43EPY-C1ZJDOca.js +4 -0
  128. package/src/ui/dist/assets/diagram-G47NLZAW-D3pr3FSP.js +25 -0
  129. package/src/ui/dist/assets/diagram-NH7WQ7WH-Bwry0NV-.js +25 -0
  130. package/src/ui/dist/assets/diagram-OA4YK3LP-L6g09cmV.js +31 -0
  131. package/src/ui/dist/assets/diagram-WEI45ONY-Bh1puYVX.js +42 -0
  132. package/src/ui/dist/assets/diff-DbItnlRl.js +2 -0
  133. package/src/ui/dist/assets/directory-open-01563666-DWU9wJ6I.js +2 -0
  134. package/src/ui/dist/assets/directory-open-4ed118d0-CunoC1EB.js +2 -0
  135. package/src/ui/dist/assets/dockerfile-BKs6k2Af.js +2 -0
  136. package/src/ui/dist/assets/dtd-DF_7sFjM.js +2 -0
  137. package/src/ui/dist/assets/dylan-DwRh75JA.js +2 -0
  138. package/src/ui/dist/assets/ebnf-CDyGwa7X.js +2 -0
  139. package/src/ui/dist/assets/ebnfDiagram-CCIWWBDH-BC_mBRR5.js +2 -0
  140. package/src/ui/dist/assets/ecl-Cabwm37j.js +2 -0
  141. package/src/ui/dist/assets/eiffel-CnydiIhH.js +2 -0
  142. package/src/ui/dist/assets/el-GR-BZB4AONW-DsnwWweL.js +11 -0
  143. package/src/ui/dist/assets/elm-vLlmbW-K.js +2 -0
  144. package/src/ui/dist/assets/erDiagram-Q63AITRT-Bkxam36l.js +86 -0
  145. package/src/ui/dist/assets/erlang-BNw1qcRV.js +2 -0
  146. package/src/ui/dist/assets/es-ES-U4NZUMDT-CRxBcQgC.js +10 -0
  147. package/src/ui/dist/assets/eu-ES-A7QVB2H4-CTd_E7YR.js +12 -0
  148. package/src/ui/dist/assets/excalidraw-default-bMWIgNCy.svg +1 -0
  149. package/src/ui/dist/assets/fa-IR-HGAKTJCU-DO56k7eG.js +9 -0
  150. package/src/ui/dist/assets/factor-kuTfRLto.js +2 -0
  151. package/src/ui/dist/assets/fcl-Kvtd6kyn.js +2 -0
  152. package/src/ui/dist/assets/fi-FI-Z5N7JZ37-DEDCF9WB.js +7 -0
  153. package/src/ui/dist/assets/file-open-002ab408-DIuFHtCF.js +2 -0
  154. package/src/ui/dist/assets/file-open-7c801643-684qeFg4.js +2 -0
  155. package/src/ui/dist/assets/file-save-3189631c-C1wFhQhH.js +2 -0
  156. package/src/ui/dist/assets/file-save-745eba88-Bb9F9Kg7.js +2 -0
  157. package/src/ui/dist/assets/flowDiagram-23GEKE2U-C3SAEao7.js +157 -0
  158. package/src/ui/dist/assets/forth-Ffai-XNe.js +2 -0
  159. package/src/ui/dist/assets/fortran-DYz_wnZ1.js +2 -0
  160. package/src/ui/dist/assets/fr-FR-RHASNOE6-DE2nPyDt.js +10 -0
  161. package/src/ui/dist/assets/ganttDiagram-NO4QXBWP-U-J_Xi-_.js +293 -0
  162. package/src/ui/dist/assets/gas-Bneqetm1.js +2 -0
  163. package/src/ui/dist/assets/gherkin-heZmZLOM.js +2 -0
  164. package/src/ui/dist/assets/gitGraphDiagram-IHSO6WYX-BADVbD-a.js +107 -0
  165. package/src/ui/dist/assets/gl-ES-HMX3MZ6V-mCqVgQHC.js +11 -0
  166. package/src/ui/dist/assets/graph-C9eacEi8.js +2 -0
  167. package/src/ui/dist/assets/groovy-D9Dt4D0W.js +2 -0
  168. package/src/ui/dist/assets/haskell-Cw1EW3IL.js +2 -0
  169. package/src/ui/dist/assets/haxe-H-WmDvRZ.js +2 -0
  170. package/src/ui/dist/assets/he-IL-6SHJWFNN-KnaLrCzw.js +11 -0
  171. package/src/ui/dist/assets/hi-IN-IWLTKZ5I-wuqiaaWt.js +5 -0
  172. package/src/ui/dist/assets/http-DBlCnlav.js +2 -0
  173. package/src/ui/dist/assets/hu-HU-A5ZG7DT2-DLg_zm22.js +8 -0
  174. package/src/ui/dist/assets/id-ID-SAP4L64H-MB2mXqcR.js +11 -0
  175. package/src/ui/dist/assets/idl-BEugSyMb.js +2 -0
  176. package/src/ui/dist/assets/image-blob-reduce.esm-HwCm7IZ0.js +8 -0
  177. package/src/ui/dist/assets/index-39SJtrt0.js +2 -0
  178. package/src/ui/dist/assets/index-BQ0LIrW-.js +2 -0
  179. package/src/ui/dist/assets/index-BcAUh2Nm.css +1 -0
  180. package/src/ui/dist/assets/index-BcThOK-i.js +331 -0
  181. package/src/ui/dist/assets/index-BnxV3k_S.js +3 -0
  182. package/src/ui/dist/assets/index-C7MZBHbT.js +117 -0
  183. package/src/ui/dist/assets/index-CCvKI2rM.js +2 -0
  184. package/src/ui/dist/assets/index-CZvmcCab.js +8 -0
  185. package/src/ui/dist/assets/index-Cp3hLKNl.js +2 -0
  186. package/src/ui/dist/assets/index-D7ayFxcx.js +2 -0
  187. package/src/ui/dist/assets/index-DAYxSQGU.js +2 -0
  188. package/src/ui/dist/assets/index-DD-HeMV0.css +1 -0
  189. package/src/ui/dist/assets/index-DPMxd3Qn.js +2 -0
  190. package/src/ui/dist/assets/index-DSe2jx3k.js +4 -0
  191. package/src/ui/dist/assets/index-DUirCkgW.js +2 -0
  192. package/src/ui/dist/assets/index-DaKPQ2hN.js +159 -0
  193. package/src/ui/dist/assets/index-DzpuR0Jg.js +2 -0
  194. package/src/ui/dist/assets/index-POndwsCI.js +2 -0
  195. package/src/ui/dist/assets/index-R8NM9I_9.js +2 -0
  196. package/src/ui/dist/assets/index-TCDuDhBj.js +2 -0
  197. package/src/ui/dist/assets/index-TckzASdM.js +2 -0
  198. package/src/ui/dist/assets/index-Yi5ytFFr.js +2 -0
  199. package/src/ui/dist/assets/infoDiagram-FWYZ7A6U-CiGBlFqH.js +3 -0
  200. package/src/ui/dist/assets/init-Gi6I4Gst.js +2 -0
  201. package/src/ui/dist/assets/ishikawaDiagram-FXEZZL3T-B1aheRbz.js +71 -0
  202. package/src/ui/dist/assets/it-IT-JPQ66NNP-DT3HSU7X.js +12 -0
  203. package/src/ui/dist/assets/ja-JP-DBVTYXUO-CsfJjjWt.js +9 -0
  204. package/src/ui/dist/assets/javascript-iXu5QeM3.js +2 -0
  205. package/src/ui/dist/assets/journeyDiagram-5HDEW3XC-DtnJPxub.js +140 -0
  206. package/src/ui/dist/assets/julia-DuME0IfC.js +2 -0
  207. package/src/ui/dist/assets/kaa-6HZHGXH3-DQz-1-Gn.js +2 -0
  208. package/src/ui/dist/assets/kab-KAB-ZGHBKWFO-CaH-LSoK.js +9 -0
  209. package/src/ui/dist/assets/kanban-definition-HUTT4EX6-CZatSm3e.js +90 -0
  210. package/src/ui/dist/assets/katex-C5jXJg4s.js +258 -0
  211. package/src/ui/dist/assets/kk-KZ-P5N5QNE5-BMXmcm55.js +2 -0
  212. package/src/ui/dist/assets/km-KH-HSX4SM5Z-ZeoJNBpN.js +12 -0
  213. package/src/ui/dist/assets/ko-KR-MTYHY66A-BeaLRKph.js +10 -0
  214. package/src/ui/dist/assets/ku-TR-6OUDTVRD-B_28KrkR.js +10 -0
  215. package/src/ui/dist/assets/layout-DEXfKzaS.js +2 -0
  216. package/src/ui/dist/assets/linear-CpEDnQRC.js +2 -0
  217. package/src/ui/dist/assets/livescript-BwQOo05w.js +2 -0
  218. package/src/ui/dist/assets/lt-LT-XHIRWOB4-BxDcnUhp.js +4 -0
  219. package/src/ui/dist/assets/lua-VAEuO923.js +2 -0
  220. package/src/ui/dist/assets/lv-LV-5QDEKY6T-DpKDl1_y.js +8 -0
  221. package/src/ui/dist/assets/map-Czzmt4hB.js +2 -0
  222. package/src/ui/dist/assets/mathematica-DTrFuWx2.js +2 -0
  223. package/src/ui/dist/assets/mbox-CNhZ1qSd.js +2 -0
  224. package/src/ui/dist/assets/mermaidLoader-CBO31t0J.js +305 -0
  225. package/src/ui/dist/assets/mermaidLoader-CEK31ho9.css +1 -0
  226. package/src/ui/dist/assets/mindmap-definition-LN4V7U3C-CQ0TccEr.js +97 -0
  227. package/src/ui/dist/assets/mirc-CjQqDB4T.js +2 -0
  228. package/src/ui/dist/assets/mllike-CXdrOF99.js +2 -0
  229. package/src/ui/dist/assets/modelica-Dc1JOy9r.js +2 -0
  230. package/src/ui/dist/assets/mr-IN-CRQNXWMA-BKCZT58p.js +14 -0
  231. package/src/ui/dist/assets/mscgen-BA5vi2Kp.js +2 -0
  232. package/src/ui/dist/assets/mumps-BT43cFF4.js +2 -0
  233. package/src/ui/dist/assets/my-MM-5M5IBNSE-D34k0G9T.js +2 -0
  234. package/src/ui/dist/assets/nb-NO-T6EIAALU-DXr_KW8J.js +11 -0
  235. package/src/ui/dist/assets/nginx-DdIZxoE0.js +2 -0
  236. package/src/ui/dist/assets/nl-NL-IS3SIHDZ-C1L0aVDR.js +9 -0
  237. package/src/ui/dist/assets/nn-NO-6E72VCQL-CLWVSxbZ.js +9 -0
  238. package/src/ui/dist/assets/nsis-LdVXkNf5.js +2 -0
  239. package/src/ui/dist/assets/ntriples-BfvgReVJ.js +2 -0
  240. package/src/ui/dist/assets/oc-FR-POXYY2M6-nf8_J3eR.js +9 -0
  241. package/src/ui/dist/assets/octave-Ck1zUtKM.js +2 -0
  242. package/src/ui/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  243. package/src/ui/dist/assets/oz-BzwKVEFT.js +2 -0
  244. package/src/ui/dist/assets/pa-IN-N4M65BXN-CAMJ-xd6.js +5 -0
  245. package/src/ui/dist/assets/pascal--L3eBynH.js +2 -0
  246. package/src/ui/dist/assets/pegDiagram-2B236MQR-Bj2RcmRd.js +2 -0
  247. package/src/ui/dist/assets/percentages-BXMCSKIN-s9yWFdr6.js +160 -0
  248. package/src/ui/dist/assets/perl-CdXCOZ3F.js +2 -0
  249. package/src/ui/dist/assets/pica-BGv_AhAB.js +8 -0
  250. package/src/ui/dist/assets/pieDiagram-ENE6RG2P-obpzz8dC.js +40 -0
  251. package/src/ui/dist/assets/pig-CevX1Tat.js +2 -0
  252. package/src/ui/dist/assets/pl-PL-T2D74RX3-DKJ5H85v.js +10 -0
  253. package/src/ui/dist/assets/powershell-CFHJl5sT.js +2 -0
  254. package/src/ui/dist/assets/properties-C78fOPTZ.js +2 -0
  255. package/src/ui/dist/assets/protobuf-ChK-085T.js +2 -0
  256. package/src/ui/dist/assets/{proxy-D0vUbds5.js → proxy-BSmwef4H.js} +2 -2
  257. package/src/ui/dist/assets/pt-BR-5N22H2LF-B5kow-kN.js +10 -0
  258. package/src/ui/dist/assets/pt-PT-UZXXM6DQ-B2G34fso.js +10 -0
  259. package/src/ui/dist/assets/pug-DeIclll2.js +2 -0
  260. package/src/ui/dist/assets/puppet-DMA9R1ak.js +2 -0
  261. package/src/ui/dist/assets/python-BuPzkPfP.js +2 -0
  262. package/src/ui/dist/assets/q-pXgVlZs6.js +2 -0
  263. package/src/ui/dist/assets/quadrantDiagram-ABIIQ3AL-Cl-T7NkJ.js +8 -0
  264. package/src/ui/dist/assets/r-B6wPVr8A.js +2 -0
  265. package/src/ui/dist/assets/railroadDiagram-RFXS5EU6-DMq82RHg.js +2 -0
  266. package/src/ui/dist/assets/{refresh-cw-JNmExijd.js → refresh-cw-CbFd7guV.js} +2 -2
  267. package/src/ui/dist/assets/requirementDiagram-TGXJPOKE-tGInMDgU.js +85 -0
  268. package/src/ui/dist/assets/ro-RO-JPDTUUEW-BwLdZW91.js +12 -0
  269. package/src/ui/dist/assets/roundRect-0PYZxl1G.js +2 -0
  270. package/src/ui/dist/assets/rpm-CTu-6PCP.js +2 -0
  271. package/src/ui/dist/assets/ru-RU-B4JR7IUQ-DpdAgIJc.js +10 -0
  272. package/src/ui/dist/assets/ruby-B2Rjki9n.js +2 -0
  273. package/src/ui/dist/assets/runtimeBrand-BoKPimsz.js +2 -0
  274. package/src/ui/dist/assets/sankeyDiagram-HTMAVEWB-C7X-ET4m.js +41 -0
  275. package/src/ui/dist/assets/sas-B4kiWyti.js +2 -0
  276. package/src/ui/dist/assets/save-D1CXHwBw.js +12 -0
  277. package/src/ui/dist/assets/scheme-C41bIUwD.js +2 -0
  278. package/src/ui/dist/assets/sequenceDiagram-DBY2YBRQ-Bn24i_nY.js +163 -0
  279. package/src/ui/dist/assets/settings-2-DtSNAAcU.js +22 -0
  280. package/src/ui/dist/assets/shell-CjFT_Tl9.js +2 -0
  281. package/src/ui/dist/assets/shield-check-BVbJ8T_N.js +7 -0
  282. package/src/ui/dist/assets/si-LK-N5RQ5JYF-naJaUy5u.js +2 -0
  283. package/src/ui/dist/assets/sieve-C3Gn_uJK.js +2 -0
  284. package/src/ui/dist/assets/simple-mode-GW_nhZxv.js +2 -0
  285. package/src/ui/dist/assets/sizeCapture-X5ZJPWSS-DAQHfm82.js +2 -0
  286. package/src/ui/dist/assets/sk-SK-C5VTKIMK-1NPz1J_d.js +7 -0
  287. package/src/ui/dist/assets/sl-SI-NN7IZMDC-D8RfmNQw.js +7 -0
  288. package/src/ui/dist/assets/smalltalk-CnHTOXQT.js +2 -0
  289. package/src/ui/dist/assets/solr-DehyRSwq.js +2 -0
  290. package/src/ui/dist/assets/sparql-DkYu6x3z.js +2 -0
  291. package/src/ui/dist/assets/spreadsheet-BCZA_wO0.js +2 -0
  292. package/src/ui/dist/assets/sql-D0XecflT.js +2 -0
  293. package/src/ui/dist/assets/stateDiagram-2N3HPSRC-Ck48D1ck.js +2 -0
  294. package/src/ui/dist/assets/stateDiagram-v2-6OUMAXLB-CdfRCJAJ.js +2 -0
  295. package/src/ui/dist/assets/stex-C3f8Ysf7.js +2 -0
  296. package/src/ui/dist/assets/style-C3QI7VPf.css +1 -0
  297. package/src/ui/dist/assets/stylus-B533Al4x.js +2 -0
  298. package/src/ui/dist/assets/subset-shared.chunk-eAtLHmbi.js +85 -0
  299. package/src/ui/dist/assets/subset-worker.chunk-DeHCTnTk.js +2 -0
  300. package/src/ui/dist/assets/sv-SE-XGPEYMSR-BeOYCU-8.js +11 -0
  301. package/src/ui/dist/assets/swift-BzpIVaGY.js +2 -0
  302. package/src/ui/dist/assets/swimlanes-5IMT3BWC-DOy22UjP.js +3 -0
  303. package/src/ui/dist/assets/swimlanesDiagram-G3AALYLV-sicPbVOg.js +9 -0
  304. package/src/ui/dist/assets/ta-IN-2NMHFXQM-CtS68WDd.js +10 -0
  305. package/src/ui/dist/assets/tcl-DVfN8rqt.js +2 -0
  306. package/src/ui/dist/assets/{terminal-BBQvnOAK.js → terminal-DEby5Y_P.js} +2 -2
  307. package/src/ui/dist/assets/terminalControl-BkFeDVXo.js +134 -0
  308. package/src/ui/dist/assets/textile-CnDTJFAw.js +2 -0
  309. package/src/ui/dist/assets/th-TH-HPSO5L25-DCHY9-Co.js +3 -0
  310. package/src/ui/dist/assets/tiddlywiki-DO-Gjzrf.js +2 -0
  311. package/src/ui/dist/assets/tiki-DGYXhP31.js +2 -0
  312. package/src/ui/dist/assets/timeline-definition-FHXFAJF6-DVkG72qO.js +121 -0
  313. package/src/ui/dist/assets/toml-Bm5Em-hy.js +2 -0
  314. package/src/ui/dist/assets/tr-TR-DEFEU3FU-y5-vjTv8.js +8 -0
  315. package/src/ui/dist/assets/troff-wAsdV37c.js +2 -0
  316. package/src/ui/dist/assets/ttcn-CfJYG6tj.js +2 -0
  317. package/src/ui/dist/assets/ttcn-cfg-B9xdYoR4.js +2 -0
  318. package/src/ui/dist/assets/turtle-B1tBg_DP.js +2 -0
  319. package/src/ui/dist/assets/uk-UA-QMV73CPH-BRbR5It6.js +7 -0
  320. package/src/ui/dist/assets/useReducedMotion-D07XP52Q.js +12 -0
  321. package/src/ui/dist/assets/vb-CmGdzxic.js +2 -0
  322. package/src/ui/dist/assets/vbscript-BuJXcnF6.js +2 -0
  323. package/src/ui/dist/assets/velocity-D8B20fx6.js +2 -0
  324. package/src/ui/dist/assets/vennDiagram-L72KCM5P-DwMwBlf-.js +35 -0
  325. package/src/ui/dist/assets/verilog-C6RDOZhf.js +2 -0
  326. package/src/ui/dist/assets/vhdl-lSbBsy5d.js +2 -0
  327. package/src/ui/dist/assets/vi-VN-M7AON7JQ-s6wSBv2s.js +6 -0
  328. package/src/ui/dist/assets/wardleyDiagram-EHGQE667-CNuuvzEg.js +79 -0
  329. package/src/ui/dist/assets/webidl-ZXfAyPTL.js +2 -0
  330. package/src/ui/dist/assets/{x-DoIOGAJz.js → x-B84wq78k.js} +2 -2
  331. package/src/ui/dist/assets/xquery-DzFWVndE.js +2 -0
  332. package/src/ui/dist/assets/xychartDiagram-FW5EYKEG-dUpZxmn6.js +8 -0
  333. package/src/ui/dist/assets/yacas-BJ4BC0dw.js +2 -0
  334. package/src/ui/dist/assets/z80-Hz9HOZM7.js +2 -0
  335. package/src/ui/dist/assets/zh-CN-LNUGB5OW-Cu_-POQN.js +11 -0
  336. package/src/ui/dist/assets/zh-HK-E62DVLB3-BCx4W4qZ.js +2 -0
  337. package/src/ui/dist/assets/zh-TW-RAJ6MFWO-02wx_Mrj.js +10 -0
  338. package/src/ui/dist/icons/excalidraw.svg +1 -0
  339. package/src/ui/dist/icons/markdown.svg +1 -0
  340. package/src/ui/dist/index.html +2 -2
  341. package/src/ui/package.json +10 -0
  342. package/src/ui/playwright.config.ts +36 -0
  343. package/src/ui/pnpm-workspace.yaml +2 -0
  344. package/src/ui/public/icons/excalidraw.svg +1 -0
  345. package/src/ui/public/icons/markdown.svg +1 -0
  346. package/src/ui/tsconfig.json +2 -1
  347. package/src/ui/vite.config.ts +1 -0
  348. package/src/wf-ui-server/__tests__/a2a-store.test.mjs +432 -15
  349. package/src/wf-ui-server/__tests__/action-registry-loader.test.mjs +156 -0
  350. package/src/wf-ui-server/__tests__/claude-stream-json.test.mjs +397 -0
  351. package/src/wf-ui-server/__tests__/codex-appserver.test.mjs +392 -0
  352. package/src/wf-ui-server/__tests__/component-node-api.test.mjs +881 -0
  353. package/src/wf-ui-server/__tests__/component-node-store.test.mjs +401 -0
  354. package/src/wf-ui-server/__tests__/control-plane-acceptance.test.mjs +2027 -0
  355. package/src/wf-ui-server/__tests__/control-plane-cli-smoke.test.mjs +1014 -0
  356. package/src/wf-ui-server/__tests__/dispatch-lifecycle.test.mjs +330 -0
  357. package/src/wf-ui-server/__tests__/effort-compatibility-red-092.test.mjs +151 -0
  358. package/src/wf-ui-server/__tests__/graph-layout-agent-tree.test.mjs +254 -0
  359. package/src/wf-ui-server/__tests__/graph-layout-tidy.test.mjs +260 -0
  360. package/src/wf-ui-server/__tests__/graph-layout.test.mjs +84 -0
  361. package/src/wf-ui-server/__tests__/node-config-api.test.mjs +384 -0
  362. package/src/wf-ui-server/__tests__/opencode-server.test.mjs +526 -0
  363. package/src/wf-ui-server/__tests__/peer-capsule.test.mjs +2 -2
  364. package/src/wf-ui-server/__tests__/pty-adapter.test.mjs +71 -1
  365. package/src/wf-ui-server/__tests__/runtime-config.test.mjs +3 -3
  366. package/src/wf-ui-server/__tests__/runtime-detector.test.mjs +21 -0
  367. package/src/wf-ui-server/__tests__/runtime-review.test.mjs +828 -0
  368. package/src/wf-ui-server/__tests__/server.integration.test.mjs +1463 -11
  369. package/src/wf-ui-server/__tests__/session-cleanup.test.mjs +159 -0
  370. package/src/wf-ui-server/__tests__/session-registry.test.mjs +52 -3
  371. package/src/wf-ui-server/__tests__/settings.test.mjs +12 -2
  372. package/src/wf-ui-server/__tests__/spawn-gate.test.mjs +78 -0
  373. package/src/wf-ui-server/__tests__/task-group-parser.test.mjs +120 -0
  374. package/src/wf-ui-server/__tests__/task-group-tag.test.mjs +264 -0
  375. package/src/wf-ui-server/__tests__/task-parser.test.mjs +49 -3
  376. package/src/wf-ui-server/__tests__/terminal-context-hardening.test.mjs +122 -0
  377. package/src/wf-ui-server/__tests__/terminal-context.test.mjs +164 -0
  378. package/src/wf-ui-server/__tests__/terminal-store.test.mjs +4 -4
  379. package/src/wf-ui-server/__tests__/wf-browser-api.test.mjs +599 -0
  380. package/src/wf-ui-server/__tests__/wf-browser-store.test.mjs +184 -0
  381. package/src/wf-ui-server/__tests__/workflow-action-registry.test.mjs +282 -0
  382. package/src/wf-ui-server/__tests__/workflow-agent-communication.test.mjs +411 -0
  383. package/src/wf-ui-server/__tests__/workflow-agent-context.test.mjs +323 -0
  384. package/src/wf-ui-server/__tests__/workflow-agent-cooperation-permissions.test.mjs +318 -0
  385. package/src/wf-ui-server/__tests__/workflow-agent-messages-structured.test.mjs +458 -0
  386. package/src/wf-ui-server/__tests__/workflow-agent-routing.test.mjs +434 -0
  387. package/src/wf-ui-server/__tests__/workflow-agent-runtime.test.mjs +324 -0
  388. package/src/wf-ui-server/__tests__/workflow-api-integration.test.mjs +263 -0
  389. package/src/wf-ui-server/__tests__/workflow-api-phase3.test.mjs +1213 -0
  390. package/src/wf-ui-server/__tests__/workflow-claude-materialize.test.mjs +209 -0
  391. package/src/wf-ui-server/__tests__/workflow-cli-registry-manuals.test.mjs +173 -0
  392. package/src/wf-ui-server/__tests__/workflow-cli-surface.test.mjs +176 -0
  393. package/src/wf-ui-server/__tests__/workflow-codex-resume-id.test.mjs +556 -0
  394. package/src/wf-ui-server/__tests__/workflow-cooperation-enforcement.test.mjs +553 -0
  395. package/src/wf-ui-server/__tests__/workflow-depth-resume-dock.test.mjs +846 -0
  396. package/src/wf-ui-server/__tests__/workflow-display-node.test.mjs +150 -0
  397. package/src/wf-ui-server/__tests__/workflow-edge-update.test.mjs +296 -0
  398. package/src/wf-ui-server/__tests__/workflow-file-cache-lock-watch.test.mjs +380 -0
  399. package/src/wf-ui-server/__tests__/workflow-file-formats.test.mjs +994 -0
  400. package/src/wf-ui-server/__tests__/workflow-file-node-actions.test.mjs +294 -0
  401. package/src/wf-ui-server/__tests__/workflow-goal-actions.test.mjs +357 -0
  402. package/src/wf-ui-server/__tests__/workflow-goal-task-autoclose.test.mjs +256 -0
  403. package/src/wf-ui-server/__tests__/workflow-graceful-stop.test.mjs +108 -0
  404. package/src/wf-ui-server/__tests__/workflow-graph-undo.test.mjs +524 -0
  405. package/src/wf-ui-server/__tests__/workflow-layout.test.mjs +732 -0
  406. package/src/wf-ui-server/__tests__/workflow-magnetic-topology.test.mjs +222 -0
  407. package/src/wf-ui-server/__tests__/workflow-markdown-lock.test.mjs +359 -0
  408. package/src/wf-ui-server/__tests__/workflow-mcp-hub.test.mjs +174 -0
  409. package/src/wf-ui-server/__tests__/workflow-node-manual-schema.test.mjs +241 -0
  410. package/src/wf-ui-server/__tests__/workflow-node-runtime.test.mjs +1991 -0
  411. package/src/wf-ui-server/__tests__/workflow-node-store-utils.test.mjs +125 -0
  412. package/src/wf-ui-server/__tests__/workflow-registry-drift.test.mjs +296 -0
  413. package/src/wf-ui-server/__tests__/workflow-resource-handle-four-side.test.mjs +217 -0
  414. package/src/wf-ui-server/__tests__/workflow-role-profile.test.mjs +234 -0
  415. package/src/wf-ui-server/__tests__/workflow-set-model.test.mjs +261 -0
  416. package/src/wf-ui-server/__tests__/workflow-skill-group-context-enabled.test.mjs +131 -0
  417. package/src/wf-ui-server/__tests__/workflow-skill-group-enable-disable.test.mjs +186 -0
  418. package/src/wf-ui-server/__tests__/workflow-skills-hub.test.mjs +125 -0
  419. package/src/wf-ui-server/__tests__/workflow-state-reconcile.test.mjs +139 -0
  420. package/src/wf-ui-server/__tests__/workflow-subagent-strategy-matrix.test.mjs +529 -0
  421. package/src/wf-ui-server/__tests__/workflow-team-flow.test.mjs +278 -0
  422. package/src/wf-ui-server/__tests__/workflow-terminal-control-live.test.mjs +294 -0
  423. package/src/wf-ui-server/__tests__/workflow-terminal-control.test.mjs +631 -0
  424. package/src/wf-ui-server/__tests__/workflow-timer-wakeup.test.mjs +318 -0
  425. package/src/wf-ui-server/__tests__/workflow-unified-resume.test.mjs +537 -0
  426. package/src/wf-ui-server/__tests__/workspace-api-hardening.test.mjs +187 -0
  427. package/src/wf-ui-server/__tests__/workspace-api.test.mjs +382 -0
  428. package/src/wf-ui-server/__tests__/workspace-reveal.test.mjs +138 -0
  429. package/src/wf-ui-server/__tests__/workspace-store-hardening.test.mjs +255 -0
  430. package/src/wf-ui-server/__tests__/workspace-store.test.mjs +208 -0
  431. package/src/wf-ui-server/__tests__/ws-events.integration.test.mjs +52 -25
  432. package/src/wf-ui-server/__tests__/ws-terminal.integration.test.mjs +12 -6
  433. package/src/wf-ui-server/__tests__/ws-wf-browser.integration.test.mjs +488 -0
  434. package/src/wf-ui-server/a2a-store.mjs +3007 -89
  435. package/src/wf-ui-server/action-registry.mjs +277 -0
  436. package/src/wf-ui-server/bridge-store.mjs +276 -0
  437. package/src/wf-ui-server/chat-driver.mjs +220 -0
  438. package/src/wf-ui-server/chat-store.mjs +109 -0
  439. package/src/wf-ui-server/claude-transcript-materializer.mjs +235 -0
  440. package/src/wf-ui-server/codex-update-prompt.mjs +53 -0
  441. package/src/wf-ui-server/component-node-store.mjs +689 -0
  442. package/src/wf-ui-server/composition-api.mjs +436 -0
  443. package/src/wf-ui-server/control-plane-token.mjs +17 -0
  444. package/src/wf-ui-server/dispatch-contract.mjs +150 -0
  445. package/src/wf-ui-server/drivers/claude-stream-json.mjs +371 -0
  446. package/src/wf-ui-server/drivers/codex-appserver.mjs +513 -0
  447. package/src/wf-ui-server/drivers/opencode-server.mjs +486 -0
  448. package/src/wf-ui-server/excalidraw-svg.mjs +141 -0
  449. package/src/wf-ui-server/file-format-adapters.mjs +511 -0
  450. package/src/wf-ui-server/file-watcher.mjs +215 -0
  451. package/src/wf-ui-server/graph-layout.mjs +778 -0
  452. package/src/wf-ui-server/harness-env.mjs +46 -0
  453. package/src/wf-ui-server/json-store-utils.mjs +48 -0
  454. package/src/wf-ui-server/model-capability.mjs +773 -0
  455. package/src/wf-ui-server/node-config-store.mjs +233 -0
  456. package/src/wf-ui-server/node-store-utils.mjs +88 -0
  457. package/src/wf-ui-server/peer-capsule.mjs +56 -1
  458. package/src/wf-ui-server/pty-adapter.mjs +513 -11
  459. package/src/wf-ui-server/reveal-foreground.mjs +69 -0
  460. package/src/wf-ui-server/runtime-detector.mjs +52 -6
  461. package/src/wf-ui-server/server.mjs +5613 -403
  462. package/src/wf-ui-server/session-cleanup.mjs +363 -0
  463. package/src/wf-ui-server/session-registry.mjs +179 -3
  464. package/src/wf-ui-server/settings.mjs +25 -0
  465. package/src/wf-ui-server/spawn-gate.mjs +46 -0
  466. package/src/wf-ui-server/task-parser.mjs +88 -3
  467. package/src/wf-ui-server/terminal-store.mjs +225 -11
  468. package/src/wf-ui-server/timer-wakeup-scheduler.mjs +226 -0
  469. package/src/wf-ui-server/wf-browser-store.mjs +733 -0
  470. package/src/wf-ui-server/workflow-agent-context.mjs +1731 -0
  471. package/src/wf-ui-server/workflow-capability-node-store.mjs +676 -0
  472. package/src/wf-ui-server/workflow-event-node-store.mjs +623 -0
  473. package/src/wf-ui-server/workflow-goal-node-store.mjs +328 -0
  474. package/src/wf-ui-server/workflow-graph-store.mjs +623 -0
  475. package/src/wf-ui-server/workflow-mcp-hub.mjs +286 -0
  476. package/src/wf-ui-server/workflow-node-runtime.mjs +1767 -0
  477. package/src/wf-ui-server/workflow-node-settings-store.mjs +339 -0
  478. package/src/wf-ui-server/workflow-node-types/agent-node.mjs +920 -0
  479. package/src/wf-ui-server/workflow-node-types/display-node.mjs +151 -0
  480. package/src/wf-ui-server/workflow-node-types/excalidraw-node.mjs +145 -0
  481. package/src/wf-ui-server/workflow-node-types/file-node.mjs +484 -0
  482. package/src/wf-ui-server/workflow-node-types/github-trigger-node.mjs +207 -0
  483. package/src/wf-ui-server/workflow-node-types/goal-node.mjs +491 -0
  484. package/src/wf-ui-server/workflow-node-types/markdown-node.mjs +225 -0
  485. package/src/wf-ui-server/workflow-node-types/mcp-connector-node.mjs +71 -0
  486. package/src/wf-ui-server/workflow-node-types/role-profile-store.mjs +191 -0
  487. package/src/wf-ui-server/workflow-node-types/skill-group-node.mjs +98 -0
  488. package/src/wf-ui-server/workflow-node-types/timer-node.mjs +607 -0
  489. package/src/wf-ui-server/workflow-ontology.mjs +681 -0
  490. package/src/wf-ui-server/workflow-operation-store.mjs +115 -0
  491. package/src/wf-ui-server/workflow-skills-hub.mjs +366 -0
  492. package/src/wf-ui-server/workflow-skills-market.mjs +723 -0
  493. package/src/wf-ui-server/workspace-store.mjs +951 -0
  494. package/src/wf-ui-server/ws-chat.mjs +299 -0
  495. package/src/wf-ui-server/ws-events.mjs +45 -20
  496. package/src/wf-ui-server/ws-terminal.mjs +165 -22
  497. package/src/wf-ui-server/ws-wf-browser.mjs +419 -0
  498. package/templates/common/.claude/agents/review-manager.md +33 -29
  499. package/templates/common/.claude/commands/wf-browser.md +1 -1
  500. package/templates/common/.claude/commands/wf-help.md +5 -3
  501. package/templates/common/.claude/commands/wf-init.md +44 -0
  502. package/templates/common/.claude/commands/wf-learn.md +34 -8
  503. package/templates/common/.claude/commands/wf-max.md +19 -9
  504. package/templates/common/.claude/commands/wf-review.md +3 -3
  505. package/templates/common/.claude/commands/wf-search.md +55 -0
  506. package/templates/common/.claude/commands/wf-task-archive.md +5 -4
  507. package/templates/common/.claude/commands/wf-task-list.md +6 -3
  508. package/templates/common/.claude/commands/wf-task-record.md +10 -2
  509. package/templates/common/.claude/commands/wf-ui.md +12 -5
  510. package/templates/common/.claude/commands/wf-update.md +4 -0
  511. package/templates/common/.claude/commands/wf.md +12 -1
  512. package/templates/common/.claude/rules/ecc/common.md +10 -1
  513. package/templates/common/.claude/skills/wf/SKILL.md +36 -0
  514. package/templates/common/.claude/skills/wf-browser/SKILL.md +427 -122
  515. package/templates/common/.claude/skills/wf-browser/references/BROWSER_CONTROL_PATTERNS.md +43 -0
  516. package/templates/common/.claude/skills/wf-init/SKILL.md +33 -0
  517. package/templates/common/.claude/skills/wf-learn/SKILL.md +106 -26
  518. package/templates/common/.claude/skills/wf-max/SKILL.md +45 -11
  519. package/templates/common/.claude/skills/wf-review/SKILL.md +26 -54
  520. package/templates/common/.claude/skills/wf-search/SKILL.md +122 -0
  521. package/templates/common/.claude/skills/wf-search/references/evidence-contract.md +77 -0
  522. package/templates/common/.claude/skills/wf-search/references/policies/compare.md +38 -0
  523. package/templates/common/.claude/skills/wf-search/references/policies/fact.md +39 -0
  524. package/templates/common/.claude/skills/wf-search/references/policies/troubleshoot.md +39 -0
  525. package/templates/common/.claude/skills/wf-search/references/policies/verify.md +44 -0
  526. package/templates/common/.claude/skills/wf-search/references/source-routing.md +40 -0
  527. package/templates/common/.claude/skills/wf-search/references/tool-adapters.md +38 -0
  528. package/templates/common/.claude/skills/wf-ui/SKILL.md +146 -12
  529. package/templates/common/.claude/skills/wf-update/SKILL.md +5 -1
  530. package/templates/common/.harness-version +188 -85
  531. package/templates/common/.opencode/agents/review-manager.md +31 -27
  532. package/templates/common/.opencode/commands/wf-browser.md +1 -1
  533. package/templates/common/.opencode/commands/wf-help.md +5 -3
  534. package/templates/common/.opencode/commands/wf-init.md +44 -0
  535. package/templates/common/.opencode/commands/wf-learn.md +34 -8
  536. package/templates/common/.opencode/commands/wf-max.md +19 -9
  537. package/templates/common/.opencode/commands/wf-review.md +3 -3
  538. package/templates/common/.opencode/commands/wf-search.md +55 -0
  539. package/templates/common/.opencode/commands/wf-task-archive.md +5 -7
  540. package/templates/common/.opencode/commands/wf-task-list.md +6 -6
  541. package/templates/common/.opencode/commands/wf-task-record.md +10 -5
  542. package/templates/common/.opencode/commands/wf-ui.md +12 -5
  543. package/templates/common/.opencode/commands/wf-update.md +4 -0
  544. package/templates/common/.opencode/commands/wf.md +12 -1
  545. package/templates/common/CLAUDE.md +61 -3
  546. package/templates/common/Harness/MEMORY.md +8 -5
  547. package/templates/common/Harness/README.md +14 -10
  548. package/templates/common/Harness/a2a/action-registry.json +4397 -0
  549. package/templates/common/Harness/a2a/role-graph.json +16 -3
  550. package/templates/common/Harness/a2a/skills/terminal-control.json +45 -1
  551. package/templates/common/Harness/a2a/skills/wf-ui-map.json +50 -0
  552. package/templates/common/Harness/a2a/skills/workflow-agent-node.json +143 -0
  553. package/templates/common/Harness/a2a/skills/workflow-context.json +95 -0
  554. package/templates/common/Harness/a2a/skills/workflow-diagram-node.json +60 -0
  555. package/templates/common/Harness/a2a/skills/workflow-display-node.json +69 -0
  556. package/templates/common/Harness/a2a/skills/workflow-file-node.json +92 -0
  557. package/templates/common/Harness/a2a/skills/workflow-goal-node.json +76 -0
  558. package/templates/common/Harness/a2a/skills/workflow-markdown-node.json +79 -0
  559. package/templates/common/Harness/a2a/skills/workflow-mcp-connector-node.json +59 -0
  560. package/templates/common/Harness/a2a/skills/workflow-node-actions.json +105 -0
  561. package/templates/common/Harness/a2a/skills/workflow-node-map.json +71 -0
  562. package/templates/common/Harness/a2a/skills/workflow-ontology.json +54 -0
  563. package/templates/common/Harness/a2a/skills/workflow-resource-node.json +68 -0
  564. package/templates/common/Harness/a2a/skills/workflow-skill-group-node.json +56 -0
  565. package/templates/common/Harness/a2a/skills/workflow-timer-node.json +81 -0
  566. package/templates/common/Harness/a2a/workflows.json +33 -0
  567. package/templates/common/Harness/ownership.manifest.json +263 -2
  568. package/templates/common/Harness/project/architecture.md +499 -0
  569. package/templates/common/Harness/research/README.md +20 -0
  570. package/templates/common/Harness/scripts/README.md +70 -6
  571. package/templates/common/Harness/scripts/a2a-terminal.mjs +12 -4
  572. package/templates/common/Harness/scripts/context-budget.mjs +7 -3
  573. package/templates/common/Harness/scripts/lib/memory-context.mjs +400 -0
  574. package/templates/common/Harness/scripts/lib/path-scope.mjs +77 -0
  575. package/templates/common/Harness/scripts/lib/research-policy.mjs +99 -0
  576. package/templates/common/Harness/scripts/lib/wf-search.mjs +189 -0
  577. package/templates/common/Harness/scripts/memory-context.mjs +63 -0
  578. package/templates/common/Harness/scripts/research-policy.mjs +27 -0
  579. package/templates/common/Harness/scripts/task-context.mjs +547 -0
  580. package/templates/common/Harness/scripts/task-group-index.mjs +242 -0
  581. package/templates/common/Harness/scripts/task-state.mjs +659 -95
  582. package/templates/common/Harness/scripts/validate-harness.mjs +70 -28
  583. package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +24 -0
  584. package/templates/common/Harness/scripts/wf-remove.mjs +10 -0
  585. package/templates/common/Harness/scripts/wf-search.mjs +10 -0
  586. package/templates/common/Harness/scripts/wf-ui-control.mjs +3144 -0
  587. package/templates/common/Harness/scripts/wf-update-check.mjs +274 -51
  588. package/templates/common/Harness/scripts/wf-update-runner.mjs +53 -16
  589. package/templates/common/Harness/settings.json +10 -0
  590. package/templates/common/Harness/specs/guides/SETUP.md +4 -3
  591. package/templates/common/Harness/specs/protocols/HARNESS_BRIDGE.md +429 -48
  592. package/templates/common/Harness/specs/protocols/MEMORY_PROTOCOL.md +24 -0
  593. package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +14 -11
  594. package/templates/common/Harness/specs/runtime/command-surface.json +28 -0
  595. package/templates/common/Harness/specs/runtime/context-loading.md +6 -0
  596. package/templates/common/Harness/specs/runtime/dispatch.md +9 -1
  597. package/templates/common/Harness/specs/runtime/subagents.md +20 -5
  598. package/templates/common/Harness/specs/workflows/WF-AUTO-SPARK.md +19 -0
  599. package/templates/common/Harness/specs/workflows/WF-AUTO.md +21 -0
  600. package/templates/common/Harness/specs/workflows/WF-KERNEL.md +4 -2
  601. package/templates/common/Harness/specs/workflows/WF-MAX.md +39 -12
  602. package/templates/common/Harness/specs/workflows/WF-STATE.md +62 -14
  603. package/templates/common/Harness/specs/workflows/WF.md +32 -0
  604. package/templates/common/Harness/tasks/INDEX.json +7 -0
  605. package/templates/common/Harness/tasks/INDEX.md +12 -0
  606. package/templates/common/Harness/tasks/_template/PLAN.md +6 -0
  607. package/templates/common/Harness/tasks/_template/PROGRESS.md +5 -0
  608. package/templates/common/Harness/tasks/_template/STATE.json +22 -0
  609. package/templates/common/Harness/templates/TEST_PLAN.template.md +5 -4
  610. package/templates/common/Harness/templates/UI_CONTRACT.template.md +7 -5
  611. package/templates/common/Harness/wf-browser/README.md +69 -0
  612. package/templates/common/README.md +17 -0
  613. package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js +0 -17
  614. package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js.map +0 -1
  615. package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js +0 -7
  616. package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js.map +0 -1
  617. package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js +0 -12
  618. package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js.map +0 -1
  619. package/src/ui/dist/assets/TaskList-BN4r8s1i.js +0 -50
  620. package/src/ui/dist/assets/TaskList-BN4r8s1i.js.map +0 -1
  621. package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js +0 -74
  622. package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js.map +0 -1
  623. package/src/ui/dist/assets/WorkflowRoute-BnuhLJ6X.css +0 -1
  624. package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js +0 -39
  625. package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js.map +0 -1
  626. package/src/ui/dist/assets/index-BOVYWntB.js +0 -117
  627. package/src/ui/dist/assets/index-BOVYWntB.js.map +0 -1
  628. package/src/ui/dist/assets/index-DaUObq0H.css +0 -1
  629. package/src/ui/dist/assets/maximize-2-LNrr_rKr.js +0 -7
  630. package/src/ui/dist/assets/maximize-2-LNrr_rKr.js.map +0 -1
  631. package/src/ui/dist/assets/plus-QuTJyoT3.js +0 -7
  632. package/src/ui/dist/assets/plus-QuTJyoT3.js.map +0 -1
  633. package/src/ui/dist/assets/proxy-D0vUbds5.js.map +0 -1
  634. package/src/ui/dist/assets/refresh-cw-JNmExijd.js.map +0 -1
  635. package/src/ui/dist/assets/terminal-BBQvnOAK.js.map +0 -1
  636. package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js +0 -7
  637. package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js.map +0 -1
  638. package/src/ui/dist/assets/x-DoIOGAJz.js.map +0 -1
  639. package/src/ui/pnpm-lock.yaml +0 -1582
  640. package/src/ui/src/App.tsx +0 -86
  641. package/src/ui/src/api.ts +0 -93
  642. package/src/ui/src/components/AgentsRoute.tsx +0 -503
  643. package/src/ui/src/components/Footer.tsx +0 -69
  644. package/src/ui/src/components/Header.tsx +0 -175
  645. package/src/ui/src/components/LoadingView.tsx +0 -22
  646. package/src/ui/src/components/RolesRoute.tsx +0 -169
  647. package/src/ui/src/components/SettingsRoute.tsx +0 -166
  648. package/src/ui/src/components/TaskList.tsx +0 -388
  649. package/src/ui/src/components/TerminalDrawer.tsx +0 -611
  650. package/src/ui/src/components/WorkflowRoute.tsx +0 -670
  651. package/src/ui/src/hooks/useReducedMotion.ts +0 -17
  652. package/src/ui/src/hooks/useServerConnection.ts +0 -114
  653. package/src/ui/src/index.css +0 -281
  654. package/src/ui/src/main.tsx +0 -11
  655. package/src/ui/src/types.ts +0 -108
  656. /package/src/ui/dist/assets/{TerminalDrawer-6GBZ9nXN.css → terminalControl-6GBZ9nXN.css} +0 -0
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
4
5
 
5
6
  const root = process.cwd();
6
7
  const harnessDir = path.join(root, 'Harness');
@@ -13,15 +14,15 @@ const command = args[0] && !args[0].startsWith('--') ? args[0] : 'help';
13
14
  const OUTER_TASK_CAP = 5;
14
15
  const RESERVED = new Set(['_template', '_archive', 'continuous']);
15
16
  const TASK_ID_RE = /^task-[a-z]+(-[a-z0-9]+){1,4}$/;
17
+ const CANONICAL_STATUSES = new Set(['active', 'blocked', 'closed']);
16
18
  const NEVER_ARCHIVE_STATUSES = new Set([
17
19
  'active',
18
20
  'blocked',
19
- 'in_progress',
20
- 'running',
21
- 'pending',
22
- 'needs-user-decision',
23
21
  ]);
24
22
  const SAFE_ARCHIVE_STATUSES = new Set([
23
+ 'closed',
24
+ // Legacy values are retained for archive compatibility. New writes use
25
+ // the canonical lifecycle statuses above.
25
26
  'complete',
26
27
  'verified',
27
28
  'archived',
@@ -32,8 +33,13 @@ const SAFE_ARCHIVE_STATUSES = new Set([
32
33
  'closeout',
33
34
  ]);
34
35
  const VALID_STATUSES = new Set([
36
+ ...CANONICAL_STATUSES,
35
37
  ...NEVER_ARCHIVE_STATUSES,
36
38
  ...SAFE_ARCHIVE_STATUSES,
39
+ 'in_progress',
40
+ 'running',
41
+ 'pending',
42
+ 'needs-user-decision',
37
43
  'skipped',
38
44
  'failed',
39
45
  ]);
@@ -64,15 +70,31 @@ const PHASE_ALIASES = new Map([
64
70
  ['closed', 'closeout'],
65
71
  ]);
66
72
  const STATUS_ALIASES = new Map([
67
- ['in-progress', 'in_progress'],
68
- ['inprogress', 'in_progress'],
69
- ['needs_user_decision', 'needs-user-decision'],
70
- ['needs-user', 'needs-user-decision'],
71
- ['need-user-decision', 'needs-user-decision'],
72
- ['close-out', 'closeout'],
73
+ // Legacy values intentionally normalize to the smaller lifecycle. The raw
74
+ // value remains available to archive/history code when needed.
75
+ ['in-progress', 'active'],
76
+ ['inprogress', 'active'],
77
+ ['in_progress', 'active'],
78
+ ['running', 'active'],
79
+ ['pending', 'active'],
80
+ ['needs_user_decision', 'blocked'],
81
+ ['needs-user-decision', 'blocked'],
82
+ ['needs-user', 'blocked'],
83
+ ['need-user-decision', 'blocked'],
84
+ ['complete', 'closed'],
85
+ ['completed', 'closed'],
86
+ ['verified', 'closed'],
87
+ ['archived', 'closed'],
88
+ ['abandoned', 'closed'],
89
+ ['obsolete', 'closed'],
90
+ ['done', 'closed'],
91
+ ['closeout', 'closed'],
92
+ ['skipped', 'closed'],
93
+ ['failed', 'blocked'],
94
+ ['close-out', 'closed'],
73
95
  ]);
74
- const OPEN_TASK_STATUSES = new Set(['active', 'blocked', 'in_progress', 'running', 'pending', 'needs-user-decision']);
75
- const VALUE_FLAGS = new Set(['--keep', '--mode', '--phase', '--status', '--task', '--text', '--title', '--note', '--context']);
96
+ const OPEN_TASK_STATUSES = new Set(['active', 'blocked']);
97
+ const VALUE_FLAGS = new Set(['--keep', '--mode', '--phase', '--status', '--task', '--text', '--title', '--note', '--context', '--group', '--project', '--tag', '--tags', '--keyword', '--since']);
76
98
 
77
99
  function hasFlag(name) {
78
100
  return args.includes(name);
@@ -111,10 +133,25 @@ function print(payload) {
111
133
  if (payload.command === 'list') {
112
134
  console.log(`Active Task: ${payload.activeTask || 'None'}`);
113
135
  console.log(`Tasks: ${payload.taskCount}`);
114
- for (const task of payload.tasks || []) {
136
+ const renderTask = (task) => {
115
137
  const deps = task.dependsOn?.length ? ` dependsOn: [${task.dependsOn.join(', ')}]` : '';
116
138
  const blocks = task.blocks?.length ? ` blocks: [${task.blocks.join(', ')}]` : '';
117
- console.log(`- ${task.id}: status=${task.status || '-'} phase=${task.phase || '-'}${deps}${blocks}`);
139
+ const project = task.project || task.group || 'default';
140
+ const tags = task.tags?.length ? ` tags=[${task.tags.join(', ')}]` : '';
141
+ const dates = task.createdAt || task.updatedAt
142
+ ? ` created=${String(task.createdAt || '-').slice(0, 10)} updated=${String(task.updatedAt || '-').slice(0, 10)}`
143
+ : '';
144
+ console.log(`- ${task.id}: project=${project} status=${task.status || '-'} phase=${task.phase || '-'}${tags}${dates}${deps}${blocks}`);
145
+ };
146
+ if (payload.groups) {
147
+ console.log('');
148
+ console.log('=== Task Groups ===');
149
+ for (const group of payload.groups) {
150
+ console.log(`${group.group} (${group.tasks.length} task${group.tasks.length === 1 ? '' : 's'}):`);
151
+ for (const task of group.tasks) renderTask(task);
152
+ }
153
+ } else {
154
+ for (const task of payload.tasks || []) renderTask(task);
118
155
  }
119
156
 
120
157
  // Render graph
@@ -154,11 +191,28 @@ function print(payload) {
154
191
  for (const op of payload.operations) console.log(`- ${op.action}: ${op.taskId || op.path} (${op.reason})`);
155
192
  } else if (payload.command === 'archive') {
156
193
  if (payload.dryRun) console.log('[DRY RUN] No files moved. Use --apply to execute.');
194
+ if (payload.group) console.log(`Group: ${payload.group}`);
157
195
  console.log(`Scanned: ${payload.scanned}, Archiveable: ${payload.archiveable}, To archive: ${payload.toArchive}, Kept: ${payload.kept}, Skipped: ${payload.skipped}`);
158
196
  for (const r of payload.results) {
159
197
  const suffix = r.path ? ` -> _archive/${r.path}` : '';
160
198
  console.log(`- ${r.action}: ${r.dir} (${r.status})${suffix}`);
161
199
  }
200
+ } else if (payload.command === 'open') {
201
+ console.log(`Open tasks: ${payload.taskCount}`);
202
+ for (const task of payload.tasks || []) {
203
+ const blocked = task.blockedByOpenDeps?.length ? ` blockedBy=[${task.blockedByOpenDeps.join(', ')}]` : '';
204
+ console.log(`- ${task.id}: project=${task.project || task.group || 'default'} status=${task.status || '-'} phase=${task.phase || '-'} updated=${String(task.updatedAt || '-').slice(0, 10)}${blocked}`);
205
+ }
206
+ } else if (payload.command === 'query') {
207
+ console.log(`Query: ${payload.keyword} (${payload.count} match${payload.count === 1 ? '' : 'es'})`);
208
+ for (const result of payload.results || []) {
209
+ console.log(`- ${result.id}: project=${result.project || result.group || 'default'} status=${result.status || '-'} path=${result.path}`);
210
+ for (const hit of result.hits || []) console.log(` ${hit.file}:${hit.line} ${hit.snippet}`);
211
+ }
212
+ } else if (payload.command === 'index') {
213
+ console.log(`Task index rebuilt: ${payload.taskCount} task(s) in ${payload.projectCount} project(s).`);
214
+ console.log(`- JSON: ${payload.path}`);
215
+ console.log(`- Markdown: ${payload.markdownPath}`);
162
216
  } else {
163
217
  console.log(payload.message || '');
164
218
  }
@@ -179,23 +233,32 @@ function usage() {
179
233
  message: `Usage: node Harness/scripts/task-state.mjs <command> [options]
180
234
 
181
235
  Commands:
182
- list [--json] List task state with dependency/resume info.
236
+ list [--json] [--group <project>] [--project <project>] [--status <s>] [--by-group]
237
+ List task state with dependency/resume info.
238
+ --group is a legacy alias for --project.
239
+ index [--json] Rebuild Harness/tasks/INDEX.json and INDEX.md.
240
+ query <keyword> [--project <project>] [--status <s>] [--json]
241
+ Search task metadata and capsule notes.
183
242
  validate [--strict] [--json] Validate state consistency (includes link checks).
184
243
  reconcile [--dry-run|--apply] [--json] Normalize STATE.json and root PROGRESS.md.
185
- set-active <task-id> [--dry-run] Set the single active task.
244
+ set-active <task-id> [--dry-run] Set the focus/resume task (multiple open tasks are valid).
186
245
  transition <task-id> --status <s> --phase <p> [--dry-run]
187
- archive [--dry-run|--apply] [--keep n] [--task id] [--json]
246
+ archive [--dry-run|--apply] [--keep n] [--task id] [--group <tag>] [--json]
188
247
  Archive eligible tasks to _archive/YYYY/MM/DD/.
189
- Explicit --apply (no --task filter) archives ALL.
248
+ Explicit --apply (no --task/--group filter) archives ALL.
249
+ --group archives a whole tag (batch op, no confirmation;
250
+ it implies --apply unless --dry-run is given).
190
251
  history list [--year YYYY] [--month MM] [--json]
191
252
  List archived tasks, optional year/month filter.
192
253
  history search <keyword> [--json] Full-text search archived PLAN/PROGRESS/PROBLEM/REFERENCES.
193
254
  history load <task-id> [--json] Load one archived task's full record.
194
255
  history delete <task-id> [--dry-run|--apply] [--json]
195
256
  Delete an archived task (audit trail written).
196
- record <task-id> [--create] [--text "description"] [--status <s>] [--mode <m>] [--dry-run|--apply] [--json]
257
+ record <task-id> [--create] [--text "description"] [--status <s>] [--phase <p>] [--mode <m>] [--group <project>] [--project <project>] [--tag <tag[,tag...]>] [--dry-run|--apply] [--json]
197
258
  Create or update a task record.
198
- open [--json] List open (non-archived, active-status) tasks.
259
+ --project is canonical; --group remains compatible.
260
+ open [--project <project>] [--status <s>] [--json]
261
+ List all active/blocked tasks; multiple open tasks are valid.
199
262
 
200
263
  Archive defaults to dry-run and keeps ${OUTER_TASK_CAP} non-archived task capsules.`,
201
264
  }, 0);
@@ -216,6 +279,33 @@ function writeJsonAtomic(file, value) {
216
279
  writeTextAtomic(file, JSON.stringify(value, null, 2) + '\n');
217
280
  }
218
281
 
282
+ // Small parameterized helpers for adjacent runtime consumers. The task-state
283
+ // CLI remains the canonical owner of task paths and atomic writes; these
284
+ // helpers deliberately do not normalize or maintain a second task store.
285
+ function taskPathsForProject(projectRoot) {
286
+ const project = path.resolve(String(projectRoot || ''));
287
+ const harness = path.join(project, 'Harness');
288
+ const tasks = path.join(harness, 'tasks');
289
+ return { project, harnessDir: harness, tasksDir: tasks };
290
+ }
291
+
292
+ function readTaskState(projectRoot, taskId) {
293
+ const { tasksDir: projectTasksDir } = taskPathsForProject(projectRoot);
294
+ const file = path.join(projectTasksDir, String(taskId));
295
+ const statePath = path.join(file, 'STATE.json');
296
+ if (!fs.existsSync(statePath)) return { state: null, error: null, path: statePath };
297
+ try {
298
+ return { state: JSON.parse(fs.readFileSync(statePath, 'utf8')), error: null, path: statePath };
299
+ } catch (err) {
300
+ return { state: null, error: `invalid STATE.json: ${err.message}`, path: statePath };
301
+ }
302
+ }
303
+
304
+ function writeTaskStateAtomic(projectRoot, taskId, state) {
305
+ const { tasksDir: projectTasksDir } = taskPathsForProject(projectRoot);
306
+ writeJsonAtomic(path.join(projectTasksDir, String(taskId), 'STATE.json'), state);
307
+ }
308
+
219
309
  function normalizeCandidate(value, validSet, aliasMap) {
220
310
  if (value === null || value === undefined) return '';
221
311
  const raw = String(value).trim().toLowerCase();
@@ -272,12 +362,10 @@ function displayPhase(phase) {
272
362
 
273
363
  function statusFromPhase(phase, isActive) {
274
364
  const normalized = normalizePhase(phase);
275
- if (isActive) return 'active';
276
- if (normalized === 'verified' || normalized === 'closeout') return 'verified';
277
- if (normalized === 'archived') return 'archived';
365
+ if (normalized === 'verified' || normalized === 'closeout' || normalized === 'archived') return 'closed';
278
366
  if (normalized === 'blocked') return 'blocked';
279
- if (normalized) return 'in_progress';
280
- return 'pending';
367
+ if (isActive) return 'active';
368
+ return 'active';
281
369
  }
282
370
 
283
371
  function defaultQueues() {
@@ -310,6 +398,10 @@ const VALID_MODES = new Set([
310
398
  'wf-review',
311
399
  'wf-browser',
312
400
  ]);
401
+ // Durable task lifecycle is opt-in only for the two task-owning WF modes.
402
+ // wf-auto/wf-auto-spark remain explicit workflow capabilities, but their
403
+ // continuous capsule is not a normal task lifecycle and must not adopt tasks.
404
+ const WF_MANAGED_MODES = new Set(['wf', 'wf-max']);
313
405
 
314
406
  function normalizeMode(value) {
315
407
  if (value === null || value === undefined) return '';
@@ -318,6 +410,21 @@ function normalizeMode(value) {
318
410
  if (VALID_MODES.has(raw)) return raw;
319
411
  return '';
320
412
  }
413
+
414
+ function isWfManagedMode(value) {
415
+ return WF_MANAGED_MODES.has(normalizeMode(value));
416
+ }
417
+
418
+ function lifecycleForState(state, fallbackStatus = 'active') {
419
+ const status = normalizeStatus(state?.status) || fallbackStatus;
420
+ const mode = normalizeMode(state?.mode) || 'direct';
421
+ const wfManaged = isWfManagedMode(mode);
422
+ return {
423
+ mode,
424
+ wfManaged,
425
+ resumeRequired: wfManaged && OPEN_TASK_STATUSES.has(status),
426
+ };
427
+ }
321
428
  function normalizeQueues(state) {
322
429
  const source = state && typeof state.queues === 'object' && state.queues ? state.queues : state || {};
323
430
  return {
@@ -401,7 +508,9 @@ function collectTasks() {
401
508
  const statePhase = normalizePhase(state?.phase);
402
509
  const rootPhase = normalizePhase(rootRow?.phase);
403
510
  const progressPhase = normalizePhase(progressPhaseRaw);
404
- const phase = rootPhase || progressPhase || statePhase;
511
+ // STATE.json is authoritative once present; root/task progress remains a
512
+ // compatibility fallback for legacy capsules without normalized state.
513
+ const phase = statePhase || progressPhase || rootPhase;
405
514
  const status = stateStatus || statusFromPhase(phase, id === rootProgress.activeTask);
406
515
  const stat = fs.statSync(path.join(tasksDir, id));
407
516
  return {
@@ -484,12 +593,8 @@ function validateState({ strict = false } = {}) {
484
593
  if (task.state.phase && !normalizePhase(task.state.phase)) {
485
594
  issue(`${task.id}: unknown phase "${task.state.phase}"`);
486
595
  }
487
- if (normalizeStatus(task.state.status) === 'active' && task.id !== rootProgress.activeTask) {
488
- issue(`${task.id}: STATE.json is active but root Active Task is ${rootProgress.activeTask || 'None'}`, true);
489
- }
490
- if (task.id === rootProgress.activeTask && normalizeStatus(task.state.status) && normalizeStatus(task.state.status) !== 'active') {
491
- issue(`${task.id}: root Active Task points here but STATE.json status is "${normalizeStatus(task.state.status)}"`, true);
492
- }
596
+ // Multiple tasks may be active. The root Active Task is retained as a
597
+ // focus/resume pointer for compatibility, not as a singleton invariant.
493
598
 
494
599
  const links = task.state.links || {};
495
600
  if (Array.isArray(links.dependsOn)) {
@@ -534,10 +639,18 @@ function validateState({ strict = false } = {}) {
534
639
  }
535
640
  }
536
641
 
537
- const activeStateTasks = tasks.filter(task => normalizeStatus(task.state?.status) === 'active');
538
- if (activeStateTasks.length > 1) {
539
- issue(`Multiple STATE.json files are active: ${activeStateTasks.map(task => task.id).join(', ')}`, true);
642
+ const focusedTask = tasks.find(task => task.id === rootProgress.activeTask);
643
+ const openManagedTasks = tasks.filter(task => {
644
+ const status = normalizeStatus(task.state?.status) || task.status;
645
+ return isWfManagedMode(task.state?.mode) && OPEN_TASK_STATUSES.has(status);
646
+ });
647
+ if (focusedTask && openManagedTasks.length > 0) {
648
+ const focusedStatus = normalizeStatus(focusedTask.state?.status) || focusedTask.status;
649
+ if (!isWfManagedMode(focusedTask.state?.mode) && OPEN_TASK_STATUSES.has(focusedStatus)) {
650
+ issue(`Direct task "${focusedTask.id}" cannot be the active focus while an open WF-managed task exists`, true);
651
+ }
540
652
  }
653
+
541
654
  if (tasks.length > OUTER_TASK_CAP) {
542
655
  issue(`Harness/tasks/ has ${tasks.length} outer task capsules (cap ${OUTER_TASK_CAP}); remind the user to run $wf-task-archive when they want to archive completed tasks`);
543
656
  }
@@ -578,33 +691,41 @@ function safeTaskPath(...segments) {
578
691
  }
579
692
 
580
693
  function desiredPhaseForTask(task, activeTask) {
581
- return task.rootPhase || task.progressPhase || task.statePhase || (task.id === activeTask ? 'implement' : 'intake');
694
+ return task.statePhase || task.progressPhase || task.rootPhase || (task.id === activeTask ? 'implement' : 'intake');
582
695
  }
583
696
 
584
697
  function desiredStatusForTask(task, activeTask, desiredPhase) {
585
- if (task.id === activeTask) return 'active';
586
698
  const current = normalizeStatus(task.state?.status);
587
- if (current && current !== 'active') return current;
588
- return statusFromPhase(desiredPhase, false);
699
+ const phaseStatus = statusFromPhase(desiredPhase, false);
700
+ if (current === 'active' && phaseStatus === 'closed') return 'closed';
701
+ if (current) return current;
702
+ return statusFromPhase(desiredPhase, task.id === activeTask);
589
703
  }
590
704
 
591
705
  function defaultState(taskId, status, phase, now) {
592
706
  const runtime = defaultTaskRuntime();
707
+ const lifecycleStatus = normalizeStatus(status) || 'active';
593
708
  return {
594
709
  schemaVersion: 1,
595
710
  taskId,
596
- status,
711
+ status: lifecycleStatus,
597
712
  mode: 'direct',
598
713
  defaultRuntime: runtime,
599
714
  defaultAgentRuntime: runtime,
600
715
  tier: 'none',
601
716
  phase,
602
717
  gate: null,
718
+ project: 'default',
719
+ tags: [],
720
+ createdAt: now,
721
+ startedAt: lifecycleStatus === 'active' ? now : null,
722
+ closedAt: lifecycleStatus === 'closed' ? now : null,
603
723
  updatedAt: now,
604
724
  activeQuestion: null,
605
725
  nextAction: 'Review task state.',
606
726
  acceptance: [],
607
727
  queues: defaultQueues(),
728
+ workItems: [],
608
729
  dispatchLedger: [],
609
730
  decisions: [],
610
731
  risks: [],
@@ -622,16 +743,30 @@ function normalizeState(task, activeTask, now) {
622
743
 
623
744
  state.schemaVersion = 1;
624
745
  state.taskId = task.id;
625
- state.status = status;
746
+ // Keep an archived marker intact when an old repository has an archived
747
+ // capsule in the live directory; archive migration can still move it.
748
+ const rawStatus = String(task.state?.status || '').trim().toLowerCase();
749
+ state.status = rawStatus === 'archived' ? 'archived' : status;
626
750
  state.phase = phase;
627
751
  if (!state.mode) state.mode = 'direct';
628
752
  if (!state.defaultRuntime) state.defaultRuntime = defaultTaskRuntime();
629
753
  if (!state.defaultAgentRuntime) state.defaultAgentRuntime = state.defaultRuntime;
630
754
  if (!state.tier) state.tier = 'none';
631
755
  if (!Object.prototype.hasOwnProperty.call(state, 'gate')) state.gate = null;
756
+ state.tags = normalizeTags(state.tags);
757
+ if (!state.createdAt) state.createdAt = now;
758
+ if (!Object.prototype.hasOwnProperty.call(state, 'startedAt')) state.startedAt = state.status === 'active' ? now : null;
759
+ if (!Object.prototype.hasOwnProperty.call(state, 'closedAt')) state.closedAt = state.status === 'closed' || state.status === 'archived' ? now : null;
760
+ if (state.status === 'active' && !state.startedAt) state.startedAt = now;
761
+ if (state.status === 'closed' || state.status === 'archived') {
762
+ if (!state.closedAt) state.closedAt = now;
763
+ } else if (state.closedAt) {
764
+ state.closedAt = null;
765
+ }
632
766
  if (!Object.prototype.hasOwnProperty.call(state, 'activeQuestion')) state.activeQuestion = null;
633
767
  if (!state.nextAction) state.nextAction = task.rootRow?.goal || 'Review task state.';
634
768
  state.queues = normalizeQueues(state);
769
+ if (!Array.isArray(state.workItems)) state.workItems = [];
635
770
  if (!Array.isArray(state.dispatchLedger)) state.dispatchLedger = [];
636
771
  if (!Array.isArray(state.decisions)) state.decisions = [];
637
772
  if (!Array.isArray(state.risks)) state.risks = [];
@@ -708,11 +843,14 @@ function buildRows(tasks, activeTask, excluded = new Set()) {
708
843
  return ids.map(id => {
709
844
  const task = byId.get(id);
710
845
  const phase = normalizePhase(task.desiredState?.phase) || task.phase;
846
+ const desiredState = task.desiredState || task.state || {};
711
847
  return {
712
848
  id,
713
849
  goal: task.rootRow?.goal || task.state?.goal || task.state?.nextAction || taskTitle(id),
714
850
  phase: displayPhase(phase),
715
- closed: task.rootRow?.closed || '-',
851
+ closed: desiredState.closedAt
852
+ ? String(desiredState.closedAt).slice(0, 10)
853
+ : task.rootRow?.closed || '-',
716
854
  };
717
855
  });
718
856
  }
@@ -740,6 +878,10 @@ function buildReconcilePlan({ activeOverride = undefined, transition = null } =
740
878
  if (transition.status) {
741
879
  const status = normalizeStatus(transition.status);
742
880
  if (!status) errors.push(`${task.id}: unknown transition status "${transition.status}"`);
881
+ const currentStatus = normalizeStatus(task.state?.status) || task.status;
882
+ if (status && isWfManagedMode(task.state?.mode) && currentStatus === 'closed' && status !== 'closed') {
883
+ errors.push(`${task.id}: closed WF-managed task cannot be reopened; create a new task capsule`);
884
+ }
743
885
  if (status === 'active') activeTask = task.id;
744
886
  if (rootProgress.activeTask === task.id && SAFE_ARCHIVE_STATUSES.has(status)) activeTask = null;
745
887
  }
@@ -749,6 +891,18 @@ function buildReconcilePlan({ activeOverride = undefined, transition = null } =
749
891
  }
750
892
  }
751
893
 
894
+ const focusedTask = tasks.find(task => task.id === activeTask);
895
+ const openManagedTasks = tasks.filter(task => {
896
+ const status = normalizeStatus(task.state?.status) || task.status;
897
+ return isWfManagedMode(task.state?.mode) && OPEN_TASK_STATUSES.has(status);
898
+ });
899
+ if (focusedTask && openManagedTasks.length > 0) {
900
+ const focusedStatus = normalizeStatus(focusedTask.state?.status) || focusedTask.status;
901
+ if (!isWfManagedMode(focusedTask.state?.mode) && OPEN_TASK_STATUSES.has(focusedStatus)) {
902
+ errors.push(`Direct task "${activeTask}" cannot replace an open WF-managed focus; close the WF task or focus another WF task.`);
903
+ }
904
+ }
905
+
752
906
  if (errors.length) {
753
907
  return { ok: false, command: 'reconcile', dryRun: true, activeTask, errors, warnings: [], operations: [] };
754
908
  }
@@ -761,6 +915,9 @@ function buildReconcilePlan({ activeOverride = undefined, transition = null } =
761
915
  if (transition.status) nextState.status = normalizeStatus(transition.status);
762
916
  if (transition.phase) nextState.phase = normalizePhase(transition.phase);
763
917
  if (transition.nextAction) nextState.nextAction = transition.nextAction;
918
+ if (nextState.status === 'active' && !nextState.startedAt) nextState.startedAt = now;
919
+ if (nextState.status === 'closed' && !nextState.closedAt) nextState.closedAt = now;
920
+ if (nextState.status !== 'closed' && nextState.status !== 'archived') nextState.closedAt = null;
764
921
  nextState.updatedAt = now;
765
922
  normalized = {
766
923
  state: nextState,
@@ -826,6 +983,31 @@ function applyOperations(operations) {
826
983
  }
827
984
  }
828
985
 
986
+ function syncTaskViewsForState(taskId, state) {
987
+ const { rootProgress, tasks } = collectTasks();
988
+ const task = tasks.find(item => item.id === taskId);
989
+ if (!task) return;
990
+
991
+ task.desiredState = state;
992
+ let activeTask = rootProgress.activeTask;
993
+ const status = normalizeStatus(state.status);
994
+ if (activeTask === taskId && (status === 'closed' || status === 'archived')) activeTask = null;
995
+
996
+ const progressFile = path.join(tasksDir, taskId, 'PROGRESS.md');
997
+ const existingProgress = readText(progressFile);
998
+ const desiredProgress = renderTaskProgress(existingProgress, taskId, state);
999
+ if (desiredProgress !== existingProgress.replace(/\r\n/g, '\n')) {
1000
+ writeTextAtomic(progressFile, desiredProgress);
1001
+ }
1002
+
1003
+ const rows = buildRows(tasks, activeTask);
1004
+ const desiredRoot = renderRootProgress(rootProgress.text, activeTask, rows);
1005
+ if (desiredRoot !== rootProgress.text.replace(/\r\n/g, '\n')) {
1006
+ writeTextAtomic(progressPath, desiredRoot);
1007
+ }
1008
+ writeTaskIndex();
1009
+ }
1010
+
829
1011
  function buildListGraph(expandedTasks) {
830
1012
  const byId = new Map(expandedTasks.map(t => [t.id, t]));
831
1013
  const graph = { roots: [], depEdges: [], blockEdges: [], orphanedDeps: [] };
@@ -858,19 +1040,151 @@ function buildListGraph(expandedTasks) {
858
1040
  return graph;
859
1041
  }
860
1042
 
1043
+ function taskGroupFor(state) {
1044
+ const project = String(state?.project || '').trim();
1045
+ const legacyGroup = String(state?.group || '').trim();
1046
+ // A migrated legacy capsule may have project="default" while its
1047
+ // meaningful grouping still lives in group. Preserve that grouping.
1048
+ return (project && project !== 'default') || !legacyGroup
1049
+ ? (project || legacyGroup || 'default')
1050
+ : legacyGroup;
1051
+ }
1052
+
1053
+ function taskProjectFor(state) {
1054
+ return taskGroupFor(state);
1055
+ }
1056
+
1057
+ function taskTagsFor(state) {
1058
+ return Array.isArray(state?.tags)
1059
+ ? state.tags.map(tag => String(tag).trim()).filter(Boolean)
1060
+ : [];
1061
+ }
1062
+
1063
+ function normalizeTags(value) {
1064
+ return String(value || '')
1065
+ .split(',')
1066
+ .map(tag => tag.trim())
1067
+ .filter(Boolean);
1068
+ }
1069
+
1070
+ function taskIndexEntry(task) {
1071
+ const state = task.state || {};
1072
+ const links = state.links || {};
1073
+ const status = normalizeStatus(state.status) || task.status || 'active';
1074
+ const lifecycle = lifecycleForState(state, status);
1075
+ return {
1076
+ id: task.id,
1077
+ project: taskProjectFor(state),
1078
+ group: taskProjectFor(state),
1079
+ title: task.rootRow?.goal || state.goal || state.nextAction || taskTitle(task.id),
1080
+ status,
1081
+ mode: lifecycle.mode,
1082
+ wfManaged: lifecycle.wfManaged,
1083
+ resumeRequired: lifecycle.resumeRequired,
1084
+ phase: normalizePhase(state.phase) || task.phase || 'intake',
1085
+ tags: taskTagsFor(state),
1086
+ createdAt: state.createdAt || null,
1087
+ startedAt: state.startedAt || null,
1088
+ updatedAt: state.updatedAt || null,
1089
+ closedAt: state.closedAt || null,
1090
+ dependsOn: Array.isArray(links.dependsOn) ? links.dependsOn : [],
1091
+ blocks: Array.isArray(links.blocks) ? links.blocks : [],
1092
+ path: `Harness/tasks/${task.id}`,
1093
+ };
1094
+ }
1095
+
1096
+ function buildTaskIndexModel() {
1097
+ const { rootProgress, tasks } = collectTasks();
1098
+ const entries = tasks.map(taskIndexEntry).sort((a, b) => a.id.localeCompare(b.id));
1099
+ const projects = {};
1100
+ for (const task of entries) {
1101
+ if (!projects[task.project]) projects[task.project] = [];
1102
+ projects[task.project].push(task.id);
1103
+ }
1104
+ const activeEntry = entries.find(task => task.id === rootProgress.activeTask) || null;
1105
+ const resumeTask = activeEntry?.resumeRequired
1106
+ ? {
1107
+ taskId: activeEntry.id,
1108
+ mode: activeEntry.mode,
1109
+ status: activeEntry.status,
1110
+ project: activeEntry.project,
1111
+ }
1112
+ : null;
1113
+ return {
1114
+ schemaVersion: 1,
1115
+ generatedAt: new Date().toISOString(),
1116
+ sourceOfTruth: 'Harness/tasks/*/STATE.json',
1117
+ activeTask: rootProgress.activeTask,
1118
+ resumeTask,
1119
+ resumeRequired: Boolean(resumeTask),
1120
+ projects,
1121
+ tasks: entries,
1122
+ };
1123
+ }
1124
+
1125
+ function renderTaskIndexMd(model) {
1126
+ const lines = [
1127
+ '# Task Index (generated)',
1128
+ '',
1129
+ `Generated: ${model.generatedAt}`,
1130
+ '',
1131
+ 'This file is derived from task STATE.json files. Do not edit it directly.',
1132
+ '',
1133
+ '| Task | Project | Status | Mode | Phase | Created | Updated |',
1134
+ '|------|---------|--------|------|-------|---------|---------|',
1135
+ ];
1136
+ for (const task of model.tasks) {
1137
+ lines.push(`| \`${task.id}\` | ${task.project} | ${task.status} | ${task.mode} | ${task.phase} | ${String(task.createdAt || '-').slice(0, 10)} | ${String(task.updatedAt || '-').slice(0, 10)} |`);
1138
+ }
1139
+ lines.push('', '## Projects', '');
1140
+ for (const [project, ids] of Object.entries(model.projects).sort(([a], [b]) => a.localeCompare(b))) {
1141
+ lines.push(`- **${project}**: ${ids.map(id => `\`${id}\``).join(', ')}`);
1142
+ }
1143
+ lines.push('');
1144
+ return lines.join('\n');
1145
+ }
1146
+
1147
+ function writeTaskIndex() {
1148
+ const model = buildTaskIndexModel();
1149
+ writeJsonAtomic(path.join(tasksDir, 'INDEX.json'), model);
1150
+ writeTextAtomic(path.join(tasksDir, 'INDEX.md'), renderTaskIndexMd(model));
1151
+ return model;
1152
+ }
1153
+
1154
+ function taskSearchText(task) {
1155
+ const files = ['PLAN.md', 'PROGRESS.md', 'PROBLEM.md', 'REFERENCES.md'];
1156
+ const stateText = JSON.stringify(task.state || {});
1157
+ const fileText = files.map(file => readText(path.join(task.path, file))).join('\n');
1158
+ return `${task.id}\n${stateText}\n${fileText}`.toLowerCase();
1159
+ }
1160
+
861
1161
  function runList() {
862
1162
  const { rootProgress, tasks } = collectTasks();
1163
+ const groupFilter = flagValue('--group');
1164
+ const projectFilter = flagValue('--project') || groupFilter;
1165
+ const statusFilter = flagValue('--status');
863
1166
 
864
1167
  const expandedTasks = tasks.map(task => {
865
1168
  const state = task.state || {};
866
1169
  const links = state.links || {};
867
1170
  const status = normalizeStatus(state.status) || task.status;
1171
+ const lifecycle = lifecycleForState(state, status);
868
1172
  return {
869
1173
  id: task.id,
870
1174
  status,
1175
+ mode: lifecycle.mode,
1176
+ wfManaged: lifecycle.wfManaged,
1177
+ resumeRequired: lifecycle.resumeRequired,
871
1178
  phase: normalizePhase(state.phase) || task.phase,
872
1179
  rootPhase: task.rootPhase,
873
1180
  progressPhase: task.progressPhase,
1181
+ project: taskProjectFor(state),
1182
+ group: taskProjectFor(state),
1183
+ tags: taskTagsFor(state),
1184
+ createdAt: state.createdAt || null,
1185
+ startedAt: state.startedAt || null,
1186
+ updatedAt: state.updatedAt || null,
1187
+ closedAt: state.closedAt || null,
874
1188
  dependsOn: Array.isArray(links.dependsOn) ? links.dependsOn : [],
875
1189
  blocks: Array.isArray(links.blocks) ? links.blocks : [],
876
1190
  statusDisplay: status || '-',
@@ -880,14 +1194,48 @@ function runList() {
880
1194
  };
881
1195
  });
882
1196
 
883
- const graph = buildListGraph(expandedTasks);
1197
+ const normalizedStatusFilter = statusFilter ? normalizeStatus(statusFilter) : null;
1198
+ if (statusFilter && !normalizedStatusFilter) {
1199
+ finish({
1200
+ ok: false,
1201
+ command: 'list',
1202
+ errors: [`Invalid status filter "${statusFilter}". Valid: active, blocked, closed (legacy aliases accepted)`],
1203
+ warnings: [],
1204
+ tasks: [],
1205
+ }, 1);
1206
+ return;
1207
+ }
1208
+ const selectedTasks = projectFilter
1209
+ ? expandedTasks.filter(task => task.project === projectFilter)
1210
+ : expandedTasks;
1211
+ const statusTasks = normalizedStatusFilter
1212
+ ? selectedTasks.filter(task => task.status === normalizedStatusFilter)
1213
+ : selectedTasks;
1214
+
1215
+ let groups = null;
1216
+ if (hasFlag('--by-group')) {
1217
+ const byName = new Map();
1218
+ for (const task of statusTasks) {
1219
+ if (!byName.has(task.group)) byName.set(task.group, []);
1220
+ byName.get(task.group).push(task);
1221
+ }
1222
+ groups = [...byName.entries()]
1223
+ .map(([group, groupTasks]) => ({ group, tasks: groupTasks }))
1224
+ .sort((a, b) => (a.group === 'default' ? 1 : 0) - (b.group === 'default' ? 1 : 0) || a.group.localeCompare(b.group));
1225
+ }
1226
+
1227
+ const graph = buildListGraph(statusTasks);
884
1228
 
885
1229
  const payload = {
886
1230
  ok: true,
887
1231
  command: 'list',
888
- taskCount: expandedTasks.length,
1232
+ taskCount: statusTasks.length,
889
1233
  activeTask: rootProgress.activeTask,
890
- tasks: expandedTasks,
1234
+ groupFilter: groupFilter || null,
1235
+ projectFilter: projectFilter || null,
1236
+ statusFilter: normalizedStatusFilter,
1237
+ tasks: statusTasks,
1238
+ groups,
891
1239
  graph: {
892
1240
  roots: graph.roots.map(t => t.id),
893
1241
  depEdges: graph.depEdges,
@@ -900,6 +1248,77 @@ function runList() {
900
1248
  finish(payload, 0);
901
1249
  }
902
1250
 
1251
+ function runIndex() {
1252
+ const model = writeTaskIndex();
1253
+ finish({
1254
+ ok: true,
1255
+ command: 'index',
1256
+ path: 'Harness/tasks/INDEX.json',
1257
+ markdownPath: 'Harness/tasks/INDEX.md',
1258
+ generatedAt: model.generatedAt,
1259
+ projectCount: Object.keys(model.projects).length,
1260
+ taskCount: model.tasks.length,
1261
+ projects: model.projects,
1262
+ warnings: [],
1263
+ }, 0);
1264
+ }
1265
+
1266
+ function runQuery() {
1267
+ const keyword = flagValue('--keyword') || findTaskIdArg(1);
1268
+ const projectFilter = flagValue('--project') || flagValue('--group');
1269
+ const statusFilter = flagValue('--status');
1270
+ const normalizedStatusFilter = statusFilter ? normalizeStatus(statusFilter) : null;
1271
+ if (statusFilter && !normalizedStatusFilter) {
1272
+ finish({
1273
+ ok: false,
1274
+ command: 'query',
1275
+ errors: [`Invalid status filter "${statusFilter}". Valid: active, blocked, closed (legacy aliases accepted)`],
1276
+ warnings: [],
1277
+ results: [],
1278
+ }, 1);
1279
+ return;
1280
+ }
1281
+ if (!keyword) {
1282
+ finish({ ok: false, command: 'query', errors: ['query requires <keyword> or --keyword <text>'], warnings: [], results: [] }, 1);
1283
+ return;
1284
+ }
1285
+
1286
+ const needle = String(keyword).trim().toLowerCase();
1287
+ const { tasks } = collectTasks();
1288
+ const results = tasks
1289
+ .filter(task => {
1290
+ const status = normalizeStatus(task.state?.status) || task.status;
1291
+ if (projectFilter && taskProjectFor(task.state) !== projectFilter) return false;
1292
+ if (normalizedStatusFilter && status !== normalizedStatusFilter) return false;
1293
+ return taskSearchText(task).includes(needle);
1294
+ })
1295
+ .map(task => {
1296
+ const entry = taskIndexEntry(task);
1297
+ const hits = [];
1298
+ for (const file of ['PLAN.md', 'PROGRESS.md', 'PROBLEM.md', 'REFERENCES.md', 'STATE.json']) {
1299
+ const content = readText(path.join(task.path, file));
1300
+ const lines = content.split('\n');
1301
+ for (let index = 0; index < lines.length; index += 1) {
1302
+ if (lines[index].toLowerCase().includes(needle)) {
1303
+ hits.push({ file, line: index + 1, snippet: lines[index].trim().slice(0, 160) });
1304
+ }
1305
+ }
1306
+ }
1307
+ return { ...entry, hits };
1308
+ });
1309
+
1310
+ finish({
1311
+ ok: true,
1312
+ command: 'query',
1313
+ keyword,
1314
+ projectFilter: projectFilter || null,
1315
+ statusFilter: normalizedStatusFilter,
1316
+ count: results.length,
1317
+ results,
1318
+ warnings: [],
1319
+ }, 0);
1320
+ }
1321
+
903
1322
  function runValidate() {
904
1323
  const payload = validateState({ strict: hasFlag('--strict') });
905
1324
  finish(payload, payload.ok ? 0 : 1);
@@ -910,6 +1329,7 @@ function runReconcile(overrides = {}) {
910
1329
  if (overrides.commandLabel) plan.command = overrides.commandLabel;
911
1330
  if (plan.ok && hasFlag('--apply')) {
912
1331
  applyOperations(plan.operations);
1332
+ writeTaskIndex();
913
1333
  plan.dryRun = false;
914
1334
  }
915
1335
  finish(plan, plan.ok ? 0 : 1);
@@ -922,6 +1342,31 @@ function runSetActive() {
922
1342
  } catch (err) {
923
1343
  finish({ ok: false, command: 'set-active', errors: [err.message], warnings: [], operations: [] }, 1);
924
1344
  }
1345
+ const { tasks } = collectTasks();
1346
+ const task = tasks.find(item => item.id === taskId);
1347
+ const status = normalizeStatus(task?.state?.status) || task?.status;
1348
+ if (status === 'closed' || status === 'archived') {
1349
+ finish({
1350
+ ok: false,
1351
+ command: 'set-active',
1352
+ errors: [`Cannot set closed task "${taskId}" as active focus.`],
1353
+ warnings: [],
1354
+ operations: [],
1355
+ }, 1);
1356
+ }
1357
+ const openManagedTasks = tasks.filter(item => {
1358
+ const itemStatus = normalizeStatus(item.state?.status) || item.status;
1359
+ return isWfManagedMode(item.state?.mode) && OPEN_TASK_STATUSES.has(itemStatus);
1360
+ });
1361
+ if (openManagedTasks.length > 0 && !isWfManagedMode(task?.state?.mode)) {
1362
+ finish({
1363
+ ok: false,
1364
+ command: 'set-active',
1365
+ errors: [`Direct task "${taskId}" cannot replace an open WF-managed focus; close the WF task or focus another WF task.`],
1366
+ warnings: [],
1367
+ operations: [],
1368
+ }, 1);
1369
+ }
925
1370
  const previousApply = hasFlag('--apply');
926
1371
  if (!previousApply && !hasFlag('--dry-run')) args.push('--apply');
927
1372
  runReconcile({ activeOverride: taskId, commandLabel: 'set-active' });
@@ -959,11 +1404,23 @@ function buildArchivePlan() {
959
1404
  }
960
1405
  }
961
1406
 
1407
+ const groupFilter = flagValue('--group');
1408
+ if (taskFilter && groupFilter) {
1409
+ return { ok: false, command: 'archive', errors: ['archive accepts either --task <id> or --group <tag>, not both'], warnings: [], results: [] };
1410
+ }
1411
+
962
1412
  const { rootProgress, tasks } = collectTasks();
963
- const selectedTasks = taskFilter ? tasks.filter(task => task.id === taskFilter) : tasks;
1413
+ const selectedTasks = taskFilter
1414
+ ? tasks.filter(task => task.id === taskFilter)
1415
+ : groupFilter
1416
+ ? tasks.filter(task => taskGroupFor(task.state) === groupFilter)
1417
+ : tasks;
964
1418
  if (taskFilter && selectedTasks.length === 0) {
965
1419
  return { ok: false, command: 'archive', errors: [`Task "${taskFilter}" not found in Harness/tasks/`], warnings: [], results: [] };
966
1420
  }
1421
+ if (groupFilter && selectedTasks.length === 0) {
1422
+ return { ok: false, command: 'archive', errors: [`Group "${groupFilter}" not found in Harness/tasks/`], warnings: [], results: [] };
1423
+ }
967
1424
 
968
1425
  const archiveable = [];
969
1426
  const skipped = [];
@@ -974,13 +1431,14 @@ function buildArchivePlan() {
974
1431
  }
975
1432
 
976
1433
  const dryRun = !hasFlag('--apply');
977
- const explicitTrigger = dryRun === false && !taskFilter;
1434
+ const explicitTrigger = dryRun === false && !taskFilter && !groupFilter;
978
1435
  archiveable.sort((a, b) => a.mtimeMs - b.mtimeMs || a.id.localeCompare(b.id));
979
1436
 
980
- // Explicit user trigger (no --task filter, with --apply): archive ALL eligible tasks.
1437
+ // Explicit user trigger (no --task/--group filter, with --apply): archive ALL eligible tasks.
981
1438
  // Explicit --task targets the selected eligible task in both dry-run and apply.
1439
+ // Explicit --group is a batch op that archives every eligible task in the tag (implies apply).
982
1440
  // Auto/scheduled/dry-run: only archive tasks exceeding --keep cap.
983
- const toArchiveCount = taskFilter
1441
+ const toArchiveCount = taskFilter || groupFilter
984
1442
  ? archiveable.length
985
1443
  : explicitTrigger
986
1444
  ? archiveable.length
@@ -1019,8 +1477,8 @@ function buildArchivePlan() {
1019
1477
  ...dateParts(task.mtimeMs),
1020
1478
  action: hasFlag('--apply') ? 'archived' : 'would-archive',
1021
1479
  status: hasFlag('--apply')
1022
- ? (explicitTrigger ? 'explicit --apply: all eligible' : 'moved')
1023
- : (taskFilter ? 'dry-run' : `dry-run (${toArchiveCount} of ${archiveable.length} eligible)`),
1480
+ ? (groupFilter ? `group "${groupFilter}": batch archive` : explicitTrigger ? 'explicit --apply: all eligible' : 'moved')
1481
+ : (taskFilter ? 'dry-run' : groupFilter ? `dry-run (group "${groupFilter}")` : `dry-run (${toArchiveCount} of ${archiveable.length} eligible)`),
1024
1482
  })),
1025
1483
  ...keptArchiveable.map(task => ({
1026
1484
  dir: task.id,
@@ -1043,6 +1501,7 @@ function buildArchivePlan() {
1043
1501
  ok: true,
1044
1502
  command: 'archive',
1045
1503
  dryRun: !hasFlag('--apply'),
1504
+ group: groupFilter || null,
1046
1505
  keep: keepValue,
1047
1506
  scanned: selectedTasks.length,
1048
1507
  archiveable: archiveable.length,
@@ -1247,6 +1706,8 @@ function appendArchiveIndex(entries, graphContent) {
1247
1706
  }
1248
1707
 
1249
1708
  function runArchive() {
1709
+ // --group is a confirmation-less batch op: it implies --apply unless --dry-run is given.
1710
+ if (hasFlag('--group') && !hasFlag('--apply') && !hasFlag('--dry-run')) args.push('--apply');
1250
1711
  const plan = buildArchivePlan();
1251
1712
  if (!plan.ok) finish(plan, 1);
1252
1713
 
@@ -1279,6 +1740,7 @@ function runArchive() {
1279
1740
  const rows = buildRows(plan.tasks, plan.rootProgress.activeTask, plan.toArchiveIds);
1280
1741
  const desiredProgress = renderRootProgress(plan.rootProgress.text, plan.rootProgress.activeTask, rows);
1281
1742
  writeTextAtomic(progressPath, desiredProgress);
1743
+ writeTaskIndex();
1282
1744
  plan.dryRun = false;
1283
1745
  }
1284
1746
 
@@ -1424,7 +1886,11 @@ function runRecord() {
1424
1886
 
1425
1887
  const text = flagValue('--text');
1426
1888
  const statusRaw = flagValue('--status');
1889
+ const phaseRaw = flagValue('--phase');
1427
1890
  const modeRaw = flagValue('--mode');
1891
+ const groupRaw = flagValue('--group');
1892
+ const projectRaw = flagValue('--project');
1893
+ const tagsRaw = flagValue('--tag') || flagValue('--tags');
1428
1894
 
1429
1895
  if (!existing.state && isCreate) {
1430
1896
  const now = new Date().toISOString();
@@ -1435,20 +1901,31 @@ function runRecord() {
1435
1901
  return;
1436
1902
  }
1437
1903
  }
1438
- const status = statusRaw ? normalizeStatus(statusRaw) : 'pending';
1439
- const mode = modeRaw || 'direct';
1440
- const newState = defaultState(taskId, status, 'intake', now);
1441
- newState.mode = mode;
1442
- if (text) newState.nextAction = text;
1443
- if (modeRaw) {
1444
- const normalizedMode = normalizeMode(modeRaw);
1445
- if (!normalizedMode) {
1446
- finish({ ok: false, command: 'record', errors: [`Invalid mode "${modeRaw}". Valid: ${[...VALID_MODES].join(', ')}`], warnings: [] }, 1);
1904
+ if (phaseRaw && !normalizePhase(phaseRaw)) {
1905
+ finish({ ok: false, command: 'record', errors: [`Invalid phase "${phaseRaw}". Valid: ${[...VALID_PHASES].join(', ')}`], warnings: [] }, 1);
1447
1906
  return;
1448
1907
  }
1449
- newState.mode = normalizedMode;
1450
- }
1451
-
1908
+ const status = statusRaw ? normalizeStatus(statusRaw) : 'active';
1909
+ let mode = 'direct';
1910
+ if (modeRaw) {
1911
+ const normalizedMode = normalizeMode(modeRaw);
1912
+ if (!normalizedMode) {
1913
+ finish({ ok: false, command: 'record', errors: [`Invalid mode "${modeRaw}". Valid: ${[...VALID_MODES].join(', ')}`], warnings: [] }, 1);
1914
+ return;
1915
+ }
1916
+ mode = normalizedMode;
1917
+ }
1918
+ const phase = phaseRaw ? normalizePhase(phaseRaw) : 'intake';
1919
+ const newState = defaultState(taskId, status, phase, now);
1920
+ newState.mode = mode;
1921
+ const project = String(projectRaw || groupRaw || '').trim();
1922
+ if (project) {
1923
+ newState.project = project;
1924
+ // Keep the legacy key populated for older Harness readers.
1925
+ newState.group = project;
1926
+ }
1927
+ if (tagsRaw) newState.tags = normalizeTags(tagsRaw);
1928
+ if (text) newState.nextAction = text;
1452
1929
  const templateState = readTemplateState();
1453
1930
  if (templateState) {
1454
1931
  if (Array.isArray(templateState.acceptance)) newState.acceptance = [...templateState.acceptance];
@@ -1480,8 +1957,12 @@ function runRecord() {
1480
1957
  const newRow = { id: taskId, goal: text || taskTitle(taskId), phase: displayPhase('intake'), closed: '-' };
1481
1958
  parsed.rows.push(newRow);
1482
1959
  const rows = parsed.rows.map(r => ({ id: r.id, goal: r.goal, phase: r.phase, closed: r.closed }));
1483
- const newRoot = renderRootProgress(rootText, parsed.activeTask, rows);
1960
+ const newActiveTask = isWfManagedMode(newState.mode) && OPEN_TASK_STATUSES.has(newState.status)
1961
+ ? taskId
1962
+ : parsed.activeTask;
1963
+ const newRoot = renderRootProgress(rootText, newActiveTask, rows);
1484
1964
  writeTextAtomic(progressPath, newRoot);
1965
+ writeTaskIndex();
1485
1966
  }
1486
1967
 
1487
1968
  finish({
@@ -1499,9 +1980,36 @@ function runRecord() {
1499
1980
  if (existing.state) {
1500
1981
  const now = new Date().toISOString();
1501
1982
  const updated = { ...existing.state };
1983
+ if (!updated.createdAt) updated.createdAt = updated.updatedAt || now;
1984
+ if (!Object.prototype.hasOwnProperty.call(updated, 'startedAt')) updated.startedAt = null;
1985
+ if (!Object.prototype.hasOwnProperty.call(updated, 'closedAt')) updated.closedAt = null;
1502
1986
  updated.updatedAt = now;
1503
1987
  if (!updated.defaultRuntime) updated.defaultRuntime = defaultTaskRuntime();
1504
1988
  if (!updated.defaultAgentRuntime) updated.defaultAgentRuntime = updated.defaultRuntime;
1989
+ const currentMode = normalizeMode(updated.mode) || 'direct';
1990
+ const currentStatus = normalizeStatus(updated.status) || 'active';
1991
+ if (modeRaw) {
1992
+ const normalizedMode = normalizeMode(modeRaw);
1993
+ if (!normalizedMode) {
1994
+ finish({ ok: false, command: 'record', errors: [`Invalid mode "${modeRaw}". Valid: ${[...VALID_MODES].join(', ')}`], warnings: [] }, 1);
1995
+ return;
1996
+ }
1997
+ if (currentMode === 'direct' && isWfManagedMode(normalizedMode)) {
1998
+ finish({ ok: false, command: 'record', errors: [`Direct task "${taskId}" cannot be promoted into WF-managed lifecycle; create a new task with --mode ${normalizedMode}.`], warnings: [] }, 1);
1999
+ return;
2000
+ }
2001
+ if (isWfManagedMode(currentMode) && normalizedMode !== currentMode) {
2002
+ finish({ ok: false, command: 'record', errors: [`WF-managed task "${taskId}" cannot downgrade or exit WF lifecycle; close it and create a new task.`], warnings: [] }, 1);
2003
+ return;
2004
+ }
2005
+ }
2006
+ if (isWfManagedMode(currentMode) && currentStatus === 'closed' && statusRaw) {
2007
+ const requestedStatus = normalizeStatus(statusRaw);
2008
+ if (requestedStatus && requestedStatus !== 'closed') {
2009
+ finish({ ok: false, command: 'record', errors: [`Closed WF-managed task "${taskId}" cannot be reopened; create a new task capsule.`], warnings: [] }, 1);
2010
+ return;
2011
+ }
2012
+ }
1505
2013
  if (statusRaw) {
1506
2014
  const ns = normalizeStatus(statusRaw);
1507
2015
  if (!ns) {
@@ -1510,18 +2018,30 @@ function runRecord() {
1510
2018
  }
1511
2019
  updated.status = ns;
1512
2020
  }
1513
- if (modeRaw) {
1514
- const normalizedMode = normalizeMode(modeRaw);
1515
- if (!normalizedMode) {
1516
- finish({ ok: false, command: 'record', errors: [`Invalid mode "${modeRaw}". Valid: ${[...VALID_MODES].join(', ')}`], warnings: [] }, 1);
2021
+ if (phaseRaw) {
2022
+ const normalizedPhase = normalizePhase(phaseRaw);
2023
+ if (!normalizedPhase) {
2024
+ finish({ ok: false, command: 'record', errors: [`Invalid phase "${phaseRaw}". Valid: ${[...VALID_PHASES].join(', ')}`], warnings: [] }, 1);
1517
2025
  return;
1518
2026
  }
1519
- updated.mode = normalizedMode;
2027
+ updated.phase = normalizedPhase;
2028
+ }
2029
+ if (modeRaw) updated.mode = normalizeMode(modeRaw);
2030
+ const project = String(projectRaw || groupRaw || '').trim();
2031
+ if (project) {
2032
+ updated.project = project;
2033
+ updated.group = project;
1520
2034
  }
2035
+ if (tagsRaw) updated.tags = normalizeTags(tagsRaw);
1521
2036
  if (text) updated.nextAction = text;
1522
2037
 
2038
+ if (updated.status === 'active' && !updated.startedAt) updated.startedAt = now;
2039
+ if (updated.status === 'closed' && !updated.closedAt) updated.closedAt = now;
2040
+ if (updated.status !== 'closed') updated.closedAt = null;
2041
+
1523
2042
  if (actuallyApply) {
1524
2043
  writeJsonAtomic(existing.path, updated);
2044
+ syncTaskViewsForState(taskId, updated);
1525
2045
  }
1526
2046
 
1527
2047
  finish({
@@ -1539,14 +2059,31 @@ function runRecord() {
1539
2059
 
1540
2060
  function runOpen() {
1541
2061
  const { rootProgress, tasks } = collectTasks();
2062
+ const projectFilter = flagValue('--project') || flagValue('--group');
2063
+ const statusFilter = flagValue('--status');
2064
+ const normalizedStatusFilter = statusFilter ? normalizeStatus(statusFilter) : null;
2065
+ if (statusFilter && !normalizedStatusFilter) {
2066
+ finish({
2067
+ ok: false,
2068
+ command: 'open',
2069
+ errors: [`Invalid status filter "${statusFilter}". Valid: active, blocked (legacy aliases accepted)`],
2070
+ warnings: [],
2071
+ tasks: [],
2072
+ }, 1);
2073
+ return;
2074
+ }
1542
2075
 
1543
2076
  const openTasks = tasks.filter(task => {
1544
2077
  const status = normalizeStatus(task.state?.status) || task.status;
1545
- return OPEN_TASK_STATUSES.has(status);
2078
+ if (!OPEN_TASK_STATUSES.has(status)) return false;
2079
+ if (projectFilter && taskProjectFor(task.state) !== projectFilter) return false;
2080
+ if (normalizedStatusFilter && status !== normalizedStatusFilter) return false;
2081
+ return true;
1546
2082
  }).map(task => {
1547
2083
  const state = task.state || {};
1548
2084
  const links = state.links || {};
1549
2085
  const status = normalizeStatus(state.status) || task.status;
2086
+ const lifecycle = lifecycleForState(state, status);
1550
2087
  const dependsOn = Array.isArray(links.dependsOn) ? links.dependsOn : [];
1551
2088
  const openDepTasks = dependsOn.filter(depId => {
1552
2089
  const depTask = tasks.find(t => t.id === depId);
@@ -1556,8 +2093,17 @@ function runOpen() {
1556
2093
  });
1557
2094
  return {
1558
2095
  id: task.id,
2096
+ project: taskProjectFor(state),
2097
+ group: taskProjectFor(state),
1559
2098
  status,
2099
+ mode: lifecycle.mode,
2100
+ wfManaged: lifecycle.wfManaged,
2101
+ resumeRequired: lifecycle.resumeRequired,
1560
2102
  phase: normalizePhase(state.phase) || task.phase,
2103
+ createdAt: state.createdAt || null,
2104
+ startedAt: state.startedAt || null,
2105
+ updatedAt: state.updatedAt || null,
2106
+ closedAt: state.closedAt || null,
1561
2107
  dependsOn,
1562
2108
  blocks: Array.isArray(links.blocks) ? links.blocks : [],
1563
2109
  blockedByOpenDeps: openDepTasks,
@@ -1570,6 +2116,8 @@ function runOpen() {
1570
2116
  finish({
1571
2117
  ok: true,
1572
2118
  command: 'open',
2119
+ projectFilter: projectFilter || null,
2120
+ statusFilter: normalizedStatusFilter,
1573
2121
  taskCount: openTasks.length,
1574
2122
  tasks: openTasks,
1575
2123
  errors: [],
@@ -1781,32 +2329,48 @@ function runHistoryDelete() {
1781
2329
  }
1782
2330
  }
1783
2331
 
2332
+ export {
2333
+ readTaskState,
2334
+ writeTaskStateAtomic,
2335
+ taskPathsForProject,
2336
+ writeTextAtomic,
2337
+ writeJsonAtomic,
2338
+ };
2339
+
1784
2340
  // ---- dispatch ----
1785
- if (command === 'help' || hasFlag('--help') || hasFlag('-h')) usage();
1786
- if (command === 'list') runList();
1787
- if (command === 'validate') runValidate();
1788
- if (command === 'reconcile') runReconcile();
1789
- if (command === 'set-active') runSetActive();
1790
- if (command === 'transition') runTransition();
1791
- if (command === 'archive') runArchive();
1792
- if (command === 'record') runRecord();
1793
- if (command === 'open') runOpen();
1794
- if (command === 'history') {
1795
- const sub = args[1];
1796
- if (sub === 'list') runHistoryList();
1797
- else if (sub === 'search') runHistorySearch();
1798
- else if (sub === 'load') runHistoryLoad();
1799
- else if (sub === 'delete') runHistoryDelete();
1800
- else finish({
1801
- ok: false, command: 'history',
1802
- errors: [`Unknown history subcommand "${sub}". Try: list, search <kw>, load <id>, delete <id>`],
2341
+ // Keep imports side-effect free so runtime workers can reuse the canonical
2342
+ // reader/writer without accidentally running the task-state CLI.
2343
+ const isMain = process.argv[1]
2344
+ && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
2345
+ if (isMain) {
2346
+ if (command === 'help' || hasFlag('--help') || hasFlag('-h')) usage();
2347
+ if (command === 'list') runList();
2348
+ if (command === 'index') runIndex();
2349
+ if (command === 'query') runQuery();
2350
+ if (command === 'validate') runValidate();
2351
+ if (command === 'reconcile') runReconcile();
2352
+ if (command === 'set-active') runSetActive();
2353
+ if (command === 'transition') runTransition();
2354
+ if (command === 'archive') runArchive();
2355
+ if (command === 'record') runRecord();
2356
+ if (command === 'open') runOpen();
2357
+ if (command === 'history') {
2358
+ const sub = args[1];
2359
+ if (sub === 'list') runHistoryList();
2360
+ else if (sub === 'search') runHistorySearch();
2361
+ else if (sub === 'load') runHistoryLoad();
2362
+ else if (sub === 'delete') runHistoryDelete();
2363
+ else finish({
2364
+ ok: false, command: 'history',
2365
+ errors: [`Unknown history subcommand "${sub}". Try: list, search <kw>, load <id>, delete <id>`],
2366
+ warnings: [],
2367
+ }, 1);
2368
+ }
2369
+
2370
+ finish({
2371
+ ok: false,
2372
+ command,
2373
+ errors: [`Unknown command "${command}". Run node Harness/scripts/task-state.mjs --help.`],
1803
2374
  warnings: [],
1804
2375
  }, 1);
1805
2376
  }
1806
-
1807
- finish({
1808
- ok: false,
1809
- command,
1810
- errors: [`Unknown command "${command}". Run node Harness/scripts/task-state.mjs --help.`],
1811
- warnings: [],
1812
- }, 1);