@repo-os/repoos 0.5.68 → 0.5.69

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 (222) hide show
  1. package/dist/.build-info.json +2 -2
  2. package/dist/.build-stamp.json +1 -1
  3. package/dist/cli/api-route-parity.js +4 -0
  4. package/dist/cli/help.js +6 -0
  5. package/dist/cli/index.js +7 -1
  6. package/dist/commands/control-api.d.ts +1 -0
  7. package/dist/commands/control-api.js +98 -2
  8. package/dist/commands/init.js +7 -2
  9. package/dist/commands/ui-contrast-audit.js +55 -2
  10. package/dist/core/config.d.ts +1 -0
  11. package/dist/core/config.js +40 -2
  12. package/dist/core/dev-targets.d.ts +25 -0
  13. package/dist/core/dev-targets.js +155 -0
  14. package/dist/core/git.d.ts +18 -0
  15. package/dist/core/git.js +10 -1
  16. package/dist/core/models.js +7 -1
  17. package/dist/core/shot-page.d.ts +9 -0
  18. package/dist/core/shot-page.js +76 -1
  19. package/dist/core/types.d.ts +49 -0
  20. package/dist/server/agents.d.ts +10 -0
  21. package/dist/server/agents.js +117 -5
  22. package/dist/server/conflict-resolution.d.ts +26 -6
  23. package/dist/server/conflict-resolution.js +71 -3
  24. package/dist/server/dev.d.ts +131 -0
  25. package/dist/server/dev.js +621 -0
  26. package/dist/server/integration-orchestrator.js +75 -13
  27. package/dist/server/merge-conflict.d.ts +9 -2
  28. package/dist/server/merge-conflict.js +10 -4
  29. package/dist/server/review.d.ts +1 -1
  30. package/dist/server/review.js +20 -10
  31. package/dist/server/routes/config.js +117 -1
  32. package/dist/server/routes/dev.d.ts +19 -0
  33. package/dist/server/routes/dev.js +60 -0
  34. package/dist/server/routes/index.d.ts +1 -0
  35. package/dist/server/routes/index.js +1 -0
  36. package/dist/server/routes/tasks.js +49 -10
  37. package/dist/server/routes/types.d.ts +6 -0
  38. package/dist/server/server.js +21 -0
  39. package/dist/server/shot-capture.js +4 -0
  40. package/dist/server/ui-handoff-gate.d.ts +27 -0
  41. package/dist/server/ui-handoff-gate.js +46 -5
  42. package/dist/ui/assets/{AgentModelModal-BU_T-d03.js → AgentModelModal-C8GhSyIa.js} +1 -1
  43. package/dist/ui/assets/AgentsView-ChOMboOE.js +1 -0
  44. package/dist/ui/assets/ChecksView-CU7ikl8l.js +0 -0
  45. package/dist/ui/assets/ConfirmDialog-CcPmuMHQ.js +1 -0
  46. package/dist/ui/assets/ContextView-Dx2siGFv.js +4 -0
  47. package/dist/ui/assets/DashboardView-DP81OKcA.js +1 -0
  48. package/dist/ui/assets/{DeleteConfirmDialog-B7_fR8N6.js → DeleteConfirmDialog-BLHJs3Sg.js} +1 -1
  49. package/dist/ui/assets/{DeploymentsView-oXkw2BSL.js → DeploymentsView-CJBx2mcp.js} +1 -1
  50. package/dist/ui/assets/DevView-CoL2KIaZ.css +1 -0
  51. package/dist/ui/assets/DevView-omNNz-ZQ.js +2 -0
  52. package/dist/ui/assets/{DiffView-DLFmq-vx.js → DiffView-CKMjBvIa.js} +1 -1
  53. package/dist/ui/assets/InputsView-B79I4jwu.js +1 -0
  54. package/dist/ui/assets/LoginView-B2WAQcq0.js +1 -0
  55. package/dist/ui/assets/ReleasesView-D3qIgMaL.js +5 -0
  56. package/dist/ui/assets/SearchOverlay-0rM6zfNm.js +1 -0
  57. package/dist/ui/assets/SettingsView-CEWHEE4r.css +1 -0
  58. package/dist/ui/assets/SettingsView-gqVC3E7s.js +4 -0
  59. package/dist/ui/assets/StoriesView-BazXnyDz.js +1 -0
  60. package/dist/ui/assets/{VoiceDictate-CMtXjAXc.js → VoiceDictate-JU6yFzDl.js} +2 -2
  61. package/dist/ui/assets/{WorkView-BhDux3s_.css → WorkView-B4O1dgd8.css} +1 -1
  62. package/dist/ui/assets/WorkView-qq7xNw0b.js +2 -0
  63. package/dist/ui/assets/abnfDiagram-VCTEODGH-KzOgBc6g.js +1 -0
  64. package/dist/ui/assets/architecture-7GRP2DOG-BHZiePHg.js +1 -0
  65. package/dist/ui/assets/architectureDiagram-5GKGNRK7-CP4J_21k.js +36 -0
  66. package/dist/ui/assets/auth-DOfKWRWn.js +1 -0
  67. package/dist/ui/assets/{blockDiagram-I7D4REHJ-CSam08UZ.js → blockDiagram-I7D4REHJ-B5q-JPzd.js} +1 -1
  68. package/dist/ui/assets/{button-uIODk23A.js → button-Cg7Fwvfz.js} +1 -1
  69. package/dist/ui/assets/{c4Diagram-7LVT6UL2-D6YRpohJ.js → c4Diagram-7LVT6UL2-C8gg_KAI.js} +1 -1
  70. package/dist/ui/assets/card-h3OmE2_z.js +1 -0
  71. package/dist/ui/assets/channel-CKK9ZV4r.js +1 -0
  72. package/dist/ui/assets/{checkbox-EUK8itx1.js → checkbox-CJdatFyS.js} +1 -1
  73. package/dist/ui/assets/chunk-2E4U76K2-Bgho8hyQ.js +2 -0
  74. package/dist/ui/assets/chunk-2ZTRR5NV-Cr-_ry_6.js +2 -0
  75. package/dist/ui/assets/chunk-3Z5EZCMW-BBL48ahF.js +1 -0
  76. package/dist/ui/assets/{chunk-4HAMMTFA-BnbHtCpa.js → chunk-4HAMMTFA-DQYpRm2O.js} +1 -1
  77. package/dist/ui/assets/chunk-5V3GS4D5-B6UpbGCn.js +1 -0
  78. package/dist/ui/assets/chunk-6AZGARVD-CYyHPsEW.js +1 -0
  79. package/dist/ui/assets/chunk-6EIED4P4-DLcrKSZa.js +1 -0
  80. package/dist/ui/assets/chunk-6K3QC6MW-8lwas69x.js +1 -0
  81. package/dist/ui/assets/chunk-6TQVIW2G-kn-138pS.js +1 -0
  82. package/dist/ui/assets/chunk-747NJXEK-ZQ1az0_F.js +2 -0
  83. package/dist/ui/assets/{chunk-75Z2AOVW-CYy9Me4H.js → chunk-75Z2AOVW-DlvTKtcP.js} +1 -1
  84. package/dist/ui/assets/chunk-CLGD4ZFX-xhgIfmhG.js +1 -0
  85. package/dist/ui/assets/{chunk-DU6HZSFF-JVoEkt2i.js → chunk-DU6HZSFF-Dkcc-d_4.js} +1 -1
  86. package/dist/ui/assets/{chunk-F27PBJKO-g4ruqsFu.js → chunk-F27PBJKO-gD-r9Rww.js} +1 -1
  87. package/dist/ui/assets/{chunk-GMAD6QVW-BfX4kTq8.js → chunk-GMAD6QVW-rvBxuygZ.js} +1 -1
  88. package/dist/ui/assets/{chunk-GVQU2GXP-C9yJ2E9t.js → chunk-GVQU2GXP-Cq3-rF-u.js} +1 -1
  89. package/dist/ui/assets/chunk-I5DQTOEV-Cux5OBYe.js +1 -0
  90. package/dist/ui/assets/chunk-ICYGCRZG-BSFpafu0.js +1 -0
  91. package/dist/ui/assets/chunk-IH6LHLGP-D3gp9iPn.js +1 -0
  92. package/dist/ui/assets/{chunk-IMKFNOWR-CbzZAPQV.js → chunk-IMKFNOWR-BHREb6T1.js} +1 -1
  93. package/dist/ui/assets/chunk-KI3K4JFJ-bgHYJH0f.js +1 -0
  94. package/dist/ui/assets/{chunk-L3NEJ4N5-Cjbclivl.js → chunk-L3NEJ4N5-D-R9OvAs.js} +1 -1
  95. package/dist/ui/assets/chunk-LNGE3PJU-p78rraeW.js +34 -0
  96. package/dist/ui/assets/{chunk-OSK3NFVY-YA4gtld_.js → chunk-OSK3NFVY-D0WDbtxw.js} +1 -1
  97. package/dist/ui/assets/chunk-OUJLGHUK-CYjUPErt.js +2 -0
  98. package/dist/ui/assets/{chunk-P2QGCYS3-Cj7e9ARN.js → chunk-P2QGCYS3-DLFLbH4p.js} +1 -1
  99. package/dist/ui/assets/{chunk-POPQ4Y6H-fwh_frIf.js → chunk-POPQ4Y6H-Cw6X8QGG.js} +1 -1
  100. package/dist/ui/assets/{chunk-PWAF6VOD-BUHsDYf7.js → chunk-PWAF6VOD-Ds6RWXzu.js} +1 -1
  101. package/dist/ui/assets/{chunk-SHT3W25Y-BsPOUuAn.js → chunk-SHT3W25Y-DvJuv0Ft.js} +3 -3
  102. package/dist/ui/assets/{chunk-SVP7TREG-CwMeLZi5.js → chunk-SVP7TREG-DMasMVeC.js} +1 -1
  103. package/dist/ui/assets/{chunk-TICWLB2K-B9QsE3VX.js → chunk-TICWLB2K-yWH6tp94.js} +1 -1
  104. package/dist/ui/assets/chunk-TLUHSLCS-CzTkKr54.js +2 -0
  105. package/dist/ui/assets/chunk-UY3FDG6J-XZwQOzbF.js +1 -0
  106. package/dist/ui/assets/chunk-XHIXRSVI-Bu6LAW6p.js +1 -0
  107. package/dist/ui/assets/classDiagram-ZZMXUADV-AoAfBymA.js +1 -0
  108. package/dist/ui/assets/classDiagram-v2-VYDZK3BY-AoAfBymA.js +1 -0
  109. package/dist/ui/assets/cynefin-OW5HDTMX-Bxpgy9Q-.js +1 -0
  110. package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-DAETHLCh.js +62 -0
  111. package/dist/ui/assets/{dagre-GXQ25YYZ-CJefo5Cd.js → dagre-GXQ25YYZ-BoTD9ES5.js} +2 -2
  112. package/dist/ui/assets/{diagram-S7CK7UJ4-BshX_cqp.js → diagram-S7CK7UJ4-etZnEmAz.js} +3 -3
  113. package/dist/ui/assets/diagram-UQ7AKVKN-CywmEzAf.js +41 -0
  114. package/dist/ui/assets/diagram-VSXAHHWV-Dp_aDmgC.js +3 -0
  115. package/dist/ui/assets/diagram-VX7I27RA-Bxt7i5Mf.js +24 -0
  116. package/dist/ui/assets/diagram-Z3DM3KII-BReedDkK.js +24 -0
  117. package/dist/ui/assets/docs-CypCpmgt.js +1 -0
  118. package/dist/ui/assets/ebnfDiagram-PWID7BFC-Dz7S0T6B.js +1 -0
  119. package/dist/ui/assets/{erDiagram-RLTQ6QDP-DKrnpHSN.js → erDiagram-RLTQ6QDP-C2OhjIoP.js} +1 -1
  120. package/dist/ui/assets/eventmodeling-NTZA5JFV-CKBX3xli.js +1 -0
  121. package/dist/ui/assets/flowDiagram-HODETNUW-DWZFA4j8.js +1 -0
  122. package/dist/ui/assets/{ganttDiagram-EL5Y4UJY-DzU13pix.js → ganttDiagram-EL5Y4UJY-X63oIy-b.js} +1 -1
  123. package/dist/ui/assets/gitGraph-4MIJSDKK-DBRsV9Fh.js +1 -0
  124. package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-CjRtFyZs.js +106 -0
  125. package/dist/ui/assets/index-BBJ0SQAG.css +1 -0
  126. package/dist/ui/assets/index-DpAh1097.js +57 -0
  127. package/dist/ui/assets/info-A6RAGUB7-AZ8VcEaY.js +1 -0
  128. package/dist/ui/assets/infoDiagram-27XIBGKW-D_WjlHCS.js +2 -0
  129. package/dist/ui/assets/input-Cg7deJC2.js +1 -0
  130. package/dist/ui/assets/{ishikawaDiagram-5VMMS53U-Cdigo_vG.js → ishikawaDiagram-5VMMS53U-CcSnDibc.js} +2 -2
  131. package/dist/ui/assets/{journeyDiagram-3NMN7TZE-DmZ4MIYL.js → journeyDiagram-3NMN7TZE-C_cOiv3U.js} +1 -1
  132. package/dist/ui/assets/{kanban-definition-UXKFOSKX-2sVFLArm.js → kanban-definition-UXKFOSKX-BE6bTzJ_.js} +3 -3
  133. package/dist/ui/assets/{line-C77c3pt1.js → line-B0YzR-ai.js} +1 -1
  134. package/dist/ui/assets/mermaid-parser.core-D-C4q-gD.js +4 -0
  135. package/dist/ui/assets/mermaid.core-BinXQHOy.js +11 -0
  136. package/dist/ui/assets/{mindmap-definition-YA3MSWOX-BsOAbuQD.js → mindmap-definition-YA3MSWOX-H_1t8eO8.js} +1 -1
  137. package/dist/ui/assets/packet-AYTQ26CC-Bdg2essy.js +1 -0
  138. package/dist/ui/assets/pegDiagram-XKGWAZYB-DEzpGr4J.js +1 -0
  139. package/dist/ui/assets/pie-WAS4IAKB-D5QrUC2g.js +1 -0
  140. package/dist/ui/assets/pieDiagram-E7YTZNPT-DzBWBOML.js +39 -0
  141. package/dist/ui/assets/{quadrantDiagram-AXDQQJYC-BFIIZWqg.js → quadrantDiagram-AXDQQJYC-BJNb82FM.js} +1 -1
  142. package/dist/ui/assets/radar-RG4KPBEZ-BuYYKOJl.js +1 -0
  143. package/dist/ui/assets/railroad-74A4TZTK-Nkuw4fzg.js +1 -0
  144. package/dist/ui/assets/railroad-abnf-HS5TGJTU-gAPZoBfp.js +1 -0
  145. package/dist/ui/assets/railroad-ebnf-LZEXJU2U-DvwkawHG.js +1 -0
  146. package/dist/ui/assets/railroad-peg-WCYAUIDC-CrrHZFIz.js +1 -0
  147. package/dist/ui/assets/railroadDiagram-O6MQD6OU-dHphbrs7.js +1 -0
  148. package/dist/ui/assets/{requirementDiagram-BXWQKSXE-DzN62GBp.js → requirementDiagram-BXWQKSXE-BpZeR6MN.js} +1 -1
  149. package/dist/ui/assets/{sankeyDiagram-P5KCCOFB-Twa9FjBx.js → sankeyDiagram-P5KCCOFB-pfWwS_Ol.js} +1 -1
  150. package/dist/ui/assets/screenshot-viewer-Zxr6licW.js +1 -0
  151. package/dist/ui/assets/{sequenceDiagram-WJ2MYXX4-DGUe1AcR.js → sequenceDiagram-WJ2MYXX4-jX0ZWILZ.js} +1 -1
  152. package/dist/ui/assets/stateDiagram-D77RDMKH-cx9TocBZ.js +1 -0
  153. package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-C8aOH2zy.js +1 -0
  154. package/dist/ui/assets/swimlanes-42K2YHIH-D0HO4IaA.js +1 -0
  155. package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-TUNEXRA4.js +8 -0
  156. package/dist/ui/assets/switch-BYQwbwcZ.js +1 -0
  157. package/dist/ui/assets/{timeline-definition-24CTP7MA-D5A6I-WY.js → timeline-definition-24CTP7MA-CU20d-G7.js} +1 -1
  158. package/dist/ui/assets/{title-ClcAtTzM.js → title-CKCTFTPg.js} +1 -1
  159. package/dist/ui/assets/treeView-Q6P3EWNA-DVwK8Kqr.js +1 -0
  160. package/dist/ui/assets/treemap-WGGIJYW6-dx7qI3mD.js +1 -0
  161. package/dist/ui/assets/value-BsoQhhdg.js +1 -0
  162. package/dist/ui/assets/vendor-ui-D3ucVR3C.js +3 -0
  163. package/dist/ui/assets/{vennDiagram-4TSXK5OY-CbrfLPNL.js → vennDiagram-4TSXK5OY-Du2dqf4k.js} +2 -2
  164. package/dist/ui/assets/viewport-BVLcUJ9I.js +1 -0
  165. package/dist/ui/assets/wardley-WFR3VGLG-Dexzwvt6.js +1 -0
  166. package/dist/ui/assets/{wardleyDiagram-VM6X3IG4-cG-Vx9Gs.js → wardleyDiagram-VM6X3IG4-CsiwW_49.js} +3 -3
  167. package/dist/ui/assets/{xychartDiagram-S5SC5T6Z-DXzo3wNb.js → xychartDiagram-S5SC5T6Z-8lEaXvDi.js} +3 -3
  168. package/dist/ui/index.html +16 -7
  169. package/dist/ui/sw.js +2 -2
  170. package/package.json +1 -1
  171. package/dist/ui/assets/AgentsView-B2AXFnUJ.js +0 -1
  172. package/dist/ui/assets/ChecksView-RAHI2BB3.js +0 -0
  173. package/dist/ui/assets/ContextView-DAxjJdK4.js +0 -4
  174. package/dist/ui/assets/DashboardView-hZr-QF_E.js +0 -1
  175. package/dist/ui/assets/InputsView-CE9ZOYMD.js +0 -1
  176. package/dist/ui/assets/LoginView-dfYNibLg.js +0 -1
  177. package/dist/ui/assets/ReleasesView-Cq8kGWZq.js +0 -5
  178. package/dist/ui/assets/SettingsView-BlDj5TRt.css +0 -1
  179. package/dist/ui/assets/SettingsView-BzZQH1hi.js +0 -4
  180. package/dist/ui/assets/StoriesView-3P5nmoFU.js +0 -1
  181. package/dist/ui/assets/WorkView-P_JFoClW.js +0 -2
  182. package/dist/ui/assets/abnfDiagram-VCTEODGH-DB7IT3Oz.js +0 -1
  183. package/dist/ui/assets/architecture-7GRP2DOG-D0IdEE2Q.js +0 -1
  184. package/dist/ui/assets/architectureDiagram-5GKGNRK7-BKe4O-fa.js +0 -36
  185. package/dist/ui/assets/channel-C0fJQ3Uq.js +0 -1
  186. package/dist/ui/assets/classDiagram-ZZMXUADV-DdZ1tR-T.js +0 -1
  187. package/dist/ui/assets/classDiagram-v2-VYDZK3BY-DdZ1tR-T.js +0 -1
  188. package/dist/ui/assets/cynefin-OW5HDTMX-DaO-Ofss.js +0 -1
  189. package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-DbkXxB0L.js +0 -62
  190. package/dist/ui/assets/diagram-UQ7AKVKN-xP1_CqiO.js +0 -41
  191. package/dist/ui/assets/diagram-VSXAHHWV-BygVWS22.js +0 -3
  192. package/dist/ui/assets/diagram-VX7I27RA-CH--xipF.js +0 -24
  193. package/dist/ui/assets/diagram-Z3DM3KII-DJrYKohr.js +0 -24
  194. package/dist/ui/assets/ebnfDiagram-PWID7BFC-BkQ5ACZ8.js +0 -1
  195. package/dist/ui/assets/eventmodeling-NTZA5JFV-DHsluImP.js +0 -1
  196. package/dist/ui/assets/flowDiagram-HODETNUW-CpTJSjiU.js +0 -1
  197. package/dist/ui/assets/gitGraph-4MIJSDKK-C2NqgafQ.js +0 -1
  198. package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-CD7Lar3K.js +0 -106
  199. package/dist/ui/assets/index-C3vwYKvY.js +0 -55
  200. package/dist/ui/assets/index-CjZQAL44.css +0 -1
  201. package/dist/ui/assets/info-A6RAGUB7-Cl6FFCuQ.js +0 -1
  202. package/dist/ui/assets/infoDiagram-27XIBGKW-Co-YjPmk.js +0 -2
  203. package/dist/ui/assets/mermaid-parser.core-xjtJ3pXb.js +0 -7
  204. package/dist/ui/assets/mermaid.core-CMyeUwV9.js +0 -44
  205. package/dist/ui/assets/packet-AYTQ26CC-BDcVOEez.js +0 -1
  206. package/dist/ui/assets/pegDiagram-XKGWAZYB-C1hq_Ygi.js +0 -1
  207. package/dist/ui/assets/pie-WAS4IAKB-CXk97zCx.js +0 -1
  208. package/dist/ui/assets/pieDiagram-E7YTZNPT-CG9wAg_-.js +0 -39
  209. package/dist/ui/assets/radar-RG4KPBEZ-D9HuDG1X.js +0 -1
  210. package/dist/ui/assets/railroad-74A4TZTK-kCOU5-RT.js +0 -1
  211. package/dist/ui/assets/railroad-abnf-HS5TGJTU-CtNhQpzv.js +0 -1
  212. package/dist/ui/assets/railroad-ebnf-LZEXJU2U-5rUlNGX_.js +0 -1
  213. package/dist/ui/assets/railroad-peg-WCYAUIDC-DOPhbUMW.js +0 -1
  214. package/dist/ui/assets/railroadDiagram-O6MQD6OU-RK45rPYx.js +0 -1
  215. package/dist/ui/assets/stateDiagram-D77RDMKH-ByryPTRm.js +0 -1
  216. package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-xS7RBCS4.js +0 -1
  217. package/dist/ui/assets/swimlanes-42K2YHIH-CnU3MB_t.js +0 -1
  218. package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-Dd7VwNJ9.js +0 -8
  219. package/dist/ui/assets/treeView-Q6P3EWNA-BzAgp6hO.js +0 -1
  220. package/dist/ui/assets/treemap-WGGIJYW6-BOnPgUXz.js +0 -1
  221. package/dist/ui/assets/vendor-ui-DsOjPSxk.js +0 -3
  222. package/dist/ui/assets/wardley-WFR3VGLG-BzZdYjpA.js +0 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "hash": "a50a11bbcdcbcbedd23ed2410aa902333da0a7c6a5f48a204f67758d061c71d1",
3
- "version": "0.5.68",
2
+ "hash": "b009708e237c666064aee174b838afdb790721e2fdef67d53c2317781afd5073",
3
+ "version": "0.5.69",
4
4
  "devUi": false
5
5
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "generatedAt": "2026-10-09T03:35:17.984Z"
2
+ "generatedAt": "2026-10-11T01:57:56.517Z"
3
3
  }
@@ -12,6 +12,10 @@ const CONTROL_API_MATCHERS = [
12
12
  re("POST", /^\/api\/tasks\/([^/]+)\/(start|pause|message|done)$/.source),
13
13
  re("POST", /^\/api\/tasks\/([^/]+)\/preview$/.source),
14
14
  re("POST", /^\/api\/tasks\/([^/]+)\/preview\/stop$/.source),
15
+ // `repoos dev …` (#0747): list/status/get and start/stop/restart.
16
+ exact("GET", "/api/dev/targets"),
17
+ re("GET", /^\/api\/dev\/targets\/([^/]+)$/.source),
18
+ re("POST", /^\/api\/dev\/targets\/([^/]+)\/(start|stop|restart)$/.source),
15
19
  exact("GET", "/api/config"),
16
20
  exact("PATCH", "/api/config"),
17
21
  exact("GET", "/api/remote-validation/status"),
package/dist/cli/help.js CHANGED
@@ -116,6 +116,12 @@ const GROUPS = [
116
116
  desc: "Start or stop the task preview (POST /api/tasks/:id/preview)",
117
117
  flags: "--stop · --json · --port",
118
118
  },
119
+ {
120
+ name: "dev",
121
+ usage: "dev [list] · dev <name> start|stop|restart|status|logs [--json] [--port N]",
122
+ desc: "Start, stop and inspect a declared dev target (GET/POST /api/dev/targets)",
123
+ flags: "--json · --port",
124
+ },
119
125
  {
120
126
  name: "config",
121
127
  usage: "config get|set <key> [value] [--json] [--port N]",
package/dist/cli/index.js CHANGED
@@ -21,7 +21,7 @@ import { cmdDoctor } from "../commands/doctor.js";
21
21
  import { cmdCertify } from "../commands/certify.js";
22
22
  import { cmdSupport } from "../commands/support.js";
23
23
  import { cmdService } from "../commands/service.js";
24
- import { cmdAgentsRunning, cmdConfig, cmdDone, cmdMessage, cmdOverride, cmdPause, cmdPreview, cmdReview, cmdRunners, cmdStart, cmdStats, cmdDecisions, } from "../commands/control-api.js";
24
+ import { cmdAgentsRunning, cmdConfig, cmdDone, cmdMessage, cmdOverride, cmdPause, cmdPreview, cmdDev, cmdReview, cmdRunners, cmdStart, cmdStats, cmdDecisions, } from "../commands/control-api.js";
25
25
  import { cmdWatch } from "../commands/watch.js";
26
26
  import { cmdDriver } from "../commands/driver.js";
27
27
  import { checkBuild } from "../core/build.js";
@@ -242,6 +242,12 @@ function main() {
242
242
  process.exitCode = code;
243
243
  });
244
244
  break;
245
+ case "dev":
246
+ void cmdDev(rest).then((code) => {
247
+ if (code !== 0)
248
+ process.exitCode = code;
249
+ });
250
+ break;
245
251
  case "config":
246
252
  void cmdConfig(rest).then((code) => {
247
253
  if (code !== 0)
@@ -5,6 +5,7 @@ export declare function cmdReview(args: string[]): Promise<number>;
5
5
  export declare function cmdDone(args: string[]): Promise<number>;
6
6
  export declare function cmdOverride(args: string[]): Promise<number>;
7
7
  export declare function cmdPreview(args: string[]): Promise<number>;
8
+ export declare function cmdDev(args: string[]): Promise<number>;
8
9
  export declare function cmdConfig(args: string[]): Promise<number>;
9
10
  export declare function cmdRunners(args: string[]): Promise<number>;
10
11
  export declare function cmdAgentsRunning(args: string[]): Promise<number>;
@@ -142,8 +142,10 @@ export async function cmdMessage(args) {
142
142
  const data = await api.requestOk("POST", `/api/tasks/${id}/message`, { text });
143
143
  if (opts.json)
144
144
  printJson(data);
145
- else
146
- console.log(c.green(" ✓ ") + c.dim(`message sent to #${id}`));
145
+ else {
146
+ const status = data?.queued ? "queued" : "sent";
147
+ console.log(c.green(" ✓ ") + c.dim(`message ${status} to #${id}`));
148
+ }
147
149
  return 0;
148
150
  }
149
151
  catch (e) {
@@ -381,6 +383,100 @@ export async function cmdPreview(args) {
381
383
  return 1;
382
384
  }
383
385
  }
386
+ export async function cmdDev(args) {
387
+ const { rest, opts, error } = parseCommonFlags(args);
388
+ if (error) {
389
+ printErr(error);
390
+ return 1;
391
+ }
392
+ // `repoos dev list` · `repoos dev <name> start|stop|restart` · `repoos dev <name>`
393
+ // The command run is ALWAYS the one declared in repoos.toml, resolved by name.
394
+ const sub = rest[0];
395
+ if (!sub || sub === "list" || sub === "status") {
396
+ const api = new RepoOsApi({ port: opts.port });
397
+ try {
398
+ await ensureServer(api);
399
+ const data = (await api.requestOk("GET", "/api/dev/targets", undefined));
400
+ if (opts.json) {
401
+ printJson(data);
402
+ return 0;
403
+ }
404
+ if (data.errors?.length) {
405
+ for (const e of data.errors)
406
+ console.log(c.yellow(" ! ") + c.dim(e));
407
+ }
408
+ const targets = data.targets ?? [];
409
+ if (!targets.length) {
410
+ console.log(c.dim(" no dev targets declared in repoos.toml ([[dev.targets]])"));
411
+ return 0;
412
+ }
413
+ for (const t of targets) {
414
+ const dot = t.status === "running"
415
+ ? c.green("●")
416
+ : t.status === "failed"
417
+ ? c.red("●")
418
+ : t.status === "starting"
419
+ ? c.yellow("●")
420
+ : c.dim("○");
421
+ const target = t.url ?? t.artifactPath ?? "";
422
+ console.log(` ${dot} ${t.name} ${c.dim(`[${t.kind}] ${t.status}`)}${target ? c.dim(` ${target}`) : ""}`);
423
+ if (t.error)
424
+ console.log(c.red(` ${t.error}`));
425
+ }
426
+ return 0;
427
+ }
428
+ catch (e) {
429
+ printErr(e.message);
430
+ return 1;
431
+ }
432
+ }
433
+ const name = sub;
434
+ const action = rest[1] ?? "status";
435
+ const valid = ["start", "stop", "restart", "status", "logs"];
436
+ if (!valid.includes(action)) {
437
+ printErr("Usage: repoos dev [list] · repoos dev <name> start|stop|restart|status|logs [--json] [--port N]");
438
+ return 1;
439
+ }
440
+ const api = new RepoOsApi({ port: opts.port });
441
+ try {
442
+ await ensureServer(api);
443
+ const base = `/api/dev/targets/${encodeURIComponent(name)}`;
444
+ if (action === "start" || action === "stop" || action === "restart") {
445
+ const data = (await api.requestOk("POST", `${base}/${action}`, {}));
446
+ if (opts.json) {
447
+ printJson(data);
448
+ return 0;
449
+ }
450
+ if (data.target?.error) {
451
+ printErr(`dev target "${name}" ${data.target.status}: ${data.target.error}`);
452
+ return 1;
453
+ }
454
+ console.log(c.green(" ✓ ") +
455
+ c.dim(`dev target "${name}" ${data.target?.status ?? action}${data.target?.url ? ` ${data.target.url}` : ""}`));
456
+ return 0;
457
+ }
458
+ if (action === "status" || action === "logs") {
459
+ const data = (await api.requestOk("GET", base, undefined));
460
+ if (opts.json) {
461
+ printJson(data);
462
+ return 0;
463
+ }
464
+ console.log(c.dim(` ${name}: ${data.target?.status ?? "unknown"}`));
465
+ if (data.target?.error)
466
+ console.log(c.red(` ${data.target.error}`));
467
+ if (action === "logs") {
468
+ for (const line of data.target?.log ?? [])
469
+ console.log(` ${line}`);
470
+ }
471
+ return 0;
472
+ }
473
+ return 0;
474
+ }
475
+ catch (e) {
476
+ printErr(e.message);
477
+ return 1;
478
+ }
479
+ }
384
480
  export async function cmdConfig(args) {
385
481
  const { rest, opts, error } = parseCommonFlags(args);
386
482
  if (error) {
@@ -296,8 +296,13 @@ Problem / Desired UX / Acceptance criteria / Notes for AI is refused unless
296
296
  \`--force\` is passed.
297
297
 
298
298
  For tabbed views (e.g. Agents), prefer \`?tab=<id>\` routes
299
- (\`/agents?tab=detected\`) over click steps to reach the right tab, and always
300
- set \`highlight\` to the changed elements. A declared \`highlight\` or \`selector\`
299
+ (\`/agents?tab=detected\`) over click steps to reach the right tab; a task drawer
300
+ is reached the same way (\`/work?task=<id>&tab=changes\`), and always
301
+ set \`highlight\` to the changed elements. Reach a populated state with a
302
+ \`route\` or a named \`state\` fixture, never with \`steps\` that click a
303
+ placeholder selector (\`data-task-id "9999"\` and the like) — such a step times
304
+ out and the capture lands on a loading state instead of the content, which is
305
+ reported as a failed step, not a match. A declared \`highlight\` or \`selector\`
301
306
  that matches nothing at capture time records a visible warning — capture still
302
307
  succeeds. Declared shots default to the whole visible window (\`fullPage:
303
308
  false\`) with changed elements outlined via \`highlight\`; \`selector\` (element
@@ -676,10 +676,63 @@ function makeAuditFixture() {
676
676
  mime: "image/png",
677
677
  });
678
678
  }
679
+ // #0753: handoff verification evidence so the Changes tab renders the
680
+ // per-shot check/cross verdict icon and the "Handoff checked …" status line
681
+ // — both are real text/colour the audit must gate. One matching shot and
682
+ // one checking shot with a failed step, plus a console/overflow issue so
683
+ // the popover's content is present too.
684
+ const shots = localShotStore(config, "0001");
685
+ const first = shots.list()[0];
686
+ const second = shots.list()[1];
687
+ const savedUrls = [];
688
+ if (first)
689
+ savedUrls.push(first.url);
690
+ if (second)
691
+ savedUrls.push(second.url);
692
+ const cacheDir = join(root, config.cacheDir || ".repoos", "ui-verification");
693
+ mkdirSync(cacheDir, { recursive: true });
694
+ writeFileSync(join(cacheDir, "0001.json"), JSON.stringify({
695
+ at: new Date().toISOString(),
696
+ issues: [
697
+ { kind: "console", message: "TypeError: fixture is undefined" },
698
+ { kind: "overflow", message: "content wider than viewport", viewportWidth: 1280 },
699
+ ],
700
+ blankShots: [],
701
+ captures: savedUrls.length,
702
+ sourceIdentity: "fixture0001",
703
+ evidenceDir: "work/.attachments/0001/shots",
704
+ captureDetails: [
705
+ {
706
+ target: "default",
707
+ route: "/",
708
+ label: "Dashboard",
709
+ url: "http://127.0.0.1:0/",
710
+ finalRoute: "/",
711
+ routeMatched: true,
712
+ assertions: [],
713
+ assertionsPassed: 0,
714
+ assertionsChecked: 0,
715
+ ...(savedUrls[0] ? { shot: { name: "a", path: "", url: savedUrls[0] } } : {}),
716
+ },
717
+ {
718
+ target: "default",
719
+ route: "/",
720
+ label: "Auto shot",
721
+ url: "http://127.0.0.1:0/",
722
+ finalRoute: "/",
723
+ routeMatched: true,
724
+ targetWarnings: ['click [data-test-id="fullscreen"]: Timeout 5000ms exceeded'],
725
+ assertions: [],
726
+ assertionsPassed: 0,
727
+ assertionsChecked: 0,
728
+ ...(savedUrls[1] ? { shot: { name: "b", path: "", url: savedUrls[1] } } : {}),
729
+ },
730
+ ],
731
+ }));
679
732
  }
680
733
  catch {
681
- // The shot store is disk-only and cannot realistically fail here; if it
682
- // does, the Changes tab simply has no shots to audit.
734
+ // The shot/verification store is disk-only and cannot realistically fail
735
+ // here; if it does, the Changes tab simply has less to audit.
683
736
  }
684
737
  return root;
685
738
  }
@@ -1,4 +1,5 @@
1
1
  import type { Agent, BuiltInAgentConfig, DistributionConfig, PreviewConfig, RepoOSConfig, AreaConfig } from "./types.js";
2
+ export { parseDevTargets } from "./dev-targets.js";
2
3
  /** Default display labels for board columns, keyed by canonical status ID. */
3
4
  export declare const DEFAULT_COLUMN_LABELS: Record<string, string>;
4
5
  /** Coding agents an Agent can run under. */
@@ -8,9 +8,14 @@ import { cpus } from "node:os";
8
8
  import { basename, dirname, join, resolve } from "node:path";
9
9
  import { STATUSES } from "./types.js";
10
10
  import { parseCheckPlanConfig } from "./check-plan.js";
11
+ import { parseDevTargets } from "./dev-targets.js";
11
12
  import { parseTailscaleHosts } from "./remote-hosts.js";
12
13
  import { DEFAULT_STORAGE_PROVIDER_ID } from "./storage/registry.js";
13
14
  import { stripTomlComment, unquoteTomlString } from "./toml-line.js";
15
+ // `parseDevTargets` (#0747) lives in the node-free `core/dev-targets.ts` so the
16
+ // browser bundle can validate a target through the SAME function the server
17
+ // uses (#0749). Re-exported for the existing core/CLI importers.
18
+ export { parseDevTargets } from "./dev-targets.js";
14
19
  /** Default display labels for board columns, keyed by canonical status ID. */
15
20
  export const DEFAULT_COLUMN_LABELS = {
16
21
  draft: "Draft",
@@ -1106,8 +1111,16 @@ export function loadConfig(rootArg, options = {}) {
1106
1111
  enabled: typeof devInspectorEnabled === "boolean" ? devInspectorEnabled : true,
1107
1112
  editorCommand: typeof devInspectorEditor === "string" ? devInspectorEditor.trim() : "",
1108
1113
  };
1109
- cfg.dev = { inspector };
1110
- }
1114
+ cfg.dev = { ...cfg.dev, inspector };
1115
+ }
1116
+ // [[dev.targets]] (#0747) — the user-declared runnable dev targets. Bad
1117
+ // rows are recorded as validation errors on the config, not silently
1118
+ // dropped: the Dev page and CLI surface them so a typo is visible.
1119
+ const devTargets = parseDevTargets(parsed);
1120
+ if (devTargets.targets.length)
1121
+ cfg.dev = { ...cfg.dev, targets: devTargets.targets };
1122
+ if (devTargets.errors.length)
1123
+ cfg.devTargetErrors = devTargets.errors;
1111
1124
  if (typeof get("ntfyEnabled") === "boolean")
1112
1125
  cfg.ntfyEnabled = get("ntfyEnabled");
1113
1126
  if (typeof get("ntfyTopic") === "string")
@@ -2431,6 +2444,21 @@ export function getConfigSchema() {
2431
2444
  default: "",
2432
2445
  description: "Optional editor launcher, e.g. `zed {file}:{line}`. Placeholders `{file}` and `{line}`; omit `{line}` to open without a line number.",
2433
2446
  },
2447
+ {
2448
+ // The Settings control is a dedicated hand-rendered card on Advanced
2449
+ // (#0749), never an auto-rendered General row — like `areas`, this schema
2450
+ // entry exists so the search overlay and the "does this key have a UI
2451
+ // row?" resolver can find it.
2452
+ key: "dev.targets",
2453
+ label: "Dev targets",
2454
+ type: "array",
2455
+ tier: "live",
2456
+ restartRequired: false,
2457
+ default: [],
2458
+ description: "The things you run yourself while developing — a local HMR web server, a debug build, a native app process — " +
2459
+ "as opposed to task previews, which RepoOS manages for a task's worktree. Only commands " +
2460
+ "declared here can be started; edit them under Advanced → Dev targets.",
2461
+ },
2434
2462
  // [board.columns] — display-only label overrides (#0396).
2435
2463
  ...STATUSES.map((status) => ({
2436
2464
  key: `board.columns.${status}`,
@@ -2516,6 +2544,16 @@ export const SUPPORTED_TOML_KEYS = [
2516
2544
  "preview.services.cwd",
2517
2545
  "preview.services.readyPath",
2518
2546
  "preview.services.readyTimeoutMs",
2547
+ // Dev targets (#0747) — the user-runnable things the Dev page controls.
2548
+ // Declared as a repeatable `[[dev.targets]]` table; each row is one target.
2549
+ "dev.targets.name",
2550
+ "dev.targets.kind",
2551
+ "dev.targets.command",
2552
+ "dev.targets.url",
2553
+ "dev.targets.artifactPath",
2554
+ "dev.targets.label",
2555
+ "dev.targets.healthCheck.url",
2556
+ "dev.targets.healthCheck.file",
2519
2557
  // Preview-only overrides (#0464): a `[preview.<base path>]` table applied
2520
2558
  // only by the preview/UI-test preview runtime. auth.enabled is the headline
2521
2559
  // case; any supported base key can be overridden the same way.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `[[dev.targets]]` parsing (#0747, extracted node-free in #0749).
3
+ *
4
+ * The user-declared runnable dev targets are parsed here rather than in
5
+ * `core/config.ts` so the SAME validator runs in the browser bundle: the
6
+ * Settings control composes a row and validates it through `parseDevTargets`,
7
+ * exactly as the server re-validates the submitted list. `core/config.ts` has
8
+ * node imports (`node:fs`, `node:os`, `node:path`) and cannot be pulled into the
9
+ * Vite browser build, so this pure function lives on its own.
10
+ *
11
+ * Unlike `[preview]`, which resolves cleanly to "nothing" and drops bad rows, a
12
+ * dev target is something the user asked the server to run, so an unusable row
13
+ * is a loud validation error rather than a silent drop: a target with a missing
14
+ * name/command/kind or a conflicting field is reported with a clear message.
15
+ */
16
+ import type { DevTargetConfig } from "./types.js";
17
+ /**
18
+ * Parse the `[[dev.targets]]` array of tables — the user-declared runnable dev
19
+ * targets. Returns `{ targets, errors }`; `loadConfig` records the errors on
20
+ * the config so the API/UI can surface them.
21
+ */
22
+ export declare function parseDevTargets(parsed: Record<string, unknown>): {
23
+ targets: DevTargetConfig[];
24
+ errors: string[];
25
+ };
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Parse the flat TOML parser's leftover inline-table string for `healthCheck`
3
+ * (`'{ url = "..." }'` — `parseFlatToml` has no inline-table support, so the
4
+ * whole table stays a string). Returns the url/file fields, or null when the
5
+ * string is not a `{ … }` table at all, so the caller can report the right
6
+ * error. Quoted string values only, which is all a URL or a file path needs.
7
+ */
8
+ function parseInlineHealthCheck(raw) {
9
+ const s = raw.trim();
10
+ if (!s.startsWith("{") || !s.endsWith("}"))
11
+ return null;
12
+ const out = {};
13
+ for (const m of s.slice(1, -1).matchAll(/([A-Za-z0-9_]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)) {
14
+ const value = (m[2] ?? m[3] ?? "").trim();
15
+ if (!value)
16
+ continue;
17
+ if (m[1] === "url")
18
+ out.url = value;
19
+ else if (m[1] === "file")
20
+ out.file = value;
21
+ }
22
+ return out;
23
+ }
24
+ /**
25
+ * Parse the `[[dev.targets]]` array of tables — the user-declared runnable dev
26
+ * targets. Returns `{ targets, errors }`; `loadConfig` records the errors on
27
+ * the config so the API/UI can surface them.
28
+ */
29
+ export function parseDevTargets(parsed) {
30
+ const raw = parsed["dev.targets"];
31
+ const targets = [];
32
+ const errors = [];
33
+ if (raw === undefined)
34
+ return { targets, errors };
35
+ if (!Array.isArray(raw)) {
36
+ errors.push("[dev.targets] must be an array of tables");
37
+ return { targets, errors };
38
+ }
39
+ const seen = new Set();
40
+ for (const entry of raw) {
41
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
42
+ errors.push("[dev.targets] ignoring a non-table row");
43
+ continue;
44
+ }
45
+ const r = entry;
46
+ const name = typeof r.name === "string" ? r.name.trim() : "";
47
+ if (!name) {
48
+ errors.push("[dev.targets] a target is missing a name");
49
+ continue;
50
+ }
51
+ if (seen.has(name)) {
52
+ errors.push(`[dev.targets] duplicate target name "${name}"`);
53
+ continue;
54
+ }
55
+ const kindRaw = typeof r.kind === "string" ? r.kind.trim() : "";
56
+ if (kindRaw !== "server" && kindRaw !== "artifact" && kindRaw !== "process") {
57
+ errors.push(`[dev.targets] ${name}: kind must be "server", "artifact" or "process"${kindRaw ? ` (got "${kindRaw}")` : ""}`);
58
+ continue;
59
+ }
60
+ const command = typeof r.command === "string" ? r.command.trim() : "";
61
+ if (!command) {
62
+ errors.push(`[dev.targets] ${name}: a command is required`);
63
+ continue;
64
+ }
65
+ const target = { name, kind: kindRaw, command };
66
+ const url = typeof r.url === "string" ? r.url.trim() : "";
67
+ const artifactPath = typeof r.artifactPath === "string" ? r.artifactPath.trim() : "";
68
+ if (kindRaw === "server" && !url) {
69
+ // A long-lived target with no URL is a `process`, not a `server` — point
70
+ // the user at the right kind rather than leaving them stuck.
71
+ errors.push(`[dev.targets] ${name}: a "server" target needs a url (use kind = "process" for a long-lived target with no URL)`);
72
+ continue;
73
+ }
74
+ if (kindRaw === "artifact" && !artifactPath) {
75
+ errors.push(`[dev.targets] ${name}: an "artifact" target needs an artifactPath`);
76
+ continue;
77
+ }
78
+ if (kindRaw === "server" && artifactPath) {
79
+ errors.push(`[dev.targets] ${name}: a "server" target cannot have an artifactPath`);
80
+ continue;
81
+ }
82
+ if (kindRaw === "artifact" && url) {
83
+ errors.push(`[dev.targets] ${name}: an "artifact" target cannot have a url`);
84
+ continue;
85
+ }
86
+ // A process has neither a URL nor a build artifact — it is healthy while it
87
+ // is alive, which is its health check. Reject the conflicting fields so a
88
+ // typo can never silently change what the target means.
89
+ if (kindRaw === "process" && url) {
90
+ errors.push(`[dev.targets] ${name}: a "process" target cannot have a url`);
91
+ continue;
92
+ }
93
+ if (kindRaw === "process" && artifactPath) {
94
+ errors.push(`[dev.targets] ${name}: a "process" target cannot have an artifactPath`);
95
+ continue;
96
+ }
97
+ if (url)
98
+ target.url = url;
99
+ if (artifactPath)
100
+ target.artifactPath = artifactPath;
101
+ const label = typeof r.label === "string" ? r.label.trim() : "";
102
+ if (label)
103
+ target.label = label;
104
+ // healthCheck arrives in one of three shapes, all accepted here so the
105
+ // documented `healthCheck = { url = "..." }` actually works:
106
+ // - an object, from a real TOML parser (or a test passing rows directly);
107
+ // - the raw inline-table string the flat line parser leaves behind
108
+ // (`'{ url = "..." }'`, since it has no inline-table support);
109
+ // - dotted keys `healthCheck.url` / `healthCheck.file`, which the flat
110
+ // parser keeps as literal keys on the row.
111
+ const hcRaw = r.healthCheck;
112
+ let hcUrl = typeof r["healthCheck.url"] === "string" ? r["healthCheck.url"].trim() : "";
113
+ let hcFile = typeof r["healthCheck.file"] === "string" ? r["healthCheck.file"].trim() : "";
114
+ const hasInlineKeys = r["healthCheck.url"] !== undefined || r["healthCheck.file"] !== undefined;
115
+ if (hcRaw !== undefined) {
116
+ if (typeof hcRaw === "string") {
117
+ const parsedHc = parseInlineHealthCheck(hcRaw);
118
+ if (!parsedHc) {
119
+ errors.push(`[dev.targets] ${name}: healthCheck must be a table with url or file`);
120
+ continue;
121
+ }
122
+ hcUrl = hcUrl || parsedHc.url || "";
123
+ hcFile = hcFile || parsedHc.file || "";
124
+ }
125
+ else if (typeof hcRaw === "object" && hcRaw !== null && !Array.isArray(hcRaw)) {
126
+ const hc = hcRaw;
127
+ hcUrl = hcUrl || (typeof hc.url === "string" ? hc.url.trim() : "");
128
+ hcFile = hcFile || (typeof hc.file === "string" ? hc.file.trim() : "");
129
+ }
130
+ else {
131
+ errors.push(`[dev.targets] ${name}: healthCheck must be a table with url or file`);
132
+ continue;
133
+ }
134
+ }
135
+ if (hcUrl || hcFile) {
136
+ target.healthCheck = {
137
+ ...(hcUrl ? { url: hcUrl } : {}),
138
+ ...(hcFile ? { file: hcFile } : {}),
139
+ };
140
+ }
141
+ else if (hcRaw !== undefined || hasInlineKeys) {
142
+ errors.push(`[dev.targets] ${name}: healthCheck needs a url or a file`);
143
+ continue;
144
+ }
145
+ // A process is healthy while its process is alive; a readiness check would
146
+ // only muddy that. Reject one rather than silently ignore it.
147
+ if (kindRaw === "process" && target.healthCheck) {
148
+ errors.push(`[dev.targets] ${name}: a "process" target cannot have a healthCheck (it is running while its process is alive)`);
149
+ continue;
150
+ }
151
+ seen.add(name);
152
+ targets.push(target);
153
+ }
154
+ return { targets, errors };
155
+ }
@@ -407,6 +407,12 @@ export declare function gitInit(root: string): boolean;
407
407
  * where 0023's surgical `commitNewFile` applies. Fail-soft: null on failure.
408
408
  */
409
409
  export declare function gitCommitAll(root: string, message: string): string | null;
410
+ /**
411
+ * The short HEAD sha of `root`, or null when git is unavailable or the path is
412
+ * not a repository (#0755). Used to tie handoff UI evidence to the exact tree
413
+ * it captured, so a later commit can be flagged as a stale capture.
414
+ */
415
+ export declare function shortHeadOf(root: string): string | null;
410
416
  export interface CommitNewFileResult {
411
417
  ok: boolean;
412
418
  /** Short commit hash (when committed). */
@@ -638,6 +644,18 @@ export interface UncommittedWorkFilesOptions {
638
644
  * the default answer for handoff and pre-merge guards.
639
645
  */
640
646
  export declare function uncommittedWorkFiles(root: string, filter?: WorkFileFilter, opts?: UncommittedWorkFilesOptions): Promise<string[]>;
647
+ /**
648
+ * Whether a path that conflicted between two sides is one the merge is allowed
649
+ * to resolve on its own: it matches an `autoResolve` entry (generated output,
650
+ * the closing task's own file) or an `autoResolveOurs` entry (unrelated task
651
+ * files, where main's copy wins). A conflicted path matching neither is a REAL
652
+ * conflict. This is the single definition of "a real conflict" shared by the
653
+ * real merge and `mergeBranch`'s dry-run pre-flight, so the two can never drift.
654
+ */
655
+ export declare function isResolvableConflict(path: string, opts: {
656
+ autoResolve?: string[];
657
+ autoResolveOurs?: string[];
658
+ }): boolean;
641
659
  /**
642
660
  * Merge `branch` into the CURRENT checkout — the main worktree, never the
643
661
  * task's worktree. Default merge semantics: fast-forward when main is an
package/dist/core/git.js CHANGED
@@ -1116,6 +1116,15 @@ export function gitCommitAll(root, message) {
1116
1116
  notifyGitMutation(root, "commit");
1117
1117
  return git(root, ["rev-parse", "--short", "HEAD"]);
1118
1118
  }
1119
+ /**
1120
+ * The short HEAD sha of `root`, or null when git is unavailable or the path is
1121
+ * not a repository (#0755). Used to tie handoff UI evidence to the exact tree
1122
+ * it captured, so a later commit can be flagged as a stale capture.
1123
+ */
1124
+ export function shortHeadOf(root) {
1125
+ const sha = git(root, ["rev-parse", "--short", "HEAD"]);
1126
+ return sha ? sha.trim() || null : null;
1127
+ }
1119
1128
  /**
1120
1129
  * Surgically commit a single new file with the user's own git identity.
1121
1130
  *
@@ -1518,7 +1527,7 @@ function blockingFiles(stderr) {
1518
1527
  * conflict. This is the single definition of "a real conflict" shared by the
1519
1528
  * real merge and `mergeBranch`'s dry-run pre-flight, so the two can never drift.
1520
1529
  */
1521
- function isResolvableConflict(path, opts) {
1530
+ export function isResolvableConflict(path, opts) {
1522
1531
  const matches = (rule, p) => p === rule || p.startsWith(rule.endsWith("/") ? rule : rule + "/");
1523
1532
  return [...(opts.autoResolve ?? []), ...(opts.autoResolveOurs ?? [])].some((r) => matches(r, path));
1524
1533
  }
@@ -293,10 +293,16 @@ const opencodeAdapter = {
293
293
  };
294
294
  const args = opts.refresh ? ["models", "--refresh"] : ["models"];
295
295
  const timeoutMs = opts.timeoutMs ?? MODELS_TIMEOUT_MS;
296
- const out = await spawnModels(bin, args, {
296
+ let out = await spawnModels(bin, args, {
297
297
  timeoutMs,
298
298
  cwd: opts.cwd,
299
299
  });
300
+ // Some opencode builds (v2.0.11 among them) reject `--refresh` with exit 1.
301
+ // Retry the plain probe: the caller has already bypassed RepoOS's own cache,
302
+ // so this is still a fresh list rather than the cached one.
303
+ if (opts.refresh && out.code !== 0 && /Unrecognized flag: --refresh/.test(out.stderr)) {
304
+ out = await spawnModels(bin, ["models"], { timeoutMs, cwd: opts.cwd });
305
+ }
300
306
  const error = probeError(bin, out, timeoutMs);
301
307
  return {
302
308
  supported: true,
@@ -61,6 +61,15 @@ export interface ShotCaptureMissHandlers {
61
61
  onSelectorMiss?: (selector: string, route: string) => void;
62
62
  /** Step selector timeouts become warnings instead of aborting capture (#0743). */
63
63
  onStepMiss?: (message: string, route: string) => void;
64
+ /**
65
+ * #0755: after the steps and settle, the page still shows a loading state
66
+ * (a visible `aria-busy` region, or "Loading changes…" text). A capture of a
67
+ * loading page is not the declared evidence — the route matched but the
68
+ * content never rendered — so the caller records this as a FAILED step, not a
69
+ * passing capture. Best-effort: a driver without `evaluate` cannot probe, and
70
+ * the capture proceeds.
71
+ */
72
+ onLoadingState?: (message: string, route: string) => void;
64
73
  }
65
74
  /**
66
75
  * Capture one page. `waitUntil: "load"` fires before a dev server's client