chainlesschain 0.162.154 → 0.162.156

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 (205) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CIpoXyVI.js → AIOps-DjBXlypQ.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Ch_QXIPk.js → ActionButton-Cv9er9bp.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-CvaBYShv.js → Analytics-CLKDjSNs.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-CfyZNzmJ.js +9 -0
  8. package/src/assets/web-panel/assets/{Audit-Bng8eHCW.js → Audit-CXafD178.js} +1 -1
  9. package/src/assets/web-panel/assets/BackgroundAgents-CP5c-GSd.js +1 -0
  10. package/src/assets/web-panel/assets/BackgroundAgents-Dq2340Kl.css +1 -0
  11. package/src/assets/web-panel/assets/{Backup-DvBs8-lm.js → Backup-BbYLQfyt.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-DKwajm0g.js → BaseInput-Bqe0MwZL.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-BoGpZ3uO.js → Chat-jNA_yUPH.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-mDQl_PXO.js → ChatBubbleRenderer-BOFLF01s.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-BMJEmxFQ.js → Checkbox-Dlzz-zD1.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-Bsoag5CV.js → Codegen-BAsDQjGf.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-CDjH9-sp.js → Col-Ju0HrAuS.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-Cg2QHPQ6.js → Community-DynJd43Z.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BU2BaqP-.js → Compact-CabbZVww.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-D6ZYMCqZ.js → Compliance-DW4BmAoh.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DaZ8VW-I.js → Cowork-Bk1S_7X-.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-DQY1RH9v.js → Cron-By4sh_NM.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BVR28vk9.js → Crosschain-DdZdbS1C.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-N44KvTP-.js → DID-CkL-sQyS.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Bjhlx6wS.js → Dashboard-g_mOpumg.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-CpLYxpy1.js → Dropdown-DtHyE7Ao.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-srEITSPI.js → EmailListRenderer-Kpvov5Wr.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-juOC_fX2.js → FamilyGuardDashboard-C4kZoePR.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-nRaQtaXW.js → Federation-BAwWoK2N.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DflhTj09.js → FormItemContext-DeI0i5f8.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DulI_3NI.js → GenericCardRenderer-CkcKRtos.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-BxOPefXn.js → Git-kX6jjI9y.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-C6G_Wnwi.js → Governance-BXJCBm6x.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-BnFYa2BF.js → Inference-ujD2JSKo.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-TqQLEabm.js → KnowledgeGraph-3Dnuf1Wh.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BH4AVh7s.js → Logs-lVAZCtUB.js} +2 -2
  37. package/src/assets/web-panel/assets/{Marketplace-DoWLTaEE.js → Marketplace-qnzNPm-A.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-D6yC7OG_.js → McpTools-CqjzefRU.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-DLCeHj21.js → Memory-BFiPAxk6.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BZBE0GRl.js → MobileBridge-D0O43VwF.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DJA15G1J.js → MobileProjects-B3now48t.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-CyTKJDHn.js → Mtc-DybhtaCS.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-DZKjgEVc.js → MtcAudit-D7MmY4F0.js} +5 -5
  44. package/src/assets/web-panel/assets/{Multisig-2rMPNhSO.js → Multisig-wZvGJnB8.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-VeMk4Fx-.js → NLProgramming-BNPpgfvn.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-jr2GHMhh.js → Notes-B6FS3KBj.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-WN9VZyFb.js → NotificationSettings-CK0yla9U.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-CXfPHIpy.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-DdjVHuzT.js → Organization-geqgwN1T.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-l8vBsGGq.js → Overflow-BWJYZFbC.js} +1 -1
  51. package/src/assets/web-panel/assets/{OverrideContext-BlgL9440.js → OverrideContext-C0w0P4A3.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-CVQDHHwq.js → P2P-DnU1505F.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-CTsNh8qE.js → PdhVaultBrowser-BGY74KnC.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-DLW87uyK.js → Permissions-CgArrWtQ.js} +3 -3
  55. package/src/assets/web-panel/assets/{PersonalDataHub-N8OvASTx.js → PersonalDataHub-C2vYE61a.js} +2 -2
  56. package/src/assets/web-panel/assets/{Pipeline-BPZmdlz2.js → Pipeline-CBYLI4yW.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-MFZw_GNh.js → Privacy-B-PdW9aN.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-zW0pEsfU.js → ProjectInit-CuWQegbV.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CppVHr_o.js → ProjectSettings-4d32m_cS.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-BTglkJO2.js → Projects-VLA7ttwd.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-CPHZ6JKT.js → Providers-B_Izb_oa.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-DO4klNZ2.js → QrScannerModal-C6B_ndXV.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-B37TTqUa.js → QuickAsk-C6tavL-y.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-n7KIfK7y.js → Recommend-CF-LaZ6x.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-SjVoS9mm.js → RemoteSession-DBqTTQ9_.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-Dt6RTeHi.js → Reputation-B01FALUi.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-Dty6jz5V.js → Row-DO0fd48A.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-DDLhqw5j.js → RssFeed-BV5LDzFR.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DrYbXi-W.js → Search-CTti4_MP.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Fc9Z6fbB.js → Security-DwSrWRCw.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-BAhPgjSM.js → Services-BEf1QyM4.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-8JF7EPfE.js → Skeleton-LZXTpovy.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-DL39dY0t.js → Skills-SKgc0Zug.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-CSvjcATc.js → Sla-C_QVOtkN.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-Demx7Gnx.js → SpeechSettings-v5TW2V-X.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-CKLnRWJr.js → SyncSettings-CQPjHdqU.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-DCd-uYcI.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-AjQI5rxD.js → Templates-62VDIjtj.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-dbtEh1yR.js → Tenant-BnFQ0pNy.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-DX-9jjfk.js → Terminal-D3v0MGSj.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-C11EDZRt.js → TimelineRenderer-BI1Ka-cu.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-GiA9fiZn.js → Tokens-BwX1jXYP.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BPcnclJn.js → Trigger-CSA8TqNx.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-C-DVY8nq.js → Trust-Bn3IjCx7.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DxodSwME.js → UkeySign-pUbOAqNi.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-D_WV0O8O.js → VideoEditing-Db2RercF.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-DSeNlVSU.js → Wallet-CpR1BVAl.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-Bwuuvpz1.js → WebAuthn-Yxtkn7Zp.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-8M96EU1N.js → WorkflowEditor-CYLe9a2E.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-D5S6SmLm.js → chat-gsOGdak0.js} +1 -1
  91. package/src/assets/web-panel/assets/{collapseMotion-DVfhbsdP.js → collapseMotion-CqM1QOwM.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-5fbnGkAP.js → colors-D9fN299a.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-DaSuuMlW.js → compact-item-vkd7s-ay.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-BNOHcXAF.js → createContext-C7YkbMyy.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-DEcR2h6k.js +1 -0
  96. package/src/assets/web-panel/assets/{echarts-CL0F2SnX.js → echarts-C61Vn3mP.js} +1 -1
  97. package/src/assets/web-panel/assets/{hasIn-DONwZUra.js → hasIn-ig7reRSU.js} +1 -1
  98. package/src/assets/web-panel/assets/{icons-BsDmGpcT.js → icons-DEmcnxBf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BJp4kRjw.js → index-9TxdMYju.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-MOgO5Yvx.js → index-B7QRpKif.js} +1 -1
  101. package/src/assets/web-panel/assets/index-BEibanjg.js +1 -0
  102. package/src/assets/web-panel/assets/{index-CAYmc5Nh.js → index-BHHN_KTS.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BcWMZqb4.js → index-BMw48d19.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B_yDD9Lm.js → index-BYe9HRSt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-xo2f4DZR.js → index-BfpVcXKa.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DbdQBn4n.js → index-BhftELHI.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Dd7xwGcD.js → index-BkP17Z95.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-RkInlwV0.js → index-BpGlLbRv.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DM3Ug61e.js → index-Bt-9ZhIu.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CrkCFohi.js → index-BvDEukXe.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-_6S41ikV.js → index-C7AY39sg.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-uR5KTNZA.js → index-C8HZMygt.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-5_fj5gVd.js → index-CAkz1f1r.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BLzMuz38.js → index-CDWTycrg.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BEO5f7Vf.js → index-CGzGe5tv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CEzd5m5a.js → index-Co2NYyew.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BcqOK7Lv.js → index-CspGymmg.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DP2-3A_j.js → index-D2yhtk6a.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-LRIt-AFl.js → index-D6s_kbjt.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B4L3gsNA.js → index-DFM1oNe1.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-KwV9g_cf.js → index-DFfxF7Vt.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BK2N5yVW.js → index-DPguq3X7.js} +4 -4
  123. package/src/assets/web-panel/assets/{index-Cf2OCa8q.js → index-DQ8U7kzC.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D7gs_3Ck.js → index-DZAxCnjS.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-PsMErAB1.js → index-DhdXbsXX.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CTZeOnzO.js → index-DuwbKHoS.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Dm5A-MN5.js → index-DvCJXmrx.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-vYtprlNs.js → index-Dy_TKZ9h.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DHx2lhHW.js → index-FzGr6CVk.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Cn5LtyIH.js → index-PkH39qsp.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CmUOjyL6.js → index-SDrcQXO1.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CztESwB4.js → index-W52sHRdr.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CSfYJqXt.js → index-bVbgYBhv.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BrAJSu-v.js → index-gj8kxcAi.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CLXf-8ek.js → index-gpurplyq.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-ClGqO-2X.js → index-nX8YE6cj.js} +1 -1
  137. package/src/assets/web-panel/assets/index-z_25tU3p.js +1 -0
  138. package/src/assets/web-panel/assets/{initDefaultProps-CxwLynp9.js → initDefaultProps-BrgfVRg9.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-XbPvGJbW.js → motion-D_3xmwac.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-D1u5pcgS.js → move-DDDJdlxw.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BDxhTBMH.js → mtc-parser-BK2uEWwn.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-EtIFGg5k.js → omit-BPHKbDcf.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-D33sWb0l.js → pickAttrs-LNnTscuz.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-BwFRzaGo.js → placementArrow-CvmTZll5.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-CBk2WqvQ.js → responsiveObserve-eBj7qi2J.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-B52HtrKn.js → slide-D68hHkrM.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-D4qxqWnH.js → statusUtils-daTlaeo0.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-BxaCFlOL.js → styleChecker-DtyKMtHB.js} +1 -1
  149. package/src/assets/web-panel/assets/useFlexGapSupport-ekFwFs9h.js +1 -0
  150. package/src/assets/web-panel/assets/{useFs-C6VAU1_6.js → useFs-jRaSEoF4.js} +1 -1
  151. package/src/assets/web-panel/assets/{useMergedState-Dc9sO2d7.js → useMergedState-DXbJa_zE.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-rEG6oXG8.js → usePersonalDataHub-DD5nGzjA.js} +1 -1
  153. package/src/assets/web-panel/assets/{useRefs-CAeFRzPf.js → useRefs-BrCfMLMc.js} +1 -1
  154. package/src/assets/web-panel/assets/{useState-LwExUYDO.js → useState-CnxtbLoC.js} +1 -1
  155. package/src/assets/web-panel/assets/{vendor-BMxUs6UX.js → vendor-Bn1JzzAn.js} +1 -1
  156. package/src/assets/web-panel/assets/{vnode-DOTafkEi.js → vnode-KcgLUiom.js} +1 -1
  157. package/src/assets/web-panel/assets/{zoom-BuwwN5K2.js → zoom-Cyk2PQ-s.js} +1 -1
  158. package/src/assets/web-panel/index.html +3 -3
  159. package/src/command-manifest.json +50 -1
  160. package/src/commands/agenda.js +228 -0
  161. package/src/commands/agent.js +23 -2
  162. package/src/commands/agents.js +62 -1
  163. package/src/commands/auto-mode.js +87 -0
  164. package/src/commands/background-session.js +499 -0
  165. package/src/commands/batch.js +345 -0
  166. package/src/commands/command.js +4 -1
  167. package/src/commands/permissions.js +49 -0
  168. package/src/commands/remote-control.js +407 -0
  169. package/src/data/changelog.json +12 -2
  170. package/src/gateways/ws/background-agent-protocol.js +337 -0
  171. package/src/gateways/ws/message-dispatcher.js +10 -0
  172. package/src/gateways/ws/remote-session-protocol.js +102 -1
  173. package/src/gateways/ws/ws-server.js +61 -0
  174. package/src/harness/mcp-client.js +8 -0
  175. package/src/index.js +22 -0
  176. package/src/lib/agent-batch.js +235 -0
  177. package/src/lib/agent-notify.js +66 -0
  178. package/src/lib/agent-schedule-store.js +330 -0
  179. package/src/lib/auto-mode-config.js +396 -0
  180. package/src/lib/background-agent-supervisor.js +202 -17
  181. package/src/lib/background-session-transport.js +315 -0
  182. package/src/lib/permission-denial-store.js +159 -0
  183. package/src/lib/remote-approval-bridge.js +360 -0
  184. package/src/lib/remote-control.js +270 -0
  185. package/src/lib/repl-denials.js +26 -0
  186. package/src/lib/shell-approval.js +4 -0
  187. package/src/lib/ws-rpc-client.js +150 -0
  188. package/src/repl/agent-repl.js +208 -16
  189. package/src/repl/permission-tier.js +31 -0
  190. package/src/runtime/agent-core.js +161 -0
  191. package/src/runtime/coding-agent-contract-shared.cjs +104 -0
  192. package/src/runtime/coding-agent-policy.cjs +28 -0
  193. package/src/runtime/headless-runner.js +126 -8
  194. package/src/runtime/headless-stream.js +87 -1
  195. package/src/runtime/mcp-config.js +11 -0
  196. package/src/runtime/mcp-tool-search.js +0 -0
  197. package/src/runtime/policies/agent-policy.js +17 -0
  198. package/src/workers/background-agent-worker.js +218 -37
  199. package/src/assets/web-panel/assets/AppLayout-B6Q6olcr.js +0 -9
  200. package/src/assets/web-panel/assets/OrderTableRenderer-CjNO-e9T.js +0 -1
  201. package/src/assets/web-panel/assets/Tasks-mhITRxjv.js +0 -1
  202. package/src/assets/web-panel/assets/devWarning-K_woVElh.js +0 -1
  203. package/src/assets/web-panel/assets/index-3ILvaBpo.js +0 -1
  204. package/src/assets/web-panel/assets/index-JpB-FoZq.js +0 -1
  205. package/src/assets/web-panel/assets/useFlexGapSupport-B1lWlqXN.js +0 -1
@@ -0,0 +1,345 @@
1
+ /**
2
+ * `cc batch` — dynamic worktree batch (gap-analysis 第四阶段 #4). Split a big
3
+ * change into independent UNITS, run each in its own git worktree in parallel,
4
+ * test each, and aggregate diff/test/merge-conflict results.
5
+ *
6
+ * cc batch --units units.json # run declared units
7
+ * cc batch --units units.json --test "npm test" --concurrency 4 --merge
8
+ * cc batch --decompose "migrate all foo() calls to bar()" --parts 8
9
+ * # ask an agent to split the goal into a units.json, then run it
10
+ * cc batch --decompose "…" --parts 8 --plan-only # emit units, don't run
11
+ *
12
+ * units.json: { "units": [ { "key": "u1", "prompt": "…", "test": "npm test -w a" } ] }
13
+ *
14
+ * Real isolation comes from worktree-isolator (the same primitive `cc team
15
+ * --worktree` uses); the fan-out + aggregation core lives in lib/agent-batch.js
16
+ * (fully injectable). Branches persist for inspection unless merged.
17
+ */
18
+
19
+ import fs from "fs";
20
+ import path from "path";
21
+ import { fileURLToPath } from "url";
22
+ import { spawn } from "child_process";
23
+ import { execFileSync } from "child_process";
24
+ import chalk from "chalk";
25
+ import { logger } from "../lib/logger.js";
26
+ import { runBatch } from "../lib/agent-batch.js";
27
+ import {
28
+ createWorktree,
29
+ removeWorktree,
30
+ previewWorktreeMerge,
31
+ mergeWorktree,
32
+ } from "../harness/worktree-isolator.js";
33
+ import { findProjectRoot } from "../lib/project-detector.js";
34
+
35
+ const BIN = path.resolve(
36
+ path.dirname(fileURLToPath(import.meta.url)),
37
+ "..",
38
+ "..",
39
+ "bin",
40
+ "chainlesschain.js",
41
+ );
42
+
43
+ const DECOMPOSE_SCHEMA = {
44
+ type: "object",
45
+ properties: {
46
+ units: {
47
+ type: "array",
48
+ items: {
49
+ type: "object",
50
+ properties: {
51
+ key: { type: "string" },
52
+ prompt: { type: "string" },
53
+ test: { type: "string" },
54
+ },
55
+ required: ["key", "prompt"],
56
+ },
57
+ },
58
+ },
59
+ required: ["units"],
60
+ };
61
+
62
+ export function registerBatchCommand(program) {
63
+ program
64
+ .command("batch")
65
+ .description(
66
+ "Split a large change into independent units, run each in its own git worktree in parallel, and aggregate results",
67
+ )
68
+ .option(
69
+ "--units <file>",
70
+ "Units JSON file ({ units: [{key, prompt, test?}] })",
71
+ )
72
+ .option(
73
+ "--decompose <goal>",
74
+ "Ask an agent to split this goal into units first (structured output)",
75
+ )
76
+ .option("--parts <n>", "Target number of units for --decompose", "6")
77
+ .option(
78
+ "--plan-only",
79
+ "With --decompose: print the units and exit (no run)",
80
+ )
81
+ .option("--concurrency <n>", "Max worktrees running at once", "4")
82
+ .option("--test <cmd>", "Default test command run in each worktree")
83
+ .option("--merge", "Merge clean, passing branches back to base")
84
+ .option("--model <model>", "Model for agent runs")
85
+ .option("--json", "Machine-readable JSON output")
86
+ .action(async (options) => {
87
+ process.exitCode = await runBatchCommand(options);
88
+ });
89
+ }
90
+
91
+ export async function runBatchCommand(options = {}, _deps = {}) {
92
+ const log = _deps.log || ((m) => console.log(m));
93
+ const errLog = _deps.err || ((m) => logger.error(m));
94
+ const repoDir =
95
+ _deps.repoDir || findProjectRoot(process.cwd()) || process.cwd();
96
+
97
+ // 1. Resolve the unit list — from a file, or by decomposing a goal.
98
+ let units;
99
+ try {
100
+ if (options.units) {
101
+ const doc = JSON.parse(fs.readFileSync(options.units, "utf8"));
102
+ units = Array.isArray(doc) ? doc : doc.units;
103
+ } else if (options.decompose) {
104
+ units = await (_deps.decompose || decomposeGoal)(
105
+ options.decompose,
106
+ Number(options.parts) || 6,
107
+ { model: options.model, repoDir },
108
+ );
109
+ if (options.planOnly) {
110
+ log(JSON.stringify({ units }, null, 2));
111
+ return 0;
112
+ }
113
+ } else {
114
+ errLog("Provide --units <file> or --decompose <goal>.");
115
+ return 4;
116
+ }
117
+ } catch (err) {
118
+ errLog(`Failed to resolve units: ${err.message}`);
119
+ return 4;
120
+ }
121
+
122
+ // 2. Run the batch with real worktree + agent + git deps.
123
+ const deps = _deps.batchDeps || {
124
+ createWorktree: (key, branch) => createWorktree(repoDir, branch).path,
125
+ removeWorktree: (worktreePath, opts) =>
126
+ removeWorktree(repoDir, worktreePath, opts),
127
+ runAgent:
128
+ _deps.runAgent ||
129
+ ((prompt, cwd) => spawnAgent(prompt, cwd, { model: options.model })),
130
+ runTest: _deps.runTest || runTestCommand,
131
+ diffStat: _deps.diffStat || gitDiffStat,
132
+ commit: _deps.commit || gitCommitAll,
133
+ previewMerge: (branch) => previewWorktreeMerge(repoDir, branch),
134
+ mergeBranch: (branch, message) =>
135
+ mergeWorktree(repoDir, branch, { message }),
136
+ branchFor: (key) => `batch/${sanitize(key)}`,
137
+ };
138
+
139
+ const onEvent = options.json ? () => {} : (ev) => log(formatEvent(ev));
140
+
141
+ let outcome;
142
+ try {
143
+ outcome = await runBatch(
144
+ {
145
+ units,
146
+ concurrency: options.concurrency,
147
+ test: options.test || null,
148
+ merge: options.merge === true,
149
+ onEvent,
150
+ },
151
+ deps,
152
+ );
153
+ } catch (err) {
154
+ errLog(`Batch failed: ${err.message}`);
155
+ return 1;
156
+ }
157
+
158
+ if (options.json) {
159
+ log(JSON.stringify(outcome, null, 2));
160
+ } else {
161
+ log("");
162
+ log(chalk.bold(" Batch summary:"));
163
+ const s = outcome.summary;
164
+ log(
165
+ ` ${s.total} units — ${chalk.green(s.done + " done")}, ` +
166
+ `${chalk.red(s.testFailed + " test-failed")}, ` +
167
+ `${chalk.yellow(s.errored + " errored")}, ${s.noChanges} no-change`,
168
+ );
169
+ if (options.merge) {
170
+ log(
171
+ ` merged ${chalk.green(s.merged)}, conflicts ${chalk.yellow(s.conflicted)}`,
172
+ );
173
+ }
174
+ log("");
175
+ }
176
+ // Non-zero when anything failed so CI can gate on it.
177
+ return outcome.summary.testFailed + outcome.summary.errored > 0 ? 1 : 0;
178
+ }
179
+
180
+ // ─── real effectful deps ───────────────────────────────────────────────────
181
+
182
+ function sanitize(key) {
183
+ return String(key).replace(/[^a-zA-Z0-9._-]/g, "-");
184
+ }
185
+
186
+ function spawnAgent(prompt, cwd, opts = {}) {
187
+ return new Promise((resolve, reject) => {
188
+ const args = [
189
+ BIN,
190
+ "agent",
191
+ "-p",
192
+ prompt,
193
+ "--permission-mode",
194
+ "acceptEdits",
195
+ "--output-format",
196
+ "text",
197
+ ];
198
+ if (opts.model) args.push("--model", opts.model);
199
+ const child = spawn(process.execPath, args, {
200
+ cwd,
201
+ env: { ...process.env, CLAUDECODE: "1" },
202
+ windowsHide: true,
203
+ });
204
+ let err = "";
205
+ child.stderr?.on("data", (d) => (err += d.toString("utf8")));
206
+ child.on("error", (e) => reject(new Error(e.message)));
207
+ child.on("close", (code) =>
208
+ code === 0
209
+ ? resolve({ code })
210
+ : reject(new Error(err.trim() || `agent exited ${code}`)),
211
+ );
212
+ });
213
+ }
214
+
215
+ function runTestCommand(command, cwd) {
216
+ return new Promise((resolve, reject) => {
217
+ const child = spawn(command, {
218
+ cwd,
219
+ shell: true,
220
+ env: process.env,
221
+ windowsHide: true,
222
+ });
223
+ let err = "";
224
+ child.stderr?.on("data", (d) => (err += d.toString("utf8")));
225
+ child.on("error", (e) => reject(new Error(e.message)));
226
+ child.on("close", (code) =>
227
+ code === 0
228
+ ? resolve({ code })
229
+ : reject(new Error(err.trim() || `test exited ${code}`)),
230
+ );
231
+ });
232
+ }
233
+
234
+ /** `git add -A` staged numstat totals for a worktree. */
235
+ function gitDiffStat(cwd) {
236
+ try {
237
+ execFileSync("git", ["add", "-A"], { cwd, stdio: "ignore" });
238
+ const out = execFileSync("git", ["diff", "--cached", "--numstat"], {
239
+ cwd,
240
+ encoding: "utf8",
241
+ });
242
+ let insertions = 0;
243
+ let deletions = 0;
244
+ let filesChanged = 0;
245
+ for (const line of out.split("\n")) {
246
+ const trimmed = line.trim();
247
+ if (!trimmed) continue;
248
+ const [add, del] = trimmed.split("\t");
249
+ filesChanged += 1;
250
+ insertions += add === "-" ? 0 : parseInt(add, 10) || 0;
251
+ deletions += del === "-" ? 0 : parseInt(del, 10) || 0;
252
+ }
253
+ return { filesChanged, insertions, deletions };
254
+ } catch {
255
+ return { filesChanged: 0, insertions: 0, deletions: 0 };
256
+ }
257
+ }
258
+
259
+ function gitCommitAll(cwd, message) {
260
+ try {
261
+ execFileSync("git", ["add", "-A"], { cwd, stdio: "ignore" });
262
+ execFileSync("git", ["commit", "-m", message, "--no-verify"], {
263
+ cwd,
264
+ stdio: "ignore",
265
+ });
266
+ return true;
267
+ } catch {
268
+ return false; // nothing to commit / commit failed
269
+ }
270
+ }
271
+
272
+ /** Spawn a headless agent that emits a units JSON via --json-schema. */
273
+ async function decomposeGoal(goal, parts, opts = {}) {
274
+ const prompt =
275
+ `Split this task into ${parts} INDEPENDENT units that can each be done in a ` +
276
+ `separate git worktree WITHOUT depending on each other's changes. For each ` +
277
+ `unit give a short kebab-case "key", a self-contained "prompt" (the full ` +
278
+ `instruction for an agent working only on that unit), and optionally a "test" ` +
279
+ `shell command to verify it.\n\nTask: ${goal}`;
280
+ return new Promise((resolve, reject) => {
281
+ const args = [
282
+ BIN,
283
+ "agent",
284
+ "-p",
285
+ prompt,
286
+ "--json-schema",
287
+ JSON.stringify(DECOMPOSE_SCHEMA),
288
+ "--permission-mode",
289
+ "plan",
290
+ ];
291
+ if (opts.model) args.push("--model", opts.model);
292
+ const child = spawn(process.execPath, args, {
293
+ cwd: opts.repoDir || process.cwd(),
294
+ env: { ...process.env, CLAUDECODE: "1" },
295
+ windowsHide: true,
296
+ });
297
+ let out = "";
298
+ let err = "";
299
+ child.stdout?.on("data", (d) => (out += d.toString("utf8")));
300
+ child.stderr?.on("data", (d) => (err += d.toString("utf8")));
301
+ child.on("error", (e) => reject(new Error(e.message)));
302
+ child.on("close", (code) => {
303
+ if (code !== 0) {
304
+ reject(new Error(err.trim() || `decompose agent exited ${code}`));
305
+ return;
306
+ }
307
+ try {
308
+ const parsed = JSON.parse(out.trim());
309
+ resolve(parsed.units);
310
+ } catch (e) {
311
+ reject(new Error(`could not parse decomposition output: ${e.message}`));
312
+ }
313
+ });
314
+ });
315
+ }
316
+
317
+ function formatEvent(ev) {
318
+ switch (ev.type) {
319
+ case "batch:start":
320
+ return chalk.dim(
321
+ ` batch: ${ev.units} units, concurrency ${ev.concurrency}`,
322
+ );
323
+ case "unit:start":
324
+ return chalk.dim(` ▶ ${ev.key} (${ev.branch})`);
325
+ case "unit:done": {
326
+ const badge =
327
+ ev.status === "done"
328
+ ? chalk.green("✓")
329
+ : ev.status === "test-failed"
330
+ ? chalk.red("✗ test")
331
+ : ev.status === "error"
332
+ ? chalk.red("✗ error")
333
+ : chalk.gray("·");
334
+ return ` ${badge} ${ev.key} (${ev.filesChanged} files)`;
335
+ }
336
+ case "unit:integrated":
337
+ return ev.conflicts
338
+ ? chalk.yellow(` ⚠ ${ev.key}: ${ev.conflicts} conflict(s)`)
339
+ : chalk.green(` ↳ ${ev.key}: ${ev.merged ? "merged" : "clean"}`);
340
+ default:
341
+ return "";
342
+ }
343
+ }
344
+
345
+ export const _internal = { decomposeGoal, gitDiffStat, DECOMPOSE_SCHEMA };
@@ -103,7 +103,10 @@ export function registerCommandCommand(program) {
103
103
  .option("--no-bang", "Do not execute !`cmd` bang segments")
104
104
  .option("--output-format <fmt>", "text | json | stream-json", "text")
105
105
  .option("--model <model>", "Override the model")
106
- .option("--permission-mode <mode>", "ApprovalGate tier (see cc agent)")
106
+ .option(
107
+ "--permission-mode <mode>",
108
+ "manual | auto | dontAsk | default | plan | acceptEdits | bypassPermissions",
109
+ )
107
110
  .action(async (name, args, options) => {
108
111
  try {
109
112
  const { getCommand, expandCommand } =
@@ -136,6 +136,55 @@ export function registerPermissionsCommand(program) {
136
136
  }
137
137
  });
138
138
 
139
+ // ── recent ────────────────────────────────────────────────────────────
140
+ cmd
141
+ .command("recent")
142
+ .alias("denials")
143
+ .description("Show recent policy denials recorded by agent runs")
144
+ .option("-n, --limit <n>", "Number of denials to show", "20")
145
+ .option("--json", "Output as JSON")
146
+ .option("--clear", "Clear the recent denial log")
147
+ .action(async (options) => {
148
+ try {
149
+ const {
150
+ clearRecentDenials,
151
+ formatRecentDenials,
152
+ readRecentDenials,
153
+ recentDenialsPath,
154
+ } = await import("../lib/permission-denial-store.js");
155
+ if (options.clear) {
156
+ const result = clearRecentDenials();
157
+ if (options.json) {
158
+ console.log(JSON.stringify(result, null, 2));
159
+ } else {
160
+ logger.log(chalk.green(`Cleared ${result.file}`));
161
+ }
162
+ return;
163
+ }
164
+ const limit = Math.max(1, Number(options.limit) || 20);
165
+ const denials = readRecentDenials({ limit });
166
+ if (options.json) {
167
+ console.log(
168
+ JSON.stringify(
169
+ {
170
+ file: recentDenialsPath(),
171
+ count: denials.length,
172
+ denials,
173
+ },
174
+ null,
175
+ 2,
176
+ ),
177
+ );
178
+ return;
179
+ }
180
+ logger.log(formatRecentDenials(denials));
181
+ logger.log(chalk.gray(` source: ${recentDenialsPath()}`));
182
+ } catch (err) {
183
+ logger.error(chalk.red(`permissions recent failed: ${err.message}`));
184
+ process.exitCode = 1;
185
+ }
186
+ });
187
+
139
188
  // ── test (dry-run) ─────────────────────────────────────────────────────
140
189
  cmd
141
190
  .command("test <tool> [args...]")