sequant 2.9.0 → 2.10.0

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 (188) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +14 -3
  4. package/dist/bin/cli.js +97 -17
  5. package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
  6. package/dist/marketplace/external_plugins/sequant/.mcp.json +1 -1
  7. package/dist/marketplace/external_plugins/sequant/README.md +56 -2
  8. package/dist/marketplace/external_plugins/sequant/hooks/parallel-marker.sh +50 -0
  9. package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +20 -7
  10. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +75 -10
  11. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/trust-model.md +18 -0
  12. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +215 -390
  13. package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +1 -1
  14. package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +4 -4
  15. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +26 -6
  16. package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +3 -1
  17. package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +38 -2
  18. package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +2 -2
  19. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +337 -57
  20. package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +6 -6
  21. package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +2 -2
  22. package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-review-checklist.md +2 -2
  23. package/dist/marketplace/external_plugins/sequant/skills/qa/references/fixtures/injection-issue-body.md +36 -0
  24. package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +21 -7
  25. package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +1 -1
  26. package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +11 -11
  27. package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +1 -1
  28. package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +5 -5
  29. package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +2 -0
  30. package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
  31. package/dist/src/commands/assess-render.d.ts +23 -0
  32. package/dist/src/commands/assess-render.js +60 -0
  33. package/dist/src/commands/doctor.js +8 -13
  34. package/dist/src/commands/init.js +13 -1
  35. package/dist/src/commands/locks.js +2 -0
  36. package/dist/src/commands/logs.js +14 -5
  37. package/dist/src/commands/merge.js +154 -2
  38. package/dist/src/commands/ready-tui-adapter.js +6 -1
  39. package/dist/src/commands/ready.d.ts +25 -3
  40. package/dist/src/commands/ready.js +39 -13
  41. package/dist/src/commands/resume.d.ts +113 -0
  42. package/dist/src/commands/resume.js +193 -0
  43. package/dist/src/commands/run-display.js +37 -4
  44. package/dist/src/commands/run-flags.d.ts +42 -1
  45. package/dist/src/commands/run-flags.js +53 -1
  46. package/dist/src/commands/run-progress.js +26 -1
  47. package/dist/src/commands/run.js +11 -10
  48. package/dist/src/commands/state.js +7 -0
  49. package/dist/src/commands/status.d.ts +9 -0
  50. package/dist/src/commands/status.js +24 -1
  51. package/dist/src/commands/sync.js +90 -19
  52. package/dist/src/commands/update.js +28 -5
  53. package/dist/src/lib/ac-parser.d.ts +14 -0
  54. package/dist/src/lib/ac-parser.js +99 -5
  55. package/dist/src/lib/assess/index.d.ts +10 -0
  56. package/dist/src/lib/assess/index.js +9 -0
  57. package/dist/src/lib/assess/renderer.d.ts +64 -0
  58. package/dist/src/lib/assess/renderer.js +481 -0
  59. package/dist/src/lib/assess/types.d.ts +224 -0
  60. package/dist/src/lib/assess/types.js +241 -0
  61. package/dist/src/lib/ci/outputs.d.ts +15 -2
  62. package/dist/src/lib/ci/outputs.js +17 -5
  63. package/dist/src/lib/cli-flags.d.ts +33 -0
  64. package/dist/src/lib/cli-flags.js +46 -0
  65. package/dist/src/lib/cli-ui/run-renderer-types.d.ts +21 -7
  66. package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
  67. package/dist/src/lib/cli-ui/run-renderer.js +62 -0
  68. package/dist/src/lib/errors.d.ts +78 -6
  69. package/dist/src/lib/errors.js +133 -15
  70. package/dist/src/lib/locks/index.d.ts +3 -3
  71. package/dist/src/lib/locks/index.js +2 -2
  72. package/dist/src/lib/locks/lock-manager.d.ts +20 -2
  73. package/dist/src/lib/locks/lock-manager.js +47 -3
  74. package/dist/src/lib/locks/types.d.ts +34 -3
  75. package/dist/src/lib/locks/types.js +16 -0
  76. package/dist/src/lib/mcp-config.d.ts +45 -0
  77. package/dist/src/lib/mcp-config.js +77 -1
  78. package/dist/src/lib/merge-check/combined-branch-test.d.ts +21 -2
  79. package/dist/src/lib/merge-check/combined-branch-test.js +233 -110
  80. package/dist/src/lib/merge-check/command-result.d.ts +36 -0
  81. package/dist/src/lib/merge-check/command-result.js +56 -0
  82. package/dist/src/lib/merge-check/index.d.ts +4 -0
  83. package/dist/src/lib/merge-check/index.js +1 -1
  84. package/dist/src/lib/merge-check/report.js +5 -1
  85. package/dist/src/lib/merge-check/types.d.ts +9 -0
  86. package/dist/src/lib/merge-check/watch.d.ts +121 -0
  87. package/dist/src/lib/merge-check/watch.js +215 -0
  88. package/dist/src/lib/qa/infra-blocked-ci.d.ts +90 -0
  89. package/dist/src/lib/qa/infra-blocked-ci.js +80 -0
  90. package/dist/src/lib/settings.d.ts +14 -0
  91. package/dist/src/lib/settings.js +61 -8
  92. package/dist/src/lib/shutdown.d.ts +42 -2
  93. package/dist/src/lib/shutdown.js +75 -5
  94. package/dist/src/lib/skills-check.d.ts +26 -0
  95. package/dist/src/lib/skills-check.js +31 -0
  96. package/dist/src/lib/stacks.d.ts +110 -1
  97. package/dist/src/lib/stacks.js +244 -5
  98. package/dist/src/lib/templates.d.ts +74 -0
  99. package/dist/src/lib/templates.js +171 -16
  100. package/dist/src/lib/test-tautology-detector.d.ts +9 -2
  101. package/dist/src/lib/test-tautology-detector.js +153 -12
  102. package/dist/src/lib/version-check.d.ts +31 -0
  103. package/dist/src/lib/version-check.js +45 -2
  104. package/dist/src/lib/workflow/batch-executor.d.ts +125 -6
  105. package/dist/src/lib/workflow/batch-executor.js +477 -44
  106. package/dist/src/lib/workflow/chain-resume.d.ts +13 -11
  107. package/dist/src/lib/workflow/chain-resume.js +24 -20
  108. package/dist/src/lib/workflow/completed-status.d.ts +52 -0
  109. package/dist/src/lib/workflow/completed-status.js +57 -0
  110. package/dist/src/lib/workflow/config-resolver.d.ts +26 -0
  111. package/dist/src/lib/workflow/config-resolver.js +54 -2
  112. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +7 -0
  113. package/dist/src/lib/workflow/drivers/aider.d.ts +2 -0
  114. package/dist/src/lib/workflow/drivers/aider.js +2 -0
  115. package/dist/src/lib/workflow/drivers/claude-code.d.ts +14 -0
  116. package/dist/src/lib/workflow/drivers/claude-code.js +20 -2
  117. package/dist/src/lib/workflow/git-diff-utils.d.ts +35 -3
  118. package/dist/src/lib/workflow/git-diff-utils.js +77 -4
  119. package/dist/src/lib/workflow/heartbeat.d.ts +38 -0
  120. package/dist/src/lib/workflow/heartbeat.js +90 -0
  121. package/dist/src/lib/workflow/log-writer.d.ts +44 -1
  122. package/dist/src/lib/workflow/log-writer.js +75 -6
  123. package/dist/src/lib/workflow/metrics-schema.d.ts +3 -3
  124. package/dist/src/lib/workflow/phase-executor.d.ts +203 -14
  125. package/dist/src/lib/workflow/phase-executor.js +457 -52
  126. package/dist/src/lib/workflow/phase-registry.js +2 -1
  127. package/dist/src/lib/workflow/platforms/github.d.ts +64 -0
  128. package/dist/src/lib/workflow/platforms/github.js +152 -0
  129. package/dist/src/lib/workflow/qa-cache.d.ts +6 -1
  130. package/dist/src/lib/workflow/qa-cache.js +22 -9
  131. package/dist/src/lib/workflow/ready-gate.d.ts +17 -4
  132. package/dist/src/lib/workflow/ready-gate.js +25 -9
  133. package/dist/src/lib/workflow/run-log-schema.d.ts +14 -0
  134. package/dist/src/lib/workflow/run-log-schema.js +47 -1
  135. package/dist/src/lib/workflow/run-orchestrator.d.ts +19 -0
  136. package/dist/src/lib/workflow/run-orchestrator.js +141 -20
  137. package/dist/src/lib/workflow/skills-preflight.d.ts +89 -0
  138. package/dist/src/lib/workflow/skills-preflight.js +112 -0
  139. package/dist/src/lib/workflow/state-cleanup.js +9 -2
  140. package/dist/src/lib/workflow/state-manager.d.ts +33 -0
  141. package/dist/src/lib/workflow/state-manager.js +83 -0
  142. package/dist/src/lib/workflow/state-schema.d.ts +26 -0
  143. package/dist/src/lib/workflow/state-schema.js +47 -0
  144. package/dist/src/lib/workflow/types.d.ts +126 -4
  145. package/dist/src/lib/workflow/types.js +1 -0
  146. package/dist/src/lib/workflow/worktree-manager.d.ts +28 -2
  147. package/dist/src/lib/workflow/worktree-manager.js +102 -23
  148. package/dist/src/mcp/tools/run.d.ts +9 -1
  149. package/dist/src/mcp/tools/run.js +20 -1
  150. package/dist/src/mcp/tools/status.js +6 -0
  151. package/dist/src/ui/tui/ElapsedTimer.d.ts +12 -3
  152. package/dist/src/ui/tui/ElapsedTimer.js +12 -10
  153. package/dist/src/ui/tui/IssueBox.js +10 -2
  154. package/dist/src/ui/tui/row-cap.js +2 -1
  155. package/dist/src/ui/tui/theme.d.ts +7 -0
  156. package/dist/src/ui/tui/theme.js +9 -0
  157. package/package.json +8 -7
  158. package/templates/hooks/parallel-marker.sh +50 -0
  159. package/templates/hooks/post-tool.sh +20 -7
  160. package/templates/hooks/pre-tool.sh +75 -10
  161. package/templates/scripts/cleanup-worktree.sh +182 -19
  162. package/templates/scripts/new-feature.sh +264 -16
  163. package/templates/skills/_shared/references/trust-model.md +18 -0
  164. package/templates/skills/assess/SKILL.md +215 -390
  165. package/templates/skills/assess/references/predicted-collision-detection.md +1 -1
  166. package/templates/skills/docs/SKILL.md +4 -4
  167. package/templates/skills/exec/SKILL.md +26 -6
  168. package/templates/skills/fullsolve/SKILL.md +3 -1
  169. package/templates/skills/loop/SKILL.md +38 -2
  170. package/templates/skills/merger/SKILL.md +2 -2
  171. package/templates/skills/qa/SKILL.md +337 -57
  172. package/templates/skills/qa/references/anti-pattern-detection.md +6 -6
  173. package/templates/skills/qa/references/call-site-review.md +2 -2
  174. package/templates/skills/qa/references/code-review-checklist.md +2 -2
  175. package/templates/skills/qa/references/fixtures/injection-issue-body.md +36 -0
  176. package/templates/skills/qa/references/quality-gates.md +21 -7
  177. package/templates/skills/qa/references/testing-requirements.md +1 -1
  178. package/templates/skills/qa/scripts/quality-checks.sh +11 -11
  179. package/templates/skills/release/SKILL.md +1 -1
  180. package/templates/skills/setup/SKILL.md +5 -5
  181. package/templates/skills/spec/SKILL.md +2 -0
  182. package/templates/skills/test/SKILL.md +1 -1
  183. package/dist/src/lib/phase-spinner.d.ts +0 -146
  184. package/dist/src/lib/phase-spinner.js +0 -255
  185. package/dist/src/lib/workflow/pr-operations.d.ts +0 -86
  186. package/dist/src/lib/workflow/pr-operations.js +0 -326
  187. package/dist/src/lib/workflow/run-summary.d.ts +0 -36
  188. package/dist/src/lib/workflow/run-summary.js +0 -142
@@ -1,255 +0,0 @@
1
- /**
2
- * Phase Spinner - Animated spinner with elapsed time for phase execution
3
- *
4
- * Wraps the cli-ui spinner with:
5
- * - Elapsed time tracking (updates every 5 seconds)
6
- * - Phase progress indicator (e.g., "spec (1/3)")
7
- * - Integration with ShutdownManager for graceful cleanup
8
- *
9
- * @example
10
- * ```typescript
11
- * const spinner = new PhaseSpinner({
12
- * phase: 'exec',
13
- * phaseIndex: 2,
14
- * totalPhases: 3,
15
- * shutdownManager,
16
- * });
17
- *
18
- * spinner.start();
19
- * // ... phase execution
20
- * spinner.succeed(); // Shows "✓ exec (2/3) (45s)"
21
- * ```
22
- */
23
- import { spinner as createSpinner } from "./cli-ui.js";
24
- /**
25
- * Elapsed time update interval in milliseconds
26
- */
27
- const ELAPSED_UPDATE_INTERVAL_MS = 5000;
28
- /**
29
- * Format elapsed time in human-readable format
30
- *
31
- * @param seconds - Elapsed time in seconds
32
- * @returns Formatted string (e.g., "45s", "2m 15s", "1h 5m")
33
- */
34
- export function formatElapsedTime(seconds) {
35
- if (seconds < 60) {
36
- return `${Math.floor(seconds)}s`;
37
- }
38
- if (seconds < 3600) {
39
- const minutes = Math.floor(seconds / 60);
40
- const remainingSeconds = Math.floor(seconds % 60);
41
- return remainingSeconds > 0
42
- ? `${minutes}m ${remainingSeconds}s`
43
- : `${minutes}m`;
44
- }
45
- const hours = Math.floor(seconds / 3600);
46
- const remainingMinutes = Math.floor((seconds % 3600) / 60);
47
- return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
48
- }
49
- /**
50
- * PhaseSpinner - Animated spinner with elapsed time and phase progress
51
- *
52
- * Features:
53
- * - Animated spinner (or text fallback) via cli-ui
54
- * - Elapsed time tracking with automatic updates
55
- * - Phase progress indicator (e.g., "exec (2/3)")
56
- * - ShutdownManager integration for graceful Ctrl+C cleanup
57
- * - Pause/resume for verbose streaming output
58
- */
59
- export class PhaseSpinner {
60
- spinner;
61
- startTime = 0;
62
- intervalId = null;
63
- disposed = false;
64
- paused = false;
65
- phase;
66
- phaseIndex;
67
- totalPhases;
68
- shutdownManager;
69
- prefix;
70
- iteration;
71
- cleanupName;
72
- constructor(options) {
73
- this.phase = options.phase;
74
- this.phaseIndex = options.phaseIndex;
75
- this.totalPhases = options.totalPhases;
76
- this.shutdownManager = options.shutdownManager;
77
- this.prefix = options.prefix ?? " ";
78
- this.iteration = options.iteration;
79
- this.cleanupName = `phase-spinner-${options.phase}`;
80
- // Create the underlying spinner with initial text
81
- this.spinner = createSpinner(this.formatText(0));
82
- }
83
- /**
84
- * Format the spinner text with phase, progress, and elapsed time
85
- */
86
- formatText(elapsedSeconds) {
87
- const progress = `(${this.phaseIndex}/${this.totalPhases})`;
88
- const elapsed = elapsedSeconds > 0 ? ` ${formatElapsedTime(elapsedSeconds)}` : "";
89
- const iterationSuffix = this.iteration && this.iteration > 1
90
- ? ` [iteration ${this.iteration}]`
91
- : "";
92
- return `${this.prefix}${this.phase} ${progress}...${elapsed}${iterationSuffix}`;
93
- }
94
- /**
95
- * Update the spinner text with current elapsed time
96
- */
97
- updateElapsedTime() {
98
- if (this.disposed || this.paused)
99
- return;
100
- const elapsedSeconds = (Date.now() - this.startTime) / 1000;
101
- this.spinner.text = this.formatText(elapsedSeconds);
102
- }
103
- /**
104
- * Start the spinner
105
- *
106
- * Begins the animation and elapsed time tracking.
107
- */
108
- start() {
109
- if (this.disposed)
110
- return this;
111
- this.startTime = Date.now();
112
- this.spinner.start(this.formatText(0));
113
- // Start elapsed time update interval
114
- this.intervalId = setInterval(() => {
115
- this.updateElapsedTime();
116
- }, ELAPSED_UPDATE_INTERVAL_MS);
117
- // Register with ShutdownManager for graceful cleanup
118
- if (this.shutdownManager) {
119
- this.shutdownManager.registerCleanup(this.cleanupName, async () => {
120
- this.stop();
121
- });
122
- }
123
- return this;
124
- }
125
- /**
126
- * Mark the phase as succeeded
127
- *
128
- * Stops the spinner and shows a checkmark with total duration.
129
- */
130
- succeed(customText) {
131
- if (this.disposed)
132
- return this;
133
- this.clearInterval();
134
- this.unregisterCleanup();
135
- const elapsedSeconds = (Date.now() - this.startTime) / 1000;
136
- const duration = formatElapsedTime(elapsedSeconds);
137
- const progress = `(${this.phaseIndex}/${this.totalPhases})`;
138
- const text = customText ?? `${this.prefix}${this.phase} ${progress} (${duration})`;
139
- this.spinner.succeed(text);
140
- this.disposed = true;
141
- return this;
142
- }
143
- /**
144
- * Mark the phase as failed
145
- *
146
- * Stops the spinner and shows an error indicator with message.
147
- */
148
- fail(error) {
149
- if (this.disposed)
150
- return this;
151
- this.clearInterval();
152
- this.unregisterCleanup();
153
- const elapsedSeconds = (Date.now() - this.startTime) / 1000;
154
- const duration = formatElapsedTime(elapsedSeconds);
155
- const progress = `(${this.phaseIndex}/${this.totalPhases})`;
156
- const errorSuffix = error ? `: ${error}` : "";
157
- const text = `${this.prefix}${this.phase} ${progress} (${duration})${errorSuffix}`;
158
- this.spinner.fail(text);
159
- this.disposed = true;
160
- return this;
161
- }
162
- /**
163
- * Stop the spinner without success/fail indication
164
- *
165
- * Use this for cleanup or when the phase is interrupted.
166
- */
167
- stop() {
168
- if (this.disposed)
169
- return this;
170
- this.clearInterval();
171
- this.unregisterCleanup();
172
- this.spinner.stop();
173
- this.disposed = true;
174
- return this;
175
- }
176
- /**
177
- * Pause the spinner (for verbose streaming output)
178
- *
179
- * Temporarily stops the animation without disposing.
180
- * Call resume() to continue.
181
- */
182
- pause() {
183
- if (this.disposed || this.paused)
184
- return this;
185
- this.paused = true;
186
- this.spinner.stop();
187
- return this;
188
- }
189
- /**
190
- * Resume the spinner after pause
191
- *
192
- * Restarts the animation with updated elapsed time.
193
- */
194
- resume() {
195
- if (this.disposed || !this.paused)
196
- return this;
197
- this.paused = false;
198
- const elapsedSeconds = (Date.now() - this.startTime) / 1000;
199
- this.spinner.start(this.formatText(elapsedSeconds));
200
- return this;
201
- }
202
- /**
203
- * Check if the spinner is currently active
204
- */
205
- get isSpinning() {
206
- return this.spinner.isSpinning && !this.disposed && !this.paused;
207
- }
208
- /**
209
- * Get the total elapsed time in seconds
210
- */
211
- get elapsedSeconds() {
212
- if (this.startTime === 0)
213
- return 0;
214
- return (Date.now() - this.startTime) / 1000;
215
- }
216
- /**
217
- * Dispose of the spinner and clean up resources
218
- *
219
- * Called automatically by succeed(), fail(), or stop().
220
- * Safe to call multiple times.
221
- */
222
- dispose() {
223
- if (this.disposed)
224
- return;
225
- this.clearInterval();
226
- this.unregisterCleanup();
227
- this.spinner.stop();
228
- this.disposed = true;
229
- }
230
- /**
231
- * Clear the elapsed time update interval
232
- */
233
- clearInterval() {
234
- if (this.intervalId !== null) {
235
- clearInterval(this.intervalId);
236
- this.intervalId = null;
237
- }
238
- }
239
- /**
240
- * Unregister from ShutdownManager
241
- */
242
- unregisterCleanup() {
243
- if (this.shutdownManager) {
244
- this.shutdownManager.unregisterCleanup(this.cleanupName);
245
- }
246
- }
247
- }
248
- /**
249
- * Create a PhaseSpinner with the given options
250
- *
251
- * Convenience function matching the pattern of cli-ui.spinner().
252
- */
253
- export function phaseSpinner(options) {
254
- return new PhaseSpinner(options);
255
- }
@@ -1,86 +0,0 @@
1
- /**
2
- * PR Operations Module
3
- *
4
- * Handles pre-PR and PR creation operations:
5
- * - Checkpoint commits for chain recovery
6
- * - Lockfile change detection and dependency reinstall
7
- * - Pre-PR rebase onto origin/main
8
- * - PR creation with existing-PR detection
9
- *
10
- * @module pr-operations
11
- */
12
- /**
13
- * Result of a pre-PR rebase operation
14
- */
15
- export interface RebaseResult {
16
- /** Whether the rebase was performed */
17
- performed: boolean;
18
- /** Whether the rebase succeeded */
19
- success: boolean;
20
- /** Whether dependencies were reinstalled */
21
- reinstalled: boolean;
22
- /** Error message if rebase failed */
23
- error?: string;
24
- }
25
- /**
26
- * Result of PR creation
27
- */
28
- export interface PRCreationResult {
29
- /** Whether PR creation was attempted */
30
- attempted: boolean;
31
- /** Whether PR was created successfully (or already existed) */
32
- success: boolean;
33
- /** PR number */
34
- prNumber?: number;
35
- /** PR URL */
36
- prUrl?: string;
37
- /** Error message if failed */
38
- error?: string;
39
- }
40
- /**
41
- * Create a checkpoint commit in the worktree after QA passes
42
- * This allows recovery in case later issues in the chain fail
43
- * @internal Exported for testing
44
- */
45
- export declare function createCheckpointCommit(worktreePath: string, issueNumber: number, verbose: boolean): boolean;
46
- /**
47
- * Check if any lockfile changed during a rebase and re-run install if needed.
48
- * This prevents dependency drift when the lockfile was updated on main.
49
- * @param worktreePath Path to the worktree
50
- * @param packageManager Package manager to use for install
51
- * @param verbose Whether to show verbose output
52
- * @param preRebaseRef Git ref pointing to pre-rebase HEAD (defaults to ORIG_HEAD,
53
- * which git sets automatically after rebase). Using ORIG_HEAD captures all
54
- * lockfile changes across multi-commit rebases, unlike HEAD~1 which only
55
- * checks the last commit.
56
- * @returns true if reinstall was performed, false otherwise
57
- * @internal Exported for testing
58
- */
59
- export declare function reinstallIfLockfileChanged(worktreePath: string, packageManager: string | undefined, verbose: boolean, preRebaseRef?: string): boolean;
60
- /**
61
- * Rebase the worktree branch onto origin/main before PR creation.
62
- * This ensures the branch is up-to-date and prevents lockfile drift.
63
- *
64
- * @param worktreePath Path to the worktree
65
- * @param issueNumber Issue number (for logging)
66
- * @param packageManager Package manager to use if reinstall needed
67
- * @param verbose Whether to show verbose output
68
- * @returns RebaseResult indicating success/failure and whether reinstall was performed
69
- * @internal Exported for testing
70
- */
71
- export declare function rebaseBeforePR(worktreePath: string, issueNumber: number, packageManager: string | undefined, verbose: boolean): RebaseResult;
72
- /**
73
- * Push branch and create a PR after successful QA.
74
- *
75
- * Handles both fresh PR creation and detection of existing PRs.
76
- * Failures are warnings — they don't fail the run.
77
- *
78
- * @param worktreePath Path to the worktree
79
- * @param issueNumber Issue number
80
- * @param issueTitle Issue title (for PR title)
81
- * @param branch Branch name
82
- * @param verbose Whether to show verbose output
83
- * @returns PRCreationResult with PR info or error
84
- * @internal Exported for testing
85
- */
86
- export declare function createPR(worktreePath: string, issueNumber: number, issueTitle: string, branch: string, verbose: boolean, labels?: string[]): PRCreationResult;
@@ -1,326 +0,0 @@
1
- /**
2
- * PR Operations Module
3
- *
4
- * Handles pre-PR and PR creation operations:
5
- * - Checkpoint commits for chain recovery
6
- * - Lockfile change detection and dependency reinstall
7
- * - Pre-PR rebase onto origin/main
8
- * - PR creation with existing-PR detection
9
- *
10
- * @module pr-operations
11
- */
12
- import chalk from "chalk";
13
- import { spawnSync } from "child_process";
14
- import { PM_CONFIG } from "../stacks.js";
15
- /**
16
- * Lockfile names for different package managers
17
- */
18
- const LOCKFILES = [
19
- "package-lock.json",
20
- "pnpm-lock.yaml",
21
- "bun.lock",
22
- "yarn.lock",
23
- ];
24
- /**
25
- * Create a checkpoint commit in the worktree after QA passes
26
- * This allows recovery in case later issues in the chain fail
27
- * @internal Exported for testing
28
- */
29
- export function createCheckpointCommit(worktreePath, issueNumber, verbose) {
30
- // Check if there are uncommitted changes
31
- const statusResult = spawnSync("git", ["-C", worktreePath, "status", "--porcelain"], { stdio: "pipe" });
32
- if (statusResult.status !== 0) {
33
- if (verbose) {
34
- console.log(chalk.yellow(` ⚠️ Could not check git status for checkpoint`));
35
- }
36
- return false;
37
- }
38
- const hasChanges = statusResult.stdout.toString().trim().length > 0;
39
- if (!hasChanges) {
40
- if (verbose) {
41
- console.log(chalk.gray(` 📌 No changes to checkpoint (already committed)`));
42
- }
43
- return true;
44
- }
45
- // Stage all changes
46
- const addResult = spawnSync("git", ["-C", worktreePath, "add", "-A"], {
47
- stdio: "pipe",
48
- });
49
- if (addResult.status !== 0) {
50
- if (verbose) {
51
- console.log(chalk.yellow(` ⚠️ Could not stage changes for checkpoint`));
52
- }
53
- return false;
54
- }
55
- // Create checkpoint commit
56
- const commitMessage = `checkpoint(#${issueNumber}): QA passed
57
-
58
- This is an automatic checkpoint commit created after issue #${issueNumber}
59
- passed QA in chain mode. It serves as a recovery point if later issues fail.`;
60
- const commitResult = spawnSync("git", ["-C", worktreePath, "commit", "-m", commitMessage], { stdio: "pipe" });
61
- if (commitResult.status !== 0) {
62
- const error = commitResult.stderr.toString();
63
- if (verbose) {
64
- console.log(chalk.yellow(` ⚠️ Could not create checkpoint commit: ${error}`));
65
- }
66
- return false;
67
- }
68
- console.log(chalk.green(` 📌 Checkpoint commit created for #${issueNumber}`));
69
- return true;
70
- }
71
- /**
72
- * Check if any lockfile changed during a rebase and re-run install if needed.
73
- * This prevents dependency drift when the lockfile was updated on main.
74
- * @param worktreePath Path to the worktree
75
- * @param packageManager Package manager to use for install
76
- * @param verbose Whether to show verbose output
77
- * @param preRebaseRef Git ref pointing to pre-rebase HEAD (defaults to ORIG_HEAD,
78
- * which git sets automatically after rebase). Using ORIG_HEAD captures all
79
- * lockfile changes across multi-commit rebases, unlike HEAD~1 which only
80
- * checks the last commit.
81
- * @returns true if reinstall was performed, false otherwise
82
- * @internal Exported for testing
83
- */
84
- export function reinstallIfLockfileChanged(worktreePath, packageManager, verbose, preRebaseRef = "ORIG_HEAD") {
85
- // Compare pre-rebase state to current HEAD to detect all lockfile changes
86
- // introduced by the rebase (including changes from main that were pulled in)
87
- let lockfileChanged = false;
88
- for (const lockfile of LOCKFILES) {
89
- const result = spawnSync("git", [
90
- "-C",
91
- worktreePath,
92
- "diff",
93
- "--name-only",
94
- `${preRebaseRef}..HEAD`,
95
- "--",
96
- lockfile,
97
- ], { stdio: "pipe" });
98
- if (result.status === 0 && result.stdout.toString().trim().length > 0) {
99
- lockfileChanged = true;
100
- if (verbose) {
101
- console.log(chalk.gray(` 📦 Lockfile changed: ${lockfile}`));
102
- }
103
- break;
104
- }
105
- }
106
- if (!lockfileChanged) {
107
- if (verbose) {
108
- console.log(chalk.gray(` 📦 No lockfile changes detected`));
109
- }
110
- return false;
111
- }
112
- // Re-run install to sync node_modules with updated lockfile
113
- console.log(chalk.blue(` 📦 Reinstalling dependencies (lockfile changed)...`));
114
- const pm = packageManager || "npm";
115
- const pmConfig = PM_CONFIG[pm];
116
- const [cmd, ...args] = pmConfig.installSilent.split(" ");
117
- const installResult = spawnSync(cmd, args, {
118
- cwd: worktreePath,
119
- stdio: "pipe",
120
- });
121
- if (installResult.status !== 0) {
122
- const error = installResult.stderr.toString();
123
- console.log(chalk.yellow(` ⚠️ Dependency reinstall failed: ${error.trim()}`));
124
- return false;
125
- }
126
- console.log(chalk.green(` ✅ Dependencies reinstalled`));
127
- return true;
128
- }
129
- /**
130
- * Rebase the worktree branch onto origin/main before PR creation.
131
- * This ensures the branch is up-to-date and prevents lockfile drift.
132
- *
133
- * @param worktreePath Path to the worktree
134
- * @param issueNumber Issue number (for logging)
135
- * @param packageManager Package manager to use if reinstall needed
136
- * @param verbose Whether to show verbose output
137
- * @returns RebaseResult indicating success/failure and whether reinstall was performed
138
- * @internal Exported for testing
139
- */
140
- export function rebaseBeforePR(worktreePath, issueNumber, packageManager, verbose) {
141
- if (verbose) {
142
- console.log(chalk.gray(` 🔄 Rebasing #${issueNumber} onto origin/main before PR...`));
143
- }
144
- // Fetch latest main to ensure we're rebasing onto fresh state
145
- const fetchResult = spawnSync("git", ["-C", worktreePath, "fetch", "origin", "main"], {
146
- stdio: "pipe",
147
- });
148
- if (fetchResult.status !== 0) {
149
- const error = fetchResult.stderr.toString();
150
- console.log(chalk.yellow(` ⚠️ Could not fetch origin/main: ${error.trim()}`));
151
- // Continue anyway - might work with local state
152
- }
153
- // Perform the rebase
154
- const rebaseResult = spawnSync("git", ["-C", worktreePath, "rebase", "origin/main"], { stdio: "pipe" });
155
- if (rebaseResult.status !== 0) {
156
- const rebaseError = rebaseResult.stderr.toString();
157
- // Check if it's a conflict
158
- if (rebaseError.includes("CONFLICT") ||
159
- rebaseError.includes("could not apply")) {
160
- console.log(chalk.yellow(` ⚠️ Rebase conflict detected. Aborting rebase and keeping original branch state.`));
161
- console.log(chalk.yellow(` ℹ️ PR will be created without rebase. Manual rebase may be required before merge.`));
162
- // Abort the rebase to restore branch state
163
- spawnSync("git", ["-C", worktreePath, "rebase", "--abort"], {
164
- stdio: "pipe",
165
- });
166
- return {
167
- performed: true,
168
- success: false,
169
- reinstalled: false,
170
- error: "Rebase conflict - manual resolution required",
171
- };
172
- }
173
- else {
174
- console.log(chalk.yellow(` ⚠️ Rebase failed: ${rebaseError.trim()}`));
175
- console.log(chalk.yellow(` ℹ️ Continuing with branch in its original state.`));
176
- return {
177
- performed: true,
178
- success: false,
179
- reinstalled: false,
180
- error: rebaseError.trim(),
181
- };
182
- }
183
- }
184
- console.log(chalk.green(` ✅ Branch rebased onto origin/main`));
185
- // Check if lockfile changed and reinstall if needed
186
- const reinstalled = reinstallIfLockfileChanged(worktreePath, packageManager, verbose);
187
- return {
188
- performed: true,
189
- success: true,
190
- reinstalled,
191
- };
192
- }
193
- /**
194
- * Push branch and create a PR after successful QA.
195
- *
196
- * Handles both fresh PR creation and detection of existing PRs.
197
- * Failures are warnings — they don't fail the run.
198
- *
199
- * @param worktreePath Path to the worktree
200
- * @param issueNumber Issue number
201
- * @param issueTitle Issue title (for PR title)
202
- * @param branch Branch name
203
- * @param verbose Whether to show verbose output
204
- * @returns PRCreationResult with PR info or error
205
- * @internal Exported for testing
206
- */
207
- export function createPR(worktreePath, issueNumber, issueTitle, branch, verbose, labels) {
208
- // Step 1: Check for existing PR on this branch
209
- const existingPR = spawnSync("gh", ["pr", "view", branch, "--json", "number,url"], { stdio: "pipe", cwd: worktreePath, timeout: 15000 });
210
- if (existingPR.status === 0 && existingPR.stdout) {
211
- try {
212
- const prInfo = JSON.parse(existingPR.stdout.toString());
213
- if (prInfo.number && prInfo.url) {
214
- if (verbose) {
215
- console.log(chalk.gray(` ℹ️ PR #${prInfo.number} already exists for branch ${branch}`));
216
- }
217
- return {
218
- attempted: true,
219
- success: true,
220
- prNumber: prInfo.number,
221
- prUrl: prInfo.url,
222
- };
223
- }
224
- }
225
- catch {
226
- // JSON parse failed — no existing PR, continue to create
227
- }
228
- }
229
- // Step 2: Push branch to remote
230
- if (verbose) {
231
- console.log(chalk.gray(` 🚀 Pushing branch ${branch} to origin...`));
232
- }
233
- const pushResult = spawnSync("git", ["-C", worktreePath, "push", "-u", "origin", branch], { stdio: "pipe", timeout: 60000 });
234
- if (pushResult.status !== 0) {
235
- const pushError = pushResult.stderr?.toString().trim() ?? "Unknown error";
236
- console.log(chalk.yellow(` ⚠️ git push failed: ${pushError}`));
237
- return {
238
- attempted: true,
239
- success: false,
240
- error: `git push failed: ${pushError}`,
241
- };
242
- }
243
- // Step 3: Create PR
244
- if (verbose) {
245
- console.log(chalk.gray(` 📝 Creating PR for #${issueNumber}...`));
246
- }
247
- const isBug = labels?.some((l) => /^bug/i.test(l));
248
- const prefix = isBug ? "fix" : "feat";
249
- const prTitle = `${prefix}(#${issueNumber}): ${issueTitle}`;
250
- const prBody = [
251
- `## Summary`,
252
- ``,
253
- `Automated PR for issue #${issueNumber}.`,
254
- ``,
255
- `Fixes #${issueNumber}`,
256
- ``,
257
- `---`,
258
- `🤖 Generated by \`sequant run\``,
259
- ].join("\n");
260
- const prResult = spawnSync("gh", ["pr", "create", "--title", prTitle, "--body", prBody, "--head", branch], { stdio: "pipe", cwd: worktreePath, timeout: 30000 });
261
- if (prResult.status !== 0) {
262
- const prError = prResult.stderr?.toString().trim() ?? "Unknown error";
263
- // Check if PR already exists (race condition or push-before-PR scenarios)
264
- if (prError.includes("already exists")) {
265
- const retryView = spawnSync("gh", ["pr", "view", branch, "--json", "number,url"], { stdio: "pipe", cwd: worktreePath, timeout: 15000 });
266
- if (retryView.status === 0 && retryView.stdout) {
267
- try {
268
- const prInfo = JSON.parse(retryView.stdout.toString());
269
- return {
270
- attempted: true,
271
- success: true,
272
- prNumber: prInfo.number,
273
- prUrl: prInfo.url,
274
- };
275
- }
276
- catch {
277
- // Fall through to error
278
- }
279
- }
280
- }
281
- console.log(chalk.yellow(` ⚠️ PR creation failed: ${prError}`));
282
- return {
283
- attempted: true,
284
- success: false,
285
- error: `gh pr create failed: ${prError}`,
286
- };
287
- }
288
- // Step 4: Extract PR URL from output and get PR details
289
- const prOutput = prResult.stdout?.toString().trim() ?? "";
290
- const prUrlMatch = prOutput.match(/https:\/\/github\.com\/[^\s]+\/pull\/(\d+)/);
291
- if (prUrlMatch) {
292
- const prNumber = parseInt(prUrlMatch[1], 10);
293
- const prUrl = prUrlMatch[0];
294
- console.log(chalk.green(` ✅ PR #${prNumber} created: ${prUrl}`));
295
- return {
296
- attempted: true,
297
- success: true,
298
- prNumber,
299
- prUrl,
300
- };
301
- }
302
- // Fallback: try gh pr view to get details
303
- const viewResult = spawnSync("gh", ["pr", "view", branch, "--json", "number,url"], { stdio: "pipe", cwd: worktreePath, timeout: 15000 });
304
- if (viewResult.status === 0 && viewResult.stdout) {
305
- try {
306
- const prInfo = JSON.parse(viewResult.stdout.toString());
307
- console.log(chalk.green(` ✅ PR #${prInfo.number} created: ${prInfo.url}`));
308
- return {
309
- attempted: true,
310
- success: true,
311
- prNumber: prInfo.number,
312
- prUrl: prInfo.url,
313
- };
314
- }
315
- catch {
316
- // Fall through
317
- }
318
- }
319
- // PR was created but we couldn't parse the URL
320
- console.log(chalk.yellow(` ⚠️ PR created but could not extract URL from output: ${prOutput}`));
321
- return {
322
- attempted: true,
323
- success: true,
324
- error: "PR created but URL extraction failed",
325
- };
326
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Run Summary Module
3
- *
4
- * Handles post-execution reporting for sequant run:
5
- * - Metrics recording (local analytics)
6
- * - Summary display with pass/fail counts
7
- *
8
- * @module run-summary
9
- */
10
- import type { ExecutionConfig, IssueResult } from "./types.js";
11
- import type { WorktreeInfo } from "./worktree-manager.js";
12
- import type { RunOptions } from "./batch-executor.js";
13
- /**
14
- * Record run metrics to local analytics file
15
- *
16
- * @param params - Metrics recording parameters
17
- */
18
- export declare function recordRunMetrics(params: {
19
- results: IssueResult[];
20
- issueNumbers: number[];
21
- config: ExecutionConfig;
22
- worktreeMap: Map<number, WorktreeInfo>;
23
- mergedOptions: RunOptions;
24
- }): Promise<void>;
25
- /**
26
- * Print the run summary to console
27
- *
28
- * @param params - Summary display parameters
29
- * @returns exit code (0 for success, 1 if any failed)
30
- */
31
- export declare function printRunSummary(params: {
32
- results: IssueResult[];
33
- logPath: string | null;
34
- config: ExecutionConfig;
35
- mergedOptions: RunOptions;
36
- }): number;