@mclawnet/swarm 0.1.13 → 0.1.15

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 (320) hide show
  1. package/dist/__tests__/always-on-activity-reader.test.d.ts +2 -0
  2. package/dist/__tests__/always-on-activity-reader.test.d.ts.map +1 -0
  3. package/dist/__tests__/always-on-activity-reader.test.js +193 -0
  4. package/dist/__tests__/always-on-activity-reader.test.js.map +1 -0
  5. package/dist/__tests__/always-on-config.test.d.ts +2 -0
  6. package/dist/__tests__/always-on-config.test.d.ts.map +1 -0
  7. package/dist/__tests__/always-on-config.test.js +285 -0
  8. package/dist/__tests__/always-on-config.test.js.map +1 -0
  9. package/dist/__tests__/always-on-manager.test.d.ts +2 -0
  10. package/dist/__tests__/always-on-manager.test.d.ts.map +1 -0
  11. package/dist/__tests__/always-on-manager.test.js +797 -0
  12. package/dist/__tests__/always-on-manager.test.js.map +1 -0
  13. package/dist/__tests__/always-on-parity.test.d.ts +2 -0
  14. package/dist/__tests__/always-on-parity.test.d.ts.map +1 -0
  15. package/dist/__tests__/always-on-parity.test.js +20 -0
  16. package/dist/__tests__/always-on-parity.test.js.map +1 -0
  17. package/dist/__tests__/cascade-picker.test.d.ts +2 -0
  18. package/dist/__tests__/cascade-picker.test.d.ts.map +1 -0
  19. package/dist/__tests__/cascade-picker.test.js +122 -0
  20. package/dist/__tests__/cascade-picker.test.js.map +1 -0
  21. package/dist/__tests__/coordinator-shipment.test.d.ts +2 -0
  22. package/dist/__tests__/coordinator-shipment.test.d.ts.map +1 -0
  23. package/dist/__tests__/coordinator-shipment.test.js +280 -0
  24. package/dist/__tests__/coordinator-shipment.test.js.map +1 -0
  25. package/dist/__tests__/coordinator-workspace-recover.test.d.ts +2 -0
  26. package/dist/__tests__/coordinator-workspace-recover.test.d.ts.map +1 -0
  27. package/dist/__tests__/coordinator-workspace-recover.test.js +140 -0
  28. package/dist/__tests__/coordinator-workspace-recover.test.js.map +1 -0
  29. package/dist/__tests__/coordinator-workspace.test.d.ts +2 -0
  30. package/dist/__tests__/coordinator-workspace.test.d.ts.map +1 -0
  31. package/dist/__tests__/coordinator-workspace.test.js +135 -0
  32. package/dist/__tests__/coordinator-workspace.test.js.map +1 -0
  33. package/dist/__tests__/default-runner-epipe.test.d.ts +2 -0
  34. package/dist/__tests__/default-runner-epipe.test.d.ts.map +1 -0
  35. package/dist/__tests__/default-runner-epipe.test.js +43 -0
  36. package/dist/__tests__/default-runner-epipe.test.js.map +1 -0
  37. package/dist/__tests__/discovery-scheduler.test.d.ts +2 -0
  38. package/dist/__tests__/discovery-scheduler.test.d.ts.map +1 -0
  39. package/dist/__tests__/discovery-scheduler.test.js +367 -0
  40. package/dist/__tests__/discovery-scheduler.test.js.map +1 -0
  41. package/dist/__tests__/env-forward-e2e.test.d.ts +2 -0
  42. package/dist/__tests__/env-forward-e2e.test.d.ts.map +1 -0
  43. package/dist/__tests__/env-forward-e2e.test.js +57 -0
  44. package/dist/__tests__/env-forward-e2e.test.js.map +1 -0
  45. package/dist/__tests__/gh-pr-creator.test.d.ts +2 -0
  46. package/dist/__tests__/gh-pr-creator.test.d.ts.map +1 -0
  47. package/dist/__tests__/gh-pr-creator.test.js +107 -0
  48. package/dist/__tests__/gh-pr-creator.test.js.map +1 -0
  49. package/dist/__tests__/git-worktree-provider.test.d.ts +2 -0
  50. package/dist/__tests__/git-worktree-provider.test.d.ts.map +1 -0
  51. package/dist/__tests__/git-worktree-provider.test.js +98 -0
  52. package/dist/__tests__/git-worktree-provider.test.js.map +1 -0
  53. package/dist/__tests__/gitignore-check.test.d.ts +2 -0
  54. package/dist/__tests__/gitignore-check.test.d.ts.map +1 -0
  55. package/dist/__tests__/gitignore-check.test.js +39 -0
  56. package/dist/__tests__/gitignore-check.test.js.map +1 -0
  57. package/dist/__tests__/idea-research-source.test.d.ts +2 -0
  58. package/dist/__tests__/idea-research-source.test.d.ts.map +1 -0
  59. package/dist/__tests__/idea-research-source.test.js +425 -0
  60. package/dist/__tests__/idea-research-source.test.js.map +1 -0
  61. package/dist/__tests__/idea-todo-source.test.d.ts +2 -0
  62. package/dist/__tests__/idea-todo-source.test.d.ts.map +1 -0
  63. package/dist/__tests__/idea-todo-source.test.js +258 -0
  64. package/dist/__tests__/idea-todo-source.test.js.map +1 -0
  65. package/dist/__tests__/introspection-dedupe.test.d.ts +2 -0
  66. package/dist/__tests__/introspection-dedupe.test.d.ts.map +1 -0
  67. package/dist/__tests__/introspection-dedupe.test.js +484 -0
  68. package/dist/__tests__/introspection-dedupe.test.js.map +1 -0
  69. package/dist/__tests__/introspection-source.test.d.ts +2 -0
  70. package/dist/__tests__/introspection-source.test.d.ts.map +1 -0
  71. package/dist/__tests__/introspection-source.test.js +1051 -0
  72. package/dist/__tests__/introspection-source.test.js.map +1 -0
  73. package/dist/__tests__/migration-roles.test.js +1 -22
  74. package/dist/__tests__/migration-roles.test.js.map +1 -1
  75. package/dist/__tests__/reconcile-researching.test.d.ts +2 -0
  76. package/dist/__tests__/reconcile-researching.test.d.ts.map +1 -0
  77. package/dist/__tests__/reconcile-researching.test.js +224 -0
  78. package/dist/__tests__/reconcile-researching.test.js.map +1 -0
  79. package/dist/__tests__/role-loader-preamble-all.test.js +3 -1
  80. package/dist/__tests__/role-loader-preamble-all.test.js.map +1 -1
  81. package/dist/__tests__/role-loader.test.js +95 -0
  82. package/dist/__tests__/role-loader.test.js.map +1 -1
  83. package/dist/__tests__/role-prompt-no-legacy-protocol.test.js +3 -1
  84. package/dist/__tests__/role-prompt-no-legacy-protocol.test.js.map +1 -1
  85. package/dist/__tests__/secret-scrub.test.d.ts +2 -0
  86. package/dist/__tests__/secret-scrub.test.d.ts.map +1 -0
  87. package/dist/__tests__/secret-scrub.test.js +55 -0
  88. package/dist/__tests__/secret-scrub.test.js.map +1 -0
  89. package/dist/__tests__/shipment-actions.test.d.ts +2 -0
  90. package/dist/__tests__/shipment-actions.test.d.ts.map +1 -0
  91. package/dist/__tests__/shipment-actions.test.js +378 -0
  92. package/dist/__tests__/shipment-actions.test.js.map +1 -0
  93. package/dist/__tests__/shipment-persistence.test.d.ts +2 -0
  94. package/dist/__tests__/shipment-persistence.test.d.ts.map +1 -0
  95. package/dist/__tests__/shipment-persistence.test.js +120 -0
  96. package/dist/__tests__/shipment-persistence.test.js.map +1 -0
  97. package/dist/__tests__/shipment-pipeline.test.d.ts +2 -0
  98. package/dist/__tests__/shipment-pipeline.test.d.ts.map +1 -0
  99. package/dist/__tests__/shipment-pipeline.test.js +392 -0
  100. package/dist/__tests__/shipment-pipeline.test.js.map +1 -0
  101. package/dist/__tests__/shipment-report.test.d.ts +2 -0
  102. package/dist/__tests__/shipment-report.test.d.ts.map +1 -0
  103. package/dist/__tests__/shipment-report.test.js +78 -0
  104. package/dist/__tests__/shipment-report.test.js.map +1 -0
  105. package/dist/__tests__/shipment-stdin-integration.test.d.ts +2 -0
  106. package/dist/__tests__/shipment-stdin-integration.test.d.ts.map +1 -0
  107. package/dist/__tests__/shipment-stdin-integration.test.js +49 -0
  108. package/dist/__tests__/shipment-stdin-integration.test.js.map +1 -0
  109. package/dist/__tests__/shipment-type-parity.test.d.ts +2 -0
  110. package/dist/__tests__/shipment-type-parity.test.d.ts.map +1 -0
  111. package/dist/__tests__/shipment-type-parity.test.js +10 -0
  112. package/dist/__tests__/shipment-type-parity.test.js.map +1 -0
  113. package/dist/__tests__/snapshot-copy-provider.test.d.ts +2 -0
  114. package/dist/__tests__/snapshot-copy-provider.test.d.ts.map +1 -0
  115. package/dist/__tests__/snapshot-copy-provider.test.js +88 -0
  116. package/dist/__tests__/snapshot-copy-provider.test.js.map +1 -0
  117. package/dist/__tests__/swarm-coordinator-backend.test.js +153 -0
  118. package/dist/__tests__/swarm-coordinator-backend.test.js.map +1 -1
  119. package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts +2 -0
  120. package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts.map +1 -0
  121. package/dist/__tests__/swarm-coordinator-complete-intercept.test.js +111 -0
  122. package/dist/__tests__/swarm-coordinator-complete-intercept.test.js.map +1 -0
  123. package/dist/__tests__/task-store-source.test.d.ts +2 -0
  124. package/dist/__tests__/task-store-source.test.d.ts.map +1 -0
  125. package/dist/__tests__/task-store-source.test.js +56 -0
  126. package/dist/__tests__/task-store-source.test.js.map +1 -0
  127. package/dist/__tests__/transport-detect.test.d.ts +2 -0
  128. package/dist/__tests__/transport-detect.test.d.ts.map +1 -0
  129. package/dist/__tests__/transport-detect.test.js +92 -0
  130. package/dist/__tests__/transport-detect.test.js.map +1 -0
  131. package/dist/__tests__/workcycle-runner-cascade.test.d.ts +2 -0
  132. package/dist/__tests__/workcycle-runner-cascade.test.d.ts.map +1 -0
  133. package/dist/__tests__/workcycle-runner-cascade.test.js +203 -0
  134. package/dist/__tests__/workcycle-runner-cascade.test.js.map +1 -0
  135. package/dist/__tests__/workcycle-runner.test.d.ts +2 -0
  136. package/dist/__tests__/workcycle-runner.test.d.ts.map +1 -0
  137. package/dist/__tests__/workcycle-runner.test.js +369 -0
  138. package/dist/__tests__/workcycle-runner.test.js.map +1 -0
  139. package/dist/__tests__/workspace-diff.test.d.ts +2 -0
  140. package/dist/__tests__/workspace-diff.test.d.ts.map +1 -0
  141. package/dist/__tests__/workspace-diff.test.js +62 -0
  142. package/dist/__tests__/workspace-diff.test.js.map +1 -0
  143. package/dist/__tests__/workspace-manager.test.d.ts +2 -0
  144. package/dist/__tests__/workspace-manager.test.d.ts.map +1 -0
  145. package/dist/__tests__/workspace-manager.test.js +120 -0
  146. package/dist/__tests__/workspace-manager.test.js.map +1 -0
  147. package/dist/__tests__/workspace-types.test.d.ts +2 -0
  148. package/dist/__tests__/workspace-types.test.d.ts.map +1 -0
  149. package/dist/__tests__/workspace-types.test.js +37 -0
  150. package/dist/__tests__/workspace-types.test.js.map +1 -0
  151. package/dist/__tests__/worktree-gc.test.d.ts +2 -0
  152. package/dist/__tests__/worktree-gc.test.d.ts.map +1 -0
  153. package/dist/__tests__/worktree-gc.test.js +183 -0
  154. package/dist/__tests__/worktree-gc.test.js.map +1 -0
  155. package/dist/always-on/activity-reader.d.ts +27 -0
  156. package/dist/always-on/activity-reader.d.ts.map +1 -0
  157. package/dist/always-on/activity-reader.js +95 -0
  158. package/dist/always-on/activity-reader.js.map +1 -0
  159. package/dist/always-on/always-on-manager.d.ts +170 -0
  160. package/dist/always-on/always-on-manager.d.ts.map +1 -0
  161. package/dist/always-on/always-on-manager.js +538 -0
  162. package/dist/always-on/always-on-manager.js.map +1 -0
  163. package/dist/always-on/config.d.ts +141 -0
  164. package/dist/always-on/config.d.ts.map +1 -0
  165. package/dist/always-on/config.js +324 -0
  166. package/dist/always-on/config.js.map +1 -0
  167. package/dist/always-on/discovery-scheduler.d.ts +60 -0
  168. package/dist/always-on/discovery-scheduler.d.ts.map +1 -0
  169. package/dist/always-on/discovery-scheduler.js +287 -0
  170. package/dist/always-on/discovery-scheduler.js.map +1 -0
  171. package/dist/always-on/ideas-client.d.ts +23 -0
  172. package/dist/always-on/ideas-client.d.ts.map +1 -0
  173. package/dist/always-on/ideas-client.js +13 -0
  174. package/dist/always-on/ideas-client.js.map +1 -0
  175. package/dist/always-on/reconcile-researching.d.ts +42 -0
  176. package/dist/always-on/reconcile-researching.d.ts.map +1 -0
  177. package/dist/always-on/reconcile-researching.js +133 -0
  178. package/dist/always-on/reconcile-researching.js.map +1 -0
  179. package/dist/always-on/task-sources/cascade-picker.d.ts +42 -0
  180. package/dist/always-on/task-sources/cascade-picker.d.ts.map +1 -0
  181. package/dist/always-on/task-sources/cascade-picker.js +65 -0
  182. package/dist/always-on/task-sources/cascade-picker.js.map +1 -0
  183. package/dist/always-on/task-sources/idea-dedupe.d.ts +62 -0
  184. package/dist/always-on/task-sources/idea-dedupe.d.ts.map +1 -0
  185. package/dist/always-on/task-sources/idea-dedupe.js +130 -0
  186. package/dist/always-on/task-sources/idea-dedupe.js.map +1 -0
  187. package/dist/always-on/task-sources/idea-research-source.d.ts +46 -0
  188. package/dist/always-on/task-sources/idea-research-source.d.ts.map +1 -0
  189. package/dist/always-on/task-sources/idea-research-source.js +308 -0
  190. package/dist/always-on/task-sources/idea-research-source.js.map +1 -0
  191. package/dist/always-on/task-sources/idea-sort.d.ts +3 -0
  192. package/dist/always-on/task-sources/idea-sort.d.ts.map +1 -0
  193. package/dist/always-on/task-sources/idea-sort.js +25 -0
  194. package/dist/always-on/task-sources/idea-sort.js.map +1 -0
  195. package/dist/always-on/task-sources/idea-todo-source.d.ts +48 -0
  196. package/dist/always-on/task-sources/idea-todo-source.d.ts.map +1 -0
  197. package/dist/always-on/task-sources/idea-todo-source.js +226 -0
  198. package/dist/always-on/task-sources/idea-todo-source.js.map +1 -0
  199. package/dist/always-on/task-sources/introspection-source.d.ts +101 -0
  200. package/dist/always-on/task-sources/introspection-source.d.ts.map +1 -0
  201. package/dist/always-on/task-sources/introspection-source.js +695 -0
  202. package/dist/always-on/task-sources/introspection-source.js.map +1 -0
  203. package/dist/always-on/task-sources/task-store-source.d.ts +15 -0
  204. package/dist/always-on/task-sources/task-store-source.d.ts.map +1 -0
  205. package/dist/always-on/task-sources/task-store-source.js +59 -0
  206. package/dist/always-on/task-sources/task-store-source.js.map +1 -0
  207. package/dist/always-on/task-sources/types.d.ts +108 -0
  208. package/dist/always-on/task-sources/types.d.ts.map +1 -0
  209. package/dist/always-on/task-sources/types.js +13 -0
  210. package/dist/always-on/task-sources/types.js.map +1 -0
  211. package/dist/always-on/types.d.ts +76 -0
  212. package/dist/always-on/types.d.ts.map +1 -0
  213. package/dist/always-on/types.js +17 -0
  214. package/dist/always-on/types.js.map +1 -0
  215. package/dist/always-on/workcycle-runner.d.ts +115 -0
  216. package/dist/always-on/workcycle-runner.d.ts.map +1 -0
  217. package/dist/always-on/workcycle-runner.js +285 -0
  218. package/dist/always-on/workcycle-runner.js.map +1 -0
  219. package/dist/always-on/worktree-gc.d.ts +41 -0
  220. package/dist/always-on/worktree-gc.d.ts.map +1 -0
  221. package/dist/always-on/worktree-gc.js +167 -0
  222. package/dist/always-on/worktree-gc.js.map +1 -0
  223. package/dist/index.d.ts +26 -2
  224. package/dist/index.d.ts.map +1 -1
  225. package/dist/index.js +24 -1
  226. package/dist/index.js.map +1 -1
  227. package/dist/persistence.d.ts +37 -1
  228. package/dist/persistence.d.ts.map +1 -1
  229. package/dist/persistence.js +48 -0
  230. package/dist/persistence.js.map +1 -1
  231. package/dist/retrospective.d.ts.map +1 -1
  232. package/dist/retrospective.js +6 -0
  233. package/dist/retrospective.js.map +1 -1
  234. package/dist/roles/role-loader.d.ts +1 -1
  235. package/dist/roles/role-loader.d.ts.map +1 -1
  236. package/dist/roles/role-loader.js +18 -0
  237. package/dist/roles/role-loader.js.map +1 -1
  238. package/dist/roles/types.d.ts +12 -0
  239. package/dist/roles/types.d.ts.map +1 -1
  240. package/dist/shipment/gh-pr-creator.d.ts +28 -0
  241. package/dist/shipment/gh-pr-creator.d.ts.map +1 -0
  242. package/dist/shipment/gh-pr-creator.js +80 -0
  243. package/dist/shipment/gh-pr-creator.js.map +1 -0
  244. package/dist/shipment/report.d.ts +27 -0
  245. package/dist/shipment/report.d.ts.map +1 -0
  246. package/dist/shipment/report.js +41 -0
  247. package/dist/shipment/report.js.map +1 -0
  248. package/dist/shipment/secret-scrub.d.ts +12 -0
  249. package/dist/shipment/secret-scrub.d.ts.map +1 -0
  250. package/dist/shipment/secret-scrub.js +30 -0
  251. package/dist/shipment/secret-scrub.js.map +1 -0
  252. package/dist/shipment/shipment-actions.d.ts +85 -0
  253. package/dist/shipment/shipment-actions.d.ts.map +1 -0
  254. package/dist/shipment/shipment-actions.js +190 -0
  255. package/dist/shipment/shipment-actions.js.map +1 -0
  256. package/dist/shipment/shipment-pipeline.d.ts +48 -0
  257. package/dist/shipment/shipment-pipeline.d.ts.map +1 -0
  258. package/dist/shipment/shipment-pipeline.js +256 -0
  259. package/dist/shipment/shipment-pipeline.js.map +1 -0
  260. package/dist/shipment/transport-detect.d.ts +16 -0
  261. package/dist/shipment/transport-detect.d.ts.map +1 -0
  262. package/dist/shipment/transport-detect.js +54 -0
  263. package/dist/shipment/transport-detect.js.map +1 -0
  264. package/dist/shipment/workspace-diff.d.ts +39 -0
  265. package/dist/shipment/workspace-diff.d.ts.map +1 -0
  266. package/dist/shipment/workspace-diff.js +64 -0
  267. package/dist/shipment/workspace-diff.js.map +1 -0
  268. package/dist/swarm-coordinator.d.ts +20 -1
  269. package/dist/swarm-coordinator.d.ts.map +1 -1
  270. package/dist/swarm-coordinator.js +193 -10
  271. package/dist/swarm-coordinator.js.map +1 -1
  272. package/dist/types.d.ts +62 -0
  273. package/dist/types.d.ts.map +1 -1
  274. package/dist/workspace/git-worktree-provider.d.ts +11 -0
  275. package/dist/workspace/git-worktree-provider.d.ts.map +1 -0
  276. package/dist/workspace/git-worktree-provider.js +123 -0
  277. package/dist/workspace/git-worktree-provider.js.map +1 -0
  278. package/dist/workspace/gitignore-check.d.ts +10 -0
  279. package/dist/workspace/gitignore-check.d.ts.map +1 -0
  280. package/dist/workspace/gitignore-check.js +25 -0
  281. package/dist/workspace/gitignore-check.js.map +1 -0
  282. package/dist/workspace/index.d.ts +5 -0
  283. package/dist/workspace/index.d.ts.map +1 -0
  284. package/dist/workspace/index.js +5 -0
  285. package/dist/workspace/index.js.map +1 -0
  286. package/dist/workspace/snapshot-copy-provider.d.ts +11 -0
  287. package/dist/workspace/snapshot-copy-provider.d.ts.map +1 -0
  288. package/dist/workspace/snapshot-copy-provider.js +66 -0
  289. package/dist/workspace/snapshot-copy-provider.js.map +1 -0
  290. package/dist/workspace/types.d.ts +36 -0
  291. package/dist/workspace/types.d.ts.map +1 -0
  292. package/dist/workspace/types.js +2 -0
  293. package/dist/workspace/types.js.map +1 -0
  294. package/dist/workspace/workspace-manager.d.ts +30 -0
  295. package/dist/workspace/workspace-manager.d.ts.map +1 -0
  296. package/dist/workspace/workspace-manager.js +104 -0
  297. package/dist/workspace/workspace-manager.js.map +1 -0
  298. package/package.json +4 -4
  299. package/roles/queen.md +1 -0
  300. package/templates/introspection.md +64 -0
  301. package/templates/research-only.md +58 -0
  302. package/roles/preset-analyst-simons.md +0 -39
  303. package/roles/preset-architect-knuth.md +0 -39
  304. package/roles/preset-designer-norman.md +0 -39
  305. package/roles/preset-designer.md +0 -39
  306. package/roles/preset-dev-carmack.md +0 -39
  307. package/roles/preset-dev-gosling.md +0 -39
  308. package/roles/preset-developer.md +0 -52
  309. package/roles/preset-manager-grove.md +0 -39
  310. package/roles/preset-manager-musk.md +0 -39
  311. package/roles/preset-pm.md +0 -78
  312. package/roles/preset-researcher-feynman.md +0 -39
  313. package/roles/preset-reviewer.md +0 -46
  314. package/roles/preset-strategist-buffett.md +0 -39
  315. package/roles/preset-strategist-munger.md +0 -39
  316. package/roles/preset-strategist-sunzi.md +0 -39
  317. package/roles/preset-tester-beck.md +0 -40
  318. package/roles/preset-tester.md +0 -47
  319. package/roles/preset-writer-orwell.md +0 -39
  320. package/roles/preset-writer.md +0 -39
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Regex-based scrubber for common secret patterns. Applied to:
3
+ * - the diff/patch text sent to the LLM
4
+ * - the LLM's returned title/summary (LLM could re-emit a leaked secret)
5
+ * - the rendered report.md (which becomes the PR body)
6
+ *
7
+ * v1 is intentionally conservative: high-confidence patterns only. Real
8
+ * secret detection (entropy, false-positive suppression) is out of scope.
9
+ * Manual review is still required — the report disclaimer makes this explicit.
10
+ */
11
+ export declare function scrubSecrets(input: string): string;
12
+ //# sourceMappingURL=secret-scrub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-scrub.d.ts","sourceRoot":"","sources":["../../src/shipment/secret-scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAcH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKlD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Regex-based scrubber for common secret patterns. Applied to:
3
+ * - the diff/patch text sent to the LLM
4
+ * - the LLM's returned title/summary (LLM could re-emit a leaked secret)
5
+ * - the rendered report.md (which becomes the PR body)
6
+ *
7
+ * v1 is intentionally conservative: high-confidence patterns only. Real
8
+ * secret detection (entropy, false-positive suppression) is out of scope.
9
+ * Manual review is still required — the report disclaimer makes this explicit.
10
+ */
11
+ const PATTERNS = [
12
+ { name: "github-token", re: /gh[pousr]_[A-Za-z0-9]{36,}/g, replace: "[REDACTED-github-token]" },
13
+ { name: "slack-token", re: /xox[baprs]-[A-Za-z0-9-]{10,}/g, replace: "[REDACTED-slack-token]" },
14
+ { name: "openai-key", re: /sk-[A-Za-z0-9]{20,}/g, replace: "[REDACTED-openai-key]" },
15
+ { name: "aws-akia", re: /AKIA[0-9A-Z]{16}/g, replace: "[REDACTED-aws-akia]" },
16
+ {
17
+ name: "kv-assign",
18
+ re: /\b(KEY|TOKEN|SECRET|PASSWORD|PASSWD|API[_-]?KEY)\b\s*[=:]\s*["']?([A-Za-z0-9+/=_.-]{8,})["']?/gi,
19
+ replace: "$1=[REDACTED]",
20
+ },
21
+ ];
22
+ export function scrubSecrets(input) {
23
+ if (!input)
24
+ return input;
25
+ let out = input;
26
+ for (const p of PATTERNS)
27
+ out = out.replace(p.re, p.replace);
28
+ return out;
29
+ }
30
+ //# sourceMappingURL=secret-scrub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-scrub.js","sourceRoot":"","sources":["../../src/shipment/secret-scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,QAAQ,GAAyD;IACrE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,6BAA6B,EAAE,OAAO,EAAE,yBAAyB,EAAE;IAC/F,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,+BAA+B,EAAE,OAAO,EAAE,wBAAwB,EAAE;IAC/F,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,sBAAsB,EAAE,OAAO,EAAE,uBAAuB,EAAE;IACpF,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,mBAAmB,EAAE,OAAO,EAAE,qBAAqB,EAAE;IAC7E;QACE,IAAI,EAAE,WAAW;QACjB,EAAE,EAAE,iGAAiG;QACrG,OAAO,EAAE,eAAe;KACzB;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAC7D,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,85 @@
1
+ import type { CommandRunner } from "./transport-detect.js";
2
+ /**
3
+ * M6.2.3 — Shipment action handlers (Merge / Apply / Discard).
4
+ *
5
+ * Design (see docs/plans/2026-06-05-m6-2-ui-design.md §D5):
6
+ * - **Merge**: PURELY record-keeping. We do NOT call `gh pr merge` (Q1=A).
7
+ * User performs the merge on GitHub; we stamp `mergedAt` on shipment.json
8
+ * so the UI can render "merged on …" without re-asking.
9
+ * - **Apply**: `git -B clawnet-applied/<swarmId>` from the user's current
10
+ * HEAD on the project repo, then `git apply <diffPath>`. No auto-commit
11
+ * — user reviews/stages/commits themselves.
12
+ * - **Discard**: tear down the worktree (force) + delete the dedicated
13
+ * branch + remove shipment.json. UI is responsible for the double-confirm
14
+ * (Q2=A); by the time we're here the user has already opted in.
15
+ *
16
+ * All three are stateless w.r.t. `SwarmCoordinator` — they operate purely on
17
+ * the persisted `shipment.json` + `recovery.json` snapshots so they keep
18
+ * working after the live swarm has been GC'd.
19
+ */
20
+ export type ShipmentActionKind = "merge" | "apply" | "discard";
21
+ export interface ShipmentActionInput {
22
+ workDir: string;
23
+ swarmId: string;
24
+ /**
25
+ * Optional override for the workspace root used by `apply` and `discard`.
26
+ * Falls back to `loadSwarmSnapshot(...).workspace.metadata.repoRoot` (apply
27
+ * needs to checkout in the *parent* repo, not the worktree dir).
28
+ */
29
+ workspaceRepoRoot?: string;
30
+ /**
31
+ * Optional override for the worktree cwd (used by `discard` for
32
+ * `git worktree remove`). Falls back to snapshot.
33
+ */
34
+ workspaceCwd?: string;
35
+ /** Override for shipment.branchName (used by `discard`). */
36
+ branchName?: string;
37
+ /** Override for shipment.diffPath (used by `apply`). */
38
+ diffPath?: string;
39
+ /**
40
+ * Apply-only: bypass the "dirty working tree" and "branch already exists"
41
+ * safety checks. Default `false`. The hub plumbs this through from a
42
+ * `force=true` request param so a future UX layer (planned for M7) can
43
+ * surface a second confirmation before clobbering.
44
+ */
45
+ force?: boolean;
46
+ /** Test injection — git runner. Defaults to a spawn-based runner. */
47
+ runner?: CommandRunner;
48
+ }
49
+ export interface ShipmentActionResult {
50
+ ok: boolean;
51
+ message?: string;
52
+ /** Populated by `apply` on success — the branch the diff was applied onto. */
53
+ newBranchName?: string;
54
+ error?: string;
55
+ }
56
+ /**
57
+ * Apply: create a fresh `clawnet-applied/<swarmId>` branch in the project
58
+ * repo (not the worktree) and `git apply` the saved diff onto it. No commit
59
+ * is made — the user reviews + commits themselves.
60
+ *
61
+ * Uses `git checkout -B` so re-running apply doesn't leave a half-merged
62
+ * state behind; the branch is treated as ephemeral until the user commits.
63
+ */
64
+ export declare function applyShipment(input: ShipmentActionInput): Promise<ShipmentActionResult>;
65
+ /**
66
+ * Merge: record-only. We *don't* call `gh pr merge` — branch protection +
67
+ * token surface area + merge-strategy complexity make it unsafe to attempt
68
+ * server-side. Instead we stamp `mergedAt` on shipment.json so the UI can
69
+ * render "Merged on …" and stop showing the action button.
70
+ */
71
+ export declare function mergeShipment(input: ShipmentActionInput): Promise<ShipmentActionResult>;
72
+ /**
73
+ * Discard: tear down the worktree, force-delete the branch, and remove
74
+ * shipment.json so the UI stops surfacing the action row.
75
+ *
76
+ * Each step is best-effort — a missing worktree (already disposed) or branch
77
+ * (never created) is treated as a no-op, not a hard failure. Only the
78
+ * shipment.json removal is strictly required for the UI to consider this
79
+ * "done"; failure there surfaces as `ok: false`.
80
+ *
81
+ * UI is responsible for double-confirming with the user before calling this
82
+ * (per Q2=A) — by this point we assume the user knows what they're losing.
83
+ */
84
+ export declare function discardShipment(input: ShipmentActionInput): Promise<ShipmentActionResult>;
85
+ //# sourceMappingURL=shipment-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-actions.d.ts","sourceRoot":"","sources":["../../src/shipment/shipment-actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAK3D;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwDD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAuE/B;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAkB/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CA4D/B"}
@@ -0,0 +1,190 @@
1
+ import { existsSync, unlinkSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { projectRoot } from "@mclawnet/task";
5
+ import { createLogger } from "@mclawnet/logger";
6
+ import { loadShipmentResult, loadSwarmSnapshot, saveShipmentResult } from "../persistence.js";
7
+ const log = createLogger({ module: "swarm/shipment/actions" });
8
+ function getHome() {
9
+ return process.env.CLAWNET_HOME ?? homedir();
10
+ }
11
+ /** Default spawn-based git runner. Mirrors the one in shipment-pipeline. */
12
+ const defaultRunner = async (cmd, args, cwd) => {
13
+ const { spawn } = await import("node:child_process");
14
+ return new Promise((resolve) => {
15
+ const child = spawn(cmd, args, {
16
+ cwd,
17
+ stdio: ["ignore", "pipe", "pipe"],
18
+ });
19
+ let stdout = "";
20
+ let stderr = "";
21
+ child.stdout?.on("data", (c) => (stdout += c.toString("utf-8")));
22
+ child.stderr?.on("data", (c) => (stderr += c.toString("utf-8")));
23
+ child.on("error", (e) => resolve({ exitCode: -1, stdout, stderr: e.message }));
24
+ child.on("close", (code) => resolve({ exitCode: code ?? -1, stdout, stderr }));
25
+ });
26
+ };
27
+ /**
28
+ * Resolve workspace metadata for an action. Three lookup paths:
29
+ * 1. Caller-supplied overrides (`workspaceRepoRoot` / `workspaceCwd` /
30
+ * `branchName` / `diffPath`) — wins because the caller may already have
31
+ * the live SwarmInstance and we don't want to round-trip the disk.
32
+ * 2. `recovery.json` workspace block — present while the swarm hasn't been
33
+ * destroyed.
34
+ * 3. `shipment.json` `branchName` / `diffPath` — survives `runRetroAndCleanup`.
35
+ *
36
+ * We deliberately load *both* recovery and shipment because they answer
37
+ * different questions: shipment has the diff path + applied-branch label
38
+ * after teardown, but only recovery still knows where the worktree lived on
39
+ * disk for `discard`.
40
+ */
41
+ function resolveContext(input) {
42
+ const shipment = loadShipmentResult(input.workDir, input.swarmId);
43
+ const snap = loadSwarmSnapshot(input.workDir, input.swarmId);
44
+ const ws = snap?.workspace;
45
+ return {
46
+ shipment,
47
+ workspaceRepoRoot: input.workspaceRepoRoot ?? ws?.metadata.repoRoot,
48
+ workspaceCwd: input.workspaceCwd ?? ws?.cwd,
49
+ branchName: input.branchName ?? shipment?.branchName ?? ws?.metadata.branchName,
50
+ diffPath: input.diffPath ?? shipment?.diffPath,
51
+ };
52
+ }
53
+ /**
54
+ * Apply: create a fresh `clawnet-applied/<swarmId>` branch in the project
55
+ * repo (not the worktree) and `git apply` the saved diff onto it. No commit
56
+ * is made — the user reviews + commits themselves.
57
+ *
58
+ * Uses `git checkout -B` so re-running apply doesn't leave a half-merged
59
+ * state behind; the branch is treated as ephemeral until the user commits.
60
+ */
61
+ export async function applyShipment(input) {
62
+ const ctx = resolveContext(input);
63
+ if (!ctx.workspaceRepoRoot) {
64
+ return { ok: false, error: "no workspaceRepoRoot available (snapshot gone?)" };
65
+ }
66
+ if (!ctx.diffPath) {
67
+ return { ok: false, error: "no diffPath available (shipment strategy was not diff-only)" };
68
+ }
69
+ if (!existsSync(ctx.diffPath)) {
70
+ return { ok: false, error: `diff file not found on disk: ${ctx.diffPath}` };
71
+ }
72
+ const runner = input.runner ?? defaultRunner;
73
+ const newBranchName = `clawnet-applied/${input.swarmId}`;
74
+ // M6.2 hotfix I1: refuse to clobber unless `force` is set.
75
+ // (1) Dirty working tree → `git checkout -B` would carry the WIP onto the
76
+ // new branch (the user wouldn't notice until commit time). (2) An
77
+ // existing `clawnet-applied/<id>` branch may contain a previous apply +
78
+ // manual fixups the user committed — `-B` resets it.
79
+ if (!input.force) {
80
+ const status = await runner("git", ["-C", ctx.workspaceRepoRoot, "status", "--porcelain"]);
81
+ if (status.exitCode === 0 && status.stdout.trim().length > 0) {
82
+ return {
83
+ ok: false,
84
+ error: "working tree has uncommitted changes; commit or stash before applying (or pass force)",
85
+ };
86
+ }
87
+ const branchProbe = await runner("git", ["-C", ctx.workspaceRepoRoot, "rev-parse", "--verify", newBranchName]);
88
+ if (branchProbe.exitCode === 0) {
89
+ return {
90
+ ok: false,
91
+ error: `branch ${newBranchName} already exists; pass force to clobber`,
92
+ };
93
+ }
94
+ }
95
+ const checkout = await runner("git", ["-C", ctx.workspaceRepoRoot, "checkout", "-B", newBranchName]);
96
+ if (checkout.exitCode !== 0) {
97
+ return {
98
+ ok: false,
99
+ error: `git checkout failed: ${checkout.stderr.trim() || checkout.stdout.trim()}`,
100
+ };
101
+ }
102
+ const apply = await runner("git", ["-C", ctx.workspaceRepoRoot, "apply", ctx.diffPath]);
103
+ if (apply.exitCode !== 0) {
104
+ return {
105
+ ok: false,
106
+ error: `git apply failed: ${apply.stderr.trim() || apply.stdout.trim()}`,
107
+ };
108
+ }
109
+ log.info({ swarmId: input.swarmId, repoRoot: ctx.workspaceRepoRoot, newBranchName }, "shipment-actions: applied diff to new branch");
110
+ return {
111
+ ok: true,
112
+ newBranchName,
113
+ message: `applied diff to branch ${newBranchName}`,
114
+ };
115
+ }
116
+ /**
117
+ * Merge: record-only. We *don't* call `gh pr merge` — branch protection +
118
+ * token surface area + merge-strategy complexity make it unsafe to attempt
119
+ * server-side. Instead we stamp `mergedAt` on shipment.json so the UI can
120
+ * render "Merged on …" and stop showing the action button.
121
+ */
122
+ export async function mergeShipment(input) {
123
+ const ctx = resolveContext(input);
124
+ if (!ctx.shipment) {
125
+ return { ok: false, error: "no shipment.json found" };
126
+ }
127
+ if (ctx.shipment.strategy !== "pr" || !ctx.shipment.prUrl) {
128
+ return { ok: false, error: "merge requires strategy=pr with prUrl" };
129
+ }
130
+ const updated = {
131
+ ...ctx.shipment,
132
+ mergedAt: new Date().toISOString(),
133
+ };
134
+ saveShipmentResult(input.workDir, input.swarmId, updated);
135
+ log.info({ swarmId: input.swarmId, prUrl: ctx.shipment.prUrl }, "shipment-actions: recorded merge");
136
+ return { ok: true, message: "marked as merged" };
137
+ }
138
+ /**
139
+ * Discard: tear down the worktree, force-delete the branch, and remove
140
+ * shipment.json so the UI stops surfacing the action row.
141
+ *
142
+ * Each step is best-effort — a missing worktree (already disposed) or branch
143
+ * (never created) is treated as a no-op, not a hard failure. Only the
144
+ * shipment.json removal is strictly required for the UI to consider this
145
+ * "done"; failure there surfaces as `ok: false`.
146
+ *
147
+ * UI is responsible for double-confirming with the user before calling this
148
+ * (per Q2=A) — by this point we assume the user knows what they're losing.
149
+ */
150
+ export async function discardShipment(input) {
151
+ const ctx = resolveContext(input);
152
+ const runner = input.runner ?? defaultRunner;
153
+ const warnings = [];
154
+ if (ctx.workspaceRepoRoot && ctx.workspaceCwd) {
155
+ const wt = await runner("git", ["-C", ctx.workspaceRepoRoot, "worktree", "remove", "--force", ctx.workspaceCwd]);
156
+ if (wt.exitCode !== 0) {
157
+ // Worktree may already be gone (e.g. runRetroAndCleanup ran). Log + continue.
158
+ warnings.push(`worktree remove: ${wt.stderr.trim() || wt.stdout.trim()}`);
159
+ log.warn({ swarmId: input.swarmId, stderr: wt.stderr }, "shipment-actions: worktree remove failed (continuing)");
160
+ }
161
+ }
162
+ if (ctx.workspaceRepoRoot && ctx.branchName) {
163
+ const br = await runner("git", ["-C", ctx.workspaceRepoRoot, "branch", "-D", ctx.branchName]);
164
+ if (br.exitCode !== 0) {
165
+ warnings.push(`branch -D: ${br.stderr.trim() || br.stdout.trim()}`);
166
+ log.warn({ swarmId: input.swarmId, stderr: br.stderr }, "shipment-actions: branch delete failed (continuing)");
167
+ }
168
+ }
169
+ // Delete shipment.json — UI uses presence/absence of shipment to decide
170
+ // whether to render the WorkspaceBlock action row at all, so this is the
171
+ // observable signal that "discard happened".
172
+ const shipmentPath = join(projectRoot(input.workDir, getHome()), "swarms", input.swarmId, "shipment.json");
173
+ if (existsSync(shipmentPath)) {
174
+ try {
175
+ unlinkSync(shipmentPath);
176
+ }
177
+ catch (err) {
178
+ return {
179
+ ok: false,
180
+ error: `failed to delete shipment.json: ${err instanceof Error ? err.message : String(err)}`,
181
+ };
182
+ }
183
+ }
184
+ log.info({ swarmId: input.swarmId, warnings }, "shipment-actions: discarded shipment");
185
+ return {
186
+ ok: true,
187
+ message: warnings.length > 0 ? `discarded (warnings: ${warnings.join("; ")})` : "discarded",
188
+ };
189
+ }
190
+ //# sourceMappingURL=shipment-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-actions.js","sourceRoot":"","sources":["../../src/shipment/shipment-actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { SwarmInstance } from "../types.js";
2
+ import { type CommandRunnerWithStdin } from "./gh-pr-creator.js";
3
+ export interface ShipmentInput {
4
+ swarm: SwarmInstance;
5
+ /** Optional LLM caller for title/summary generation. Falls back to swarmId/diffStat if absent. */
6
+ generateText?: (prompt: string) => Promise<string>;
7
+ /**
8
+ * Test injection: command runner for git/gh/cp commands. Supports stdin so
9
+ * `gh pr create --body-file -` receives the rendered report body.
10
+ * For backwards compatibility, runners that only accept (cmd, args) or
11
+ * (cmd, args, cwd) work for non-stdin commands.
12
+ */
13
+ runner?: CommandRunnerWithStdin;
14
+ /** Test injection: file writer (for fallback diff.patch + report.md). */
15
+ writeFile?: (path: string, contents: string) => Promise<void>;
16
+ }
17
+ export interface ShipmentResult {
18
+ strategy: "pr" | "diff-only" | "skipped";
19
+ /** ISO timestamp when shipment ran. M6.2 UI uses this for display. */
20
+ createdAt: string;
21
+ /** PR title (LLM-generated or fallback). M6.2 UI surface. */
22
+ title?: string;
23
+ /** PR summary (first ~200 chars; full version lives in report.md). M6.2 UI surface. */
24
+ summary?: string;
25
+ prUrl?: string;
26
+ branchName?: string;
27
+ diffPath?: string;
28
+ reportPath?: string;
29
+ /** Populated when strategy=diff-only because a PR attempt failed; carries the gh error. */
30
+ prAttemptError?: string;
31
+ error?: string;
32
+ /**
33
+ * ISO timestamp stamped by `mergeShipment()` when the user marks the PR as
34
+ * merged. Lives on the on-disk JSON; M6.3's history view surfaces it as the
35
+ * "Merged" filter. Optional because most shipments are never marked merged
36
+ * and pre-M6.2.3 shipment.json files don't have the field at all.
37
+ */
38
+ mergedAt?: string;
39
+ }
40
+ /**
41
+ * Stdin-aware default runner. When `opts.stdin` is set, opens a stdin pipe so
42
+ * `gh pr create --body-file -` can read the rendered report body. Mirrors
43
+ * gh-pr-creator's own defaultRunner so production wiring actually delivers
44
+ * stdin to subprocesses.
45
+ */
46
+ export declare const defaultRunnerWithStdin: CommandRunnerWithStdin;
47
+ export declare function runShipment(input: ShipmentInput): Promise<ShipmentResult>;
48
+ //# sourceMappingURL=shipment-pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment-pipeline.d.ts","sourceRoot":"","sources":["../../src/shipment/shipment-pipeline.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,OAAO,EAAiB,KAAK,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AA+BhF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,aAAa,CAAC;IACrB,kGAAkG;IAClG,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD;;;;;OAKG;IACH,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,yEAAyE;IACzE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,IAAI,GAAG,WAAW,GAAG,SAAS,CAAC;IACzC,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AAKH,eAAO,MAAM,sBAAsB,EAAE,sBAwBpC,CAAC;AAEF,wBAAsB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CA8L/E"}
@@ -0,0 +1,256 @@
1
+ import { writeFile as fsWriteFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { createLogger } from "@mclawnet/logger";
4
+ import { computeWorkspaceDiff } from "./workspace-diff.js";
5
+ import { renderReport } from "./report.js";
6
+ import { detectTransport } from "./transport-detect.js";
7
+ import { createDraftPR } from "./gh-pr-creator.js";
8
+ import { scrubSecrets } from "./secret-scrub.js";
9
+ const log = createLogger({ module: "swarm/shipment/pipeline" });
10
+ /**
11
+ * Auto-commit identity. Surfaces in `git log`/`git blame` so reviewers can
12
+ * distinguish swarm-generated commits from human work without inspecting
13
+ * the message body.
14
+ */
15
+ const COMMIT_AUTHOR_NAME = "ClawNet Swarm";
16
+ const COMMIT_AUTHOR_EMAIL = "swarm@clawnet.local";
17
+ /** GitHub caps PR bodies near 65KB. 60KB leaves headroom for trailing markers. */
18
+ const MAX_PR_BODY = 60_000;
19
+ function commitArgs(message, extra = []) {
20
+ return [
21
+ "-c",
22
+ `user.name=${COMMIT_AUTHOR_NAME}`,
23
+ "-c",
24
+ `user.email=${COMMIT_AUTHOR_EMAIL}`,
25
+ "commit",
26
+ "--author",
27
+ `${COMMIT_AUTHOR_NAME} <${COMMIT_AUTHOR_EMAIL}>`,
28
+ ...extra,
29
+ "-m",
30
+ message,
31
+ ];
32
+ }
33
+ /**
34
+ * Stdin-aware default runner. When `opts.stdin` is set, opens a stdin pipe so
35
+ * `gh pr create --body-file -` can read the rendered report body. Mirrors
36
+ * gh-pr-creator's own defaultRunner so production wiring actually delivers
37
+ * stdin to subprocesses.
38
+ */
39
+ // Exported (not `const` private) so the EPIPE regression suite can pin the
40
+ // stdin error-listener behavior against the real runner instead of an inline
41
+ // copy. Default-runner overrides for tests still flow through
42
+ // `ShipmentInput.runner`; only the test layer touches this export directly.
43
+ export const defaultRunnerWithStdin = async (cmd, args, opts) => {
44
+ const { spawn } = await import("node:child_process");
45
+ return new Promise((resolve) => {
46
+ const child = spawn(cmd, args, {
47
+ cwd: opts.cwd,
48
+ stdio: [opts.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
49
+ });
50
+ let stdout = "";
51
+ let stderr = "";
52
+ child.stdout?.on("data", (c) => (stdout += c.toString("utf-8")));
53
+ child.stderr?.on("data", (c) => (stderr += c.toString("utf-8")));
54
+ child.on("error", (e) => resolve({ exitCode: -1, stdout, stderr: e.message }));
55
+ child.on("close", (code) => resolve({ exitCode: code ?? -1, stdout, stderr }));
56
+ if (opts.stdin !== undefined && child.stdin) {
57
+ // EPIPE safety (matches claude-process PR #181 pattern): if the child
58
+ // dies before we finish writing, stdin emits 'error' and — without a
59
+ // listener — Node crashes the agent process. Route to the same resolve
60
+ // path as spawn errors. Promise dedupes naturally so a later close()
61
+ // resolve() is a no-op.
62
+ child.stdin.on("error", (e) => resolve({ exitCode: -1, stdout, stderr: e.message }));
63
+ child.stdin.write(opts.stdin);
64
+ child.stdin.end();
65
+ }
66
+ });
67
+ };
68
+ export async function runShipment(input) {
69
+ const createdAt = new Date().toISOString();
70
+ try {
71
+ const swarm = input.swarm;
72
+ const ws = swarm.workspace;
73
+ if (!ws)
74
+ return { strategy: "skipped", createdAt, error: "no workspace" };
75
+ if (ws.strategy !== "git-worktree") {
76
+ return {
77
+ strategy: "skipped",
78
+ createdAt,
79
+ error: `${ws.strategy} not supported in M6.1 (git-worktree only)`,
80
+ };
81
+ }
82
+ const runner = input.runner ?? defaultRunnerWithStdin;
83
+ // Facade for code paths that historically used (cmd, args, cwd) — drops stdin.
84
+ const runnerNoStdin = (cmd, args, cwd) => runner(cmd, args, { cwd });
85
+ const writeFile = input.writeFile ??
86
+ (async (p, c) => {
87
+ await fsWriteFile(p, c, "utf-8");
88
+ });
89
+ // 2. Auto-commit any uncommitted changes
90
+ await runnerNoStdin("git", ["add", "-A"], ws.cwd).catch(() => null);
91
+ const status = await runnerNoStdin("git", ["status", "--porcelain"], ws.cwd).catch(() => null);
92
+ if (status && status.exitCode === 0 && status.stdout.trim().length > 0) {
93
+ // Observability for S4: surface the file list so users can spot junk
94
+ // (e.g. .DS_Store, editor swap files) that crept in via `git add -A`.
95
+ // Not filtered here — opt-in filtering would need user config.
96
+ log.info({
97
+ swarmId: swarm.id,
98
+ files: status.stdout.trim().split("\n").slice(0, 50),
99
+ }, "shipment: about to auto-commit changes");
100
+ const commitMsg = `swarm output: ${swarm.id}`;
101
+ let commit = await runnerNoStdin("git", commitArgs(commitMsg), ws.cwd).catch(() => null);
102
+ if (!commit || commit.exitCode !== 0) {
103
+ log.warn({ swarmId: swarm.id, stderr: commit?.stderr }, "shipment: git commit failed (likely pre-commit hook); retrying with --no-verify");
104
+ commit = await runnerNoStdin("git", commitArgs(`${commitMsg} (no-verify after hook failure)`, ["--no-verify"]), ws.cwd).catch(() => null);
105
+ if (!commit || commit.exitCode !== 0) {
106
+ log.warn({ swarmId: swarm.id, stderr: commit?.stderr }, "shipment: git commit --no-verify also failed; continuing with HEAD as-is");
107
+ }
108
+ }
109
+ }
110
+ // 3. Diff
111
+ const diff = await computeWorkspaceDiff({
112
+ worktreeCwd: ws.cwd,
113
+ baseCommit: ws.metadata.baseCommit,
114
+ runner: runnerNoStdin,
115
+ });
116
+ if (diff.error)
117
+ return { strategy: "skipped", createdAt, error: diff.error };
118
+ if (diff.files.length === 0)
119
+ return { strategy: "skipped", createdAt, error: "no changes" };
120
+ // Scrub secrets from diff text BEFORE it reaches the LLM or report. Common
121
+ // patterns only (GitHub/Slack/OpenAI/AWS tokens + KEY=value lines); manual
122
+ // review is still required and the report makes this explicit.
123
+ const scrubbedDiff = {
124
+ ...diff,
125
+ stat: scrubSecrets(diff.stat),
126
+ patch: scrubSecrets(diff.patch),
127
+ };
128
+ // 4. LLM title/summary or fallback
129
+ let title = `swarm: ${swarm.id}`;
130
+ let summary = `Swarm produced ${scrubbedDiff.files.length} file change(s).\n\n${scrubbedDiff.stat}`;
131
+ if (input.generateText) {
132
+ try {
133
+ const t = (await input.generateText(buildTitlePrompt(swarm, scrubbedDiff)))
134
+ .trim()
135
+ .split("\n")[0];
136
+ if (t)
137
+ title = t;
138
+ const s = (await input.generateText(buildSummaryPrompt(swarm, scrubbedDiff))).trim();
139
+ if (s)
140
+ summary = s;
141
+ }
142
+ catch (err) {
143
+ log.warn({ err, swarmId: swarm.id }, "shipment: LLM generateText failed; using fallback");
144
+ }
145
+ }
146
+ // Re-scrub LLM output: model could re-emit a leaked secret it saw in the diff.
147
+ title = scrubSecrets(title);
148
+ summary = scrubSecrets(summary);
149
+ // 5. Render report
150
+ const report = renderReport({
151
+ swarmId: swarm.id,
152
+ swarmDisplayName: swarm.displayName,
153
+ teamName: swarm.teamName ?? "default",
154
+ baseBranch: ws.metadata.baseBranch ?? "main",
155
+ baseCommit: ws.metadata.baseCommit,
156
+ branchName: ws.metadata.branchName,
157
+ startedAt: getStartedAt(swarm, createdAt),
158
+ endedAt: createdAt,
159
+ status: mapStatus(swarm.status),
160
+ diffStat: scrubbedDiff.stat,
161
+ changedFiles: scrubbedDiff.files,
162
+ title,
163
+ summary,
164
+ roles: [...swarm.roles.values()].map((r) => ({
165
+ instanceId: r.instanceId,
166
+ roleName: r.roleName,
167
+ })),
168
+ });
169
+ // 6. Transport
170
+ const transport = await detectTransport(ws.cwd, { runner: runnerNoStdin });
171
+ const branchName = ws.metadata.branchName;
172
+ // 7. PR path
173
+ let prAttemptError;
174
+ if (transport.kind === "pr") {
175
+ // GitHub caps PR bodies around 65KB. Truncate with a clear pointer to
176
+ // the full report on disk so reviewers know nothing was silently dropped.
177
+ let body = report;
178
+ if (body.length > MAX_PR_BODY) {
179
+ body =
180
+ body.substring(0, MAX_PR_BODY) +
181
+ "\n\n... (truncated — full report at `report.md` in the worktree)\n";
182
+ }
183
+ const pr = await createDraftPR({
184
+ cwd: ws.cwd,
185
+ branchName,
186
+ baseBranch: transport.defaultBranch ?? "main",
187
+ title,
188
+ bodyMarkdown: body,
189
+ }, { runner });
190
+ if (pr.ok) {
191
+ log.info({ swarmId: swarm.id, prUrl: pr.url }, "shipment: PR created");
192
+ return {
193
+ strategy: "pr",
194
+ createdAt,
195
+ title,
196
+ summary: summary.substring(0, 200),
197
+ prUrl: pr.url,
198
+ branchName,
199
+ };
200
+ }
201
+ prAttemptError = `${pr.step}: ${pr.error}`;
202
+ log.warn({ swarmId: swarm.id, step: pr.step, err: pr.error }, "shipment: PR creation failed; falling back to diff-only");
203
+ // fall through
204
+ }
205
+ // 8. Diff-only fallback
206
+ const diffPath = join(ws.cwd, "diff.patch");
207
+ const reportPath = join(ws.cwd, "report.md");
208
+ await writeFile(diffPath, scrubbedDiff.patch);
209
+ await writeFile(reportPath, report);
210
+ log.info({ swarmId: swarm.id, diffPath, reportPath }, "shipment: diff+report written to worktree");
211
+ return {
212
+ strategy: "diff-only",
213
+ createdAt,
214
+ title,
215
+ summary: summary.substring(0, 200),
216
+ diffPath,
217
+ reportPath,
218
+ branchName,
219
+ prAttemptError,
220
+ };
221
+ }
222
+ catch (err) {
223
+ log.warn({ err, swarmId: input.swarm.id }, "shipment: unexpected failure");
224
+ return { strategy: "skipped", createdAt, error: err?.message ?? String(err) };
225
+ }
226
+ }
227
+ function buildTitlePrompt(swarm, diff) {
228
+ return (`You are summarizing a swarm's work as a one-line PR title.\n\n` +
229
+ `Swarm id: ${swarm.id}\n` +
230
+ `Files changed:\n${diff.stat}\n\n` +
231
+ `Output a single short title (max 60 chars), no quotes, no trailing period.`);
232
+ }
233
+ function buildSummaryPrompt(swarm, diff) {
234
+ return (`Write a 1-3 paragraph summary of what this swarm accomplished, suitable for a PR description.\n\n` +
235
+ `Swarm id: ${swarm.id}\n` +
236
+ `Files changed:\n${diff.stat}\n\n` +
237
+ `Diff (truncated to 4000 chars):\n${diff.patch.substring(0, 4000)}\n\n` +
238
+ `Write the summary in markdown. Do not include a heading.`);
239
+ }
240
+ function getStartedAt(swarm, fallback) {
241
+ const candidate = swarm.createdAt;
242
+ if (typeof candidate === "string")
243
+ return candidate;
244
+ if (typeof candidate === "number")
245
+ return new Date(candidate).toISOString();
246
+ // No createdAt → use endedAt instead of 1970-01-01 epoch zero (cosmetic fix).
247
+ return fallback;
248
+ }
249
+ function mapStatus(s) {
250
+ if (s === "completed")
251
+ return "completed";
252
+ if (s === "failed")
253
+ return "failed";
254
+ return "cancelled";
255
+ }
256
+ //# sourceMappingURL=shipment-pipeline.js.map