@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.
- package/dist/__tests__/always-on-activity-reader.test.d.ts +2 -0
- package/dist/__tests__/always-on-activity-reader.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-activity-reader.test.js +193 -0
- package/dist/__tests__/always-on-activity-reader.test.js.map +1 -0
- package/dist/__tests__/always-on-config.test.d.ts +2 -0
- package/dist/__tests__/always-on-config.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-config.test.js +285 -0
- package/dist/__tests__/always-on-config.test.js.map +1 -0
- package/dist/__tests__/always-on-manager.test.d.ts +2 -0
- package/dist/__tests__/always-on-manager.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-manager.test.js +797 -0
- package/dist/__tests__/always-on-manager.test.js.map +1 -0
- package/dist/__tests__/always-on-parity.test.d.ts +2 -0
- package/dist/__tests__/always-on-parity.test.d.ts.map +1 -0
- package/dist/__tests__/always-on-parity.test.js +20 -0
- package/dist/__tests__/always-on-parity.test.js.map +1 -0
- package/dist/__tests__/cascade-picker.test.d.ts +2 -0
- package/dist/__tests__/cascade-picker.test.d.ts.map +1 -0
- package/dist/__tests__/cascade-picker.test.js +122 -0
- package/dist/__tests__/cascade-picker.test.js.map +1 -0
- package/dist/__tests__/coordinator-shipment.test.d.ts +2 -0
- package/dist/__tests__/coordinator-shipment.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-shipment.test.js +280 -0
- package/dist/__tests__/coordinator-shipment.test.js.map +1 -0
- package/dist/__tests__/coordinator-workspace-recover.test.d.ts +2 -0
- package/dist/__tests__/coordinator-workspace-recover.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-workspace-recover.test.js +140 -0
- package/dist/__tests__/coordinator-workspace-recover.test.js.map +1 -0
- package/dist/__tests__/coordinator-workspace.test.d.ts +2 -0
- package/dist/__tests__/coordinator-workspace.test.d.ts.map +1 -0
- package/dist/__tests__/coordinator-workspace.test.js +135 -0
- package/dist/__tests__/coordinator-workspace.test.js.map +1 -0
- package/dist/__tests__/default-runner-epipe.test.d.ts +2 -0
- package/dist/__tests__/default-runner-epipe.test.d.ts.map +1 -0
- package/dist/__tests__/default-runner-epipe.test.js +43 -0
- package/dist/__tests__/default-runner-epipe.test.js.map +1 -0
- package/dist/__tests__/discovery-scheduler.test.d.ts +2 -0
- package/dist/__tests__/discovery-scheduler.test.d.ts.map +1 -0
- package/dist/__tests__/discovery-scheduler.test.js +367 -0
- package/dist/__tests__/discovery-scheduler.test.js.map +1 -0
- package/dist/__tests__/env-forward-e2e.test.d.ts +2 -0
- package/dist/__tests__/env-forward-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/env-forward-e2e.test.js +57 -0
- package/dist/__tests__/env-forward-e2e.test.js.map +1 -0
- package/dist/__tests__/gh-pr-creator.test.d.ts +2 -0
- package/dist/__tests__/gh-pr-creator.test.d.ts.map +1 -0
- package/dist/__tests__/gh-pr-creator.test.js +107 -0
- package/dist/__tests__/gh-pr-creator.test.js.map +1 -0
- package/dist/__tests__/git-worktree-provider.test.d.ts +2 -0
- package/dist/__tests__/git-worktree-provider.test.d.ts.map +1 -0
- package/dist/__tests__/git-worktree-provider.test.js +98 -0
- package/dist/__tests__/git-worktree-provider.test.js.map +1 -0
- package/dist/__tests__/gitignore-check.test.d.ts +2 -0
- package/dist/__tests__/gitignore-check.test.d.ts.map +1 -0
- package/dist/__tests__/gitignore-check.test.js +39 -0
- package/dist/__tests__/gitignore-check.test.js.map +1 -0
- package/dist/__tests__/idea-research-source.test.d.ts +2 -0
- package/dist/__tests__/idea-research-source.test.d.ts.map +1 -0
- package/dist/__tests__/idea-research-source.test.js +425 -0
- package/dist/__tests__/idea-research-source.test.js.map +1 -0
- package/dist/__tests__/idea-todo-source.test.d.ts +2 -0
- package/dist/__tests__/idea-todo-source.test.d.ts.map +1 -0
- package/dist/__tests__/idea-todo-source.test.js +258 -0
- package/dist/__tests__/idea-todo-source.test.js.map +1 -0
- package/dist/__tests__/introspection-dedupe.test.d.ts +2 -0
- package/dist/__tests__/introspection-dedupe.test.d.ts.map +1 -0
- package/dist/__tests__/introspection-dedupe.test.js +484 -0
- package/dist/__tests__/introspection-dedupe.test.js.map +1 -0
- package/dist/__tests__/introspection-source.test.d.ts +2 -0
- package/dist/__tests__/introspection-source.test.d.ts.map +1 -0
- package/dist/__tests__/introspection-source.test.js +1051 -0
- package/dist/__tests__/introspection-source.test.js.map +1 -0
- package/dist/__tests__/migration-roles.test.js +1 -22
- package/dist/__tests__/migration-roles.test.js.map +1 -1
- package/dist/__tests__/reconcile-researching.test.d.ts +2 -0
- package/dist/__tests__/reconcile-researching.test.d.ts.map +1 -0
- package/dist/__tests__/reconcile-researching.test.js +224 -0
- package/dist/__tests__/reconcile-researching.test.js.map +1 -0
- package/dist/__tests__/role-loader-preamble-all.test.js +3 -1
- package/dist/__tests__/role-loader-preamble-all.test.js.map +1 -1
- package/dist/__tests__/role-loader.test.js +95 -0
- package/dist/__tests__/role-loader.test.js.map +1 -1
- package/dist/__tests__/role-prompt-no-legacy-protocol.test.js +3 -1
- package/dist/__tests__/role-prompt-no-legacy-protocol.test.js.map +1 -1
- package/dist/__tests__/secret-scrub.test.d.ts +2 -0
- package/dist/__tests__/secret-scrub.test.d.ts.map +1 -0
- package/dist/__tests__/secret-scrub.test.js +55 -0
- package/dist/__tests__/secret-scrub.test.js.map +1 -0
- package/dist/__tests__/shipment-actions.test.d.ts +2 -0
- package/dist/__tests__/shipment-actions.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-actions.test.js +378 -0
- package/dist/__tests__/shipment-actions.test.js.map +1 -0
- package/dist/__tests__/shipment-persistence.test.d.ts +2 -0
- package/dist/__tests__/shipment-persistence.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-persistence.test.js +120 -0
- package/dist/__tests__/shipment-persistence.test.js.map +1 -0
- package/dist/__tests__/shipment-pipeline.test.d.ts +2 -0
- package/dist/__tests__/shipment-pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-pipeline.test.js +392 -0
- package/dist/__tests__/shipment-pipeline.test.js.map +1 -0
- package/dist/__tests__/shipment-report.test.d.ts +2 -0
- package/dist/__tests__/shipment-report.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-report.test.js +78 -0
- package/dist/__tests__/shipment-report.test.js.map +1 -0
- package/dist/__tests__/shipment-stdin-integration.test.d.ts +2 -0
- package/dist/__tests__/shipment-stdin-integration.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-stdin-integration.test.js +49 -0
- package/dist/__tests__/shipment-stdin-integration.test.js.map +1 -0
- package/dist/__tests__/shipment-type-parity.test.d.ts +2 -0
- package/dist/__tests__/shipment-type-parity.test.d.ts.map +1 -0
- package/dist/__tests__/shipment-type-parity.test.js +10 -0
- package/dist/__tests__/shipment-type-parity.test.js.map +1 -0
- package/dist/__tests__/snapshot-copy-provider.test.d.ts +2 -0
- package/dist/__tests__/snapshot-copy-provider.test.d.ts.map +1 -0
- package/dist/__tests__/snapshot-copy-provider.test.js +88 -0
- package/dist/__tests__/snapshot-copy-provider.test.js.map +1 -0
- package/dist/__tests__/swarm-coordinator-backend.test.js +153 -0
- package/dist/__tests__/swarm-coordinator-backend.test.js.map +1 -1
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts +2 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.d.ts.map +1 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.js +111 -0
- package/dist/__tests__/swarm-coordinator-complete-intercept.test.js.map +1 -0
- package/dist/__tests__/task-store-source.test.d.ts +2 -0
- package/dist/__tests__/task-store-source.test.d.ts.map +1 -0
- package/dist/__tests__/task-store-source.test.js +56 -0
- package/dist/__tests__/task-store-source.test.js.map +1 -0
- package/dist/__tests__/transport-detect.test.d.ts +2 -0
- package/dist/__tests__/transport-detect.test.d.ts.map +1 -0
- package/dist/__tests__/transport-detect.test.js +92 -0
- package/dist/__tests__/transport-detect.test.js.map +1 -0
- package/dist/__tests__/workcycle-runner-cascade.test.d.ts +2 -0
- package/dist/__tests__/workcycle-runner-cascade.test.d.ts.map +1 -0
- package/dist/__tests__/workcycle-runner-cascade.test.js +203 -0
- package/dist/__tests__/workcycle-runner-cascade.test.js.map +1 -0
- package/dist/__tests__/workcycle-runner.test.d.ts +2 -0
- package/dist/__tests__/workcycle-runner.test.d.ts.map +1 -0
- package/dist/__tests__/workcycle-runner.test.js +369 -0
- package/dist/__tests__/workcycle-runner.test.js.map +1 -0
- package/dist/__tests__/workspace-diff.test.d.ts +2 -0
- package/dist/__tests__/workspace-diff.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-diff.test.js +62 -0
- package/dist/__tests__/workspace-diff.test.js.map +1 -0
- package/dist/__tests__/workspace-manager.test.d.ts +2 -0
- package/dist/__tests__/workspace-manager.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-manager.test.js +120 -0
- package/dist/__tests__/workspace-manager.test.js.map +1 -0
- package/dist/__tests__/workspace-types.test.d.ts +2 -0
- package/dist/__tests__/workspace-types.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-types.test.js +37 -0
- package/dist/__tests__/workspace-types.test.js.map +1 -0
- package/dist/__tests__/worktree-gc.test.d.ts +2 -0
- package/dist/__tests__/worktree-gc.test.d.ts.map +1 -0
- package/dist/__tests__/worktree-gc.test.js +183 -0
- package/dist/__tests__/worktree-gc.test.js.map +1 -0
- package/dist/always-on/activity-reader.d.ts +27 -0
- package/dist/always-on/activity-reader.d.ts.map +1 -0
- package/dist/always-on/activity-reader.js +95 -0
- package/dist/always-on/activity-reader.js.map +1 -0
- package/dist/always-on/always-on-manager.d.ts +170 -0
- package/dist/always-on/always-on-manager.d.ts.map +1 -0
- package/dist/always-on/always-on-manager.js +538 -0
- package/dist/always-on/always-on-manager.js.map +1 -0
- package/dist/always-on/config.d.ts +141 -0
- package/dist/always-on/config.d.ts.map +1 -0
- package/dist/always-on/config.js +324 -0
- package/dist/always-on/config.js.map +1 -0
- package/dist/always-on/discovery-scheduler.d.ts +60 -0
- package/dist/always-on/discovery-scheduler.d.ts.map +1 -0
- package/dist/always-on/discovery-scheduler.js +287 -0
- package/dist/always-on/discovery-scheduler.js.map +1 -0
- package/dist/always-on/ideas-client.d.ts +23 -0
- package/dist/always-on/ideas-client.d.ts.map +1 -0
- package/dist/always-on/ideas-client.js +13 -0
- package/dist/always-on/ideas-client.js.map +1 -0
- package/dist/always-on/reconcile-researching.d.ts +42 -0
- package/dist/always-on/reconcile-researching.d.ts.map +1 -0
- package/dist/always-on/reconcile-researching.js +133 -0
- package/dist/always-on/reconcile-researching.js.map +1 -0
- package/dist/always-on/task-sources/cascade-picker.d.ts +42 -0
- package/dist/always-on/task-sources/cascade-picker.d.ts.map +1 -0
- package/dist/always-on/task-sources/cascade-picker.js +65 -0
- package/dist/always-on/task-sources/cascade-picker.js.map +1 -0
- package/dist/always-on/task-sources/idea-dedupe.d.ts +62 -0
- package/dist/always-on/task-sources/idea-dedupe.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-dedupe.js +130 -0
- package/dist/always-on/task-sources/idea-dedupe.js.map +1 -0
- package/dist/always-on/task-sources/idea-research-source.d.ts +46 -0
- package/dist/always-on/task-sources/idea-research-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-research-source.js +308 -0
- package/dist/always-on/task-sources/idea-research-source.js.map +1 -0
- package/dist/always-on/task-sources/idea-sort.d.ts +3 -0
- package/dist/always-on/task-sources/idea-sort.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-sort.js +25 -0
- package/dist/always-on/task-sources/idea-sort.js.map +1 -0
- package/dist/always-on/task-sources/idea-todo-source.d.ts +48 -0
- package/dist/always-on/task-sources/idea-todo-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/idea-todo-source.js +226 -0
- package/dist/always-on/task-sources/idea-todo-source.js.map +1 -0
- package/dist/always-on/task-sources/introspection-source.d.ts +101 -0
- package/dist/always-on/task-sources/introspection-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/introspection-source.js +695 -0
- package/dist/always-on/task-sources/introspection-source.js.map +1 -0
- package/dist/always-on/task-sources/task-store-source.d.ts +15 -0
- package/dist/always-on/task-sources/task-store-source.d.ts.map +1 -0
- package/dist/always-on/task-sources/task-store-source.js +59 -0
- package/dist/always-on/task-sources/task-store-source.js.map +1 -0
- package/dist/always-on/task-sources/types.d.ts +108 -0
- package/dist/always-on/task-sources/types.d.ts.map +1 -0
- package/dist/always-on/task-sources/types.js +13 -0
- package/dist/always-on/task-sources/types.js.map +1 -0
- package/dist/always-on/types.d.ts +76 -0
- package/dist/always-on/types.d.ts.map +1 -0
- package/dist/always-on/types.js +17 -0
- package/dist/always-on/types.js.map +1 -0
- package/dist/always-on/workcycle-runner.d.ts +115 -0
- package/dist/always-on/workcycle-runner.d.ts.map +1 -0
- package/dist/always-on/workcycle-runner.js +285 -0
- package/dist/always-on/workcycle-runner.js.map +1 -0
- package/dist/always-on/worktree-gc.d.ts +41 -0
- package/dist/always-on/worktree-gc.d.ts.map +1 -0
- package/dist/always-on/worktree-gc.js +167 -0
- package/dist/always-on/worktree-gc.js.map +1 -0
- package/dist/index.d.ts +26 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/persistence.d.ts +37 -1
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +48 -0
- package/dist/persistence.js.map +1 -1
- package/dist/retrospective.d.ts.map +1 -1
- package/dist/retrospective.js +6 -0
- package/dist/retrospective.js.map +1 -1
- package/dist/roles/role-loader.d.ts +1 -1
- package/dist/roles/role-loader.d.ts.map +1 -1
- package/dist/roles/role-loader.js +18 -0
- package/dist/roles/role-loader.js.map +1 -1
- package/dist/roles/types.d.ts +12 -0
- package/dist/roles/types.d.ts.map +1 -1
- package/dist/shipment/gh-pr-creator.d.ts +28 -0
- package/dist/shipment/gh-pr-creator.d.ts.map +1 -0
- package/dist/shipment/gh-pr-creator.js +80 -0
- package/dist/shipment/gh-pr-creator.js.map +1 -0
- package/dist/shipment/report.d.ts +27 -0
- package/dist/shipment/report.d.ts.map +1 -0
- package/dist/shipment/report.js +41 -0
- package/dist/shipment/report.js.map +1 -0
- package/dist/shipment/secret-scrub.d.ts +12 -0
- package/dist/shipment/secret-scrub.d.ts.map +1 -0
- package/dist/shipment/secret-scrub.js +30 -0
- package/dist/shipment/secret-scrub.js.map +1 -0
- package/dist/shipment/shipment-actions.d.ts +85 -0
- package/dist/shipment/shipment-actions.d.ts.map +1 -0
- package/dist/shipment/shipment-actions.js +190 -0
- package/dist/shipment/shipment-actions.js.map +1 -0
- package/dist/shipment/shipment-pipeline.d.ts +48 -0
- package/dist/shipment/shipment-pipeline.d.ts.map +1 -0
- package/dist/shipment/shipment-pipeline.js +256 -0
- package/dist/shipment/shipment-pipeline.js.map +1 -0
- package/dist/shipment/transport-detect.d.ts +16 -0
- package/dist/shipment/transport-detect.d.ts.map +1 -0
- package/dist/shipment/transport-detect.js +54 -0
- package/dist/shipment/transport-detect.js.map +1 -0
- package/dist/shipment/workspace-diff.d.ts +39 -0
- package/dist/shipment/workspace-diff.d.ts.map +1 -0
- package/dist/shipment/workspace-diff.js +64 -0
- package/dist/shipment/workspace-diff.js.map +1 -0
- package/dist/swarm-coordinator.d.ts +20 -1
- package/dist/swarm-coordinator.d.ts.map +1 -1
- package/dist/swarm-coordinator.js +193 -10
- package/dist/swarm-coordinator.js.map +1 -1
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace/git-worktree-provider.d.ts +11 -0
- package/dist/workspace/git-worktree-provider.d.ts.map +1 -0
- package/dist/workspace/git-worktree-provider.js +123 -0
- package/dist/workspace/git-worktree-provider.js.map +1 -0
- package/dist/workspace/gitignore-check.d.ts +10 -0
- package/dist/workspace/gitignore-check.d.ts.map +1 -0
- package/dist/workspace/gitignore-check.js +25 -0
- package/dist/workspace/gitignore-check.js.map +1 -0
- package/dist/workspace/index.d.ts +5 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +5 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/snapshot-copy-provider.d.ts +11 -0
- package/dist/workspace/snapshot-copy-provider.d.ts.map +1 -0
- package/dist/workspace/snapshot-copy-provider.js +66 -0
- package/dist/workspace/snapshot-copy-provider.js.map +1 -0
- package/dist/workspace/types.d.ts +36 -0
- package/dist/workspace/types.d.ts.map +1 -0
- package/dist/workspace/types.js +2 -0
- package/dist/workspace/types.js.map +1 -0
- package/dist/workspace/workspace-manager.d.ts +30 -0
- package/dist/workspace/workspace-manager.d.ts.map +1 -0
- package/dist/workspace/workspace-manager.js +104 -0
- package/dist/workspace/workspace-manager.js.map +1 -0
- package/package.json +4 -4
- package/roles/queen.md +1 -0
- package/templates/introspection.md +64 -0
- package/templates/research-only.md +58 -0
- package/roles/preset-analyst-simons.md +0 -39
- package/roles/preset-architect-knuth.md +0 -39
- package/roles/preset-designer-norman.md +0 -39
- package/roles/preset-designer.md +0 -39
- package/roles/preset-dev-carmack.md +0 -39
- package/roles/preset-dev-gosling.md +0 -39
- package/roles/preset-developer.md +0 -52
- package/roles/preset-manager-grove.md +0 -39
- package/roles/preset-manager-musk.md +0 -39
- package/roles/preset-pm.md +0 -78
- package/roles/preset-researcher-feynman.md +0 -39
- package/roles/preset-reviewer.md +0 -46
- package/roles/preset-strategist-buffett.md +0 -39
- package/roles/preset-strategist-munger.md +0 -39
- package/roles/preset-strategist-sunzi.md +0 -39
- package/roles/preset-tester-beck.md +0 -40
- package/roles/preset-tester.md +0 -47
- package/roles/preset-writer-orwell.md +0 -39
- 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
|