sequant 1.9.0 → 1.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.
- package/README.md +4 -1
- package/dist/bin/cli.js +1 -0
- package/dist/src/commands/run.d.ts +5 -0
- package/dist/src/commands/run.js +151 -21
- package/dist/src/commands/status.d.ts +18 -2
- package/dist/src/commands/status.js +321 -2
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +5 -0
- package/dist/src/lib/settings.d.ts +6 -0
- package/dist/src/lib/workflow/state-hook.d.ts +69 -0
- package/dist/src/lib/workflow/state-hook.js +166 -0
- package/dist/src/lib/workflow/state-manager.d.ts +136 -0
- package/dist/src/lib/workflow/state-manager.js +329 -0
- package/dist/src/lib/workflow/state-schema.d.ts +224 -0
- package/dist/src/lib/workflow/state-schema.js +190 -0
- package/dist/src/lib/workflow/state-utils.d.ts +66 -0
- package/dist/src/lib/workflow/state-utils.js +243 -0
- package/package.json +1 -1
- package/templates/scripts/new-feature.sh +23 -13
package/README.md
CHANGED
|
@@ -84,6 +84,7 @@ Run without Claude Code UI:
|
|
|
84
84
|
npx sequant run 123 # Single issue
|
|
85
85
|
npx sequant run 1 2 3 # Batch (parallel)
|
|
86
86
|
npx sequant run 123 --quality-loop
|
|
87
|
+
npx sequant run 123 --base feature/dashboard # Custom base branch
|
|
87
88
|
```
|
|
88
89
|
|
|
89
90
|
---
|
|
@@ -148,7 +149,8 @@ See [Run Command Options](docs/run-command.md) for advanced usage.
|
|
|
148
149
|
{
|
|
149
150
|
"run": {
|
|
150
151
|
"qualityLoop": false,
|
|
151
|
-
"maxIterations": 3
|
|
152
|
+
"maxIterations": 3,
|
|
153
|
+
"defaultBase": "feature/dashboard" // Optional: custom default base branch
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
```
|
|
@@ -173,6 +175,7 @@ See [Customization Guide](docs/customization.md) for all options.
|
|
|
173
175
|
- [Getting Started](docs/getting-started/installation.md)
|
|
174
176
|
- [Workflow Concepts](docs/concepts/workflow-phases.md)
|
|
175
177
|
- [Run Command](docs/run-command.md)
|
|
178
|
+
- [Feature Branch Workflows](docs/feature-branch-workflow.md)
|
|
176
179
|
- [Customization](docs/customization.md)
|
|
177
180
|
- [Troubleshooting](docs/troubleshooting.md)
|
|
178
181
|
|
package/dist/bin/cli.js
CHANGED
|
@@ -99,6 +99,7 @@ program
|
|
|
99
99
|
.option("--testgen", "Run testgen phase after spec")
|
|
100
100
|
.option("--quiet", "Suppress version warnings and non-essential output")
|
|
101
101
|
.option("--chain", "Chain issues: each branches from previous (requires --sequential)")
|
|
102
|
+
.option("--base <branch>", "Base branch for worktree creation (default: main or settings.run.defaultBase)")
|
|
102
103
|
.action(runCommand);
|
|
103
104
|
program
|
|
104
105
|
.command("logs")
|
|
@@ -66,6 +66,11 @@ interface RunOptions {
|
|
|
66
66
|
quiet?: boolean;
|
|
67
67
|
/** Chain issues: each branches from previous (requires --sequential) */
|
|
68
68
|
chain?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Base branch for worktree creation.
|
|
71
|
+
* Resolution priority: this CLI flag → settings.run.defaultBase → 'main'
|
|
72
|
+
*/
|
|
73
|
+
base?: string;
|
|
69
74
|
}
|
|
70
75
|
/**
|
|
71
76
|
* Main run command
|
package/dist/src/commands/run.js
CHANGED
|
@@ -13,6 +13,7 @@ import { getManifest } from "../lib/manifest.js";
|
|
|
13
13
|
import { getSettings } from "../lib/settings.js";
|
|
14
14
|
import { PM_CONFIG } from "../lib/stacks.js";
|
|
15
15
|
import { LogWriter, createPhaseLogFromTiming, } from "../lib/workflow/log-writer.js";
|
|
16
|
+
import { StateManager, } from "../lib/workflow/state-manager.js";
|
|
16
17
|
import { DEFAULT_PHASES, DEFAULT_CONFIG, } from "../lib/workflow/types.js";
|
|
17
18
|
import { ShutdownManager } from "../lib/shutdown.js";
|
|
18
19
|
import { checkVersionCached, getVersionWarning } from "../lib/version-check.js";
|
|
@@ -137,17 +138,29 @@ async function ensureWorktree(issueNumber, title, verbose, packageManager, baseB
|
|
|
137
138
|
console.log(chalk.gray(` 🌿 Creating worktree for #${issueNumber}...`));
|
|
138
139
|
}
|
|
139
140
|
// Determine the base for the new branch
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
|
|
141
|
+
// For custom base branches, use origin/<branch> if it's a remote-style reference
|
|
142
|
+
// For local branches (chain mode), use as-is
|
|
143
|
+
const isLocalBranch = baseBranch && !baseBranch.startsWith("origin/") && baseBranch !== "main";
|
|
144
|
+
const baseRef = baseBranch
|
|
145
|
+
? isLocalBranch
|
|
146
|
+
? baseBranch
|
|
147
|
+
: baseBranch.startsWith("origin/")
|
|
148
|
+
? baseBranch
|
|
149
|
+
: `origin/${baseBranch}`
|
|
150
|
+
: "origin/main";
|
|
151
|
+
// Fetch the base branch to ensure worktree starts from fresh baseline
|
|
152
|
+
const branchToFetch = baseBranch
|
|
153
|
+
? baseBranch.replace(/^origin\//, "")
|
|
154
|
+
: "main";
|
|
155
|
+
if (!isLocalBranch) {
|
|
143
156
|
if (verbose) {
|
|
144
|
-
console.log(chalk.gray(` 🔄 Fetching latest
|
|
157
|
+
console.log(chalk.gray(` 🔄 Fetching latest ${branchToFetch}...`));
|
|
145
158
|
}
|
|
146
|
-
const fetchResult = spawnSync("git", ["fetch", "origin",
|
|
159
|
+
const fetchResult = spawnSync("git", ["fetch", "origin", branchToFetch], {
|
|
147
160
|
stdio: "pipe",
|
|
148
161
|
});
|
|
149
162
|
if (fetchResult.status !== 0 && verbose) {
|
|
150
|
-
console.log(chalk.yellow(` ⚠️ Could not fetch origin
|
|
163
|
+
console.log(chalk.yellow(` ⚠️ Could not fetch origin/${branchToFetch}, using local state`));
|
|
151
164
|
}
|
|
152
165
|
}
|
|
153
166
|
else if (verbose) {
|
|
@@ -216,12 +229,14 @@ async function ensureWorktree(issueNumber, title, verbose, packageManager, baseB
|
|
|
216
229
|
}
|
|
217
230
|
/**
|
|
218
231
|
* Ensure worktrees exist for all issues before execution
|
|
232
|
+
* @param baseBranch - Optional base branch for worktree creation (default: main)
|
|
219
233
|
*/
|
|
220
|
-
async function ensureWorktrees(issues, verbose, packageManager) {
|
|
234
|
+
async function ensureWorktrees(issues, verbose, packageManager, baseBranch) {
|
|
221
235
|
const worktrees = new Map();
|
|
222
|
-
|
|
236
|
+
const baseDisplay = baseBranch || "main";
|
|
237
|
+
console.log(chalk.blue(`\n 📂 Preparing worktrees from ${baseDisplay}...`));
|
|
223
238
|
for (const issue of issues) {
|
|
224
|
-
const worktree = await ensureWorktree(issue.number, issue.title, verbose, packageManager);
|
|
239
|
+
const worktree = await ensureWorktree(issue.number, issue.title, verbose, packageManager, baseBranch);
|
|
225
240
|
if (worktree) {
|
|
226
241
|
worktrees.set(issue.number, worktree);
|
|
227
242
|
}
|
|
@@ -236,11 +251,14 @@ async function ensureWorktrees(issues, verbose, packageManager) {
|
|
|
236
251
|
/**
|
|
237
252
|
* Ensure worktrees exist for all issues in chain mode
|
|
238
253
|
* Each issue branches from the previous issue's branch
|
|
254
|
+
* @param baseBranch - Optional starting base branch for the chain (default: main)
|
|
239
255
|
*/
|
|
240
|
-
async function ensureWorktreesChain(issues, verbose, packageManager) {
|
|
256
|
+
async function ensureWorktreesChain(issues, verbose, packageManager, baseBranch) {
|
|
241
257
|
const worktrees = new Map();
|
|
242
|
-
|
|
243
|
-
|
|
258
|
+
const baseDisplay = baseBranch || "main";
|
|
259
|
+
console.log(chalk.blue(`\n 🔗 Preparing chained worktrees from ${baseDisplay}...`));
|
|
260
|
+
// First issue starts from the specified base branch (or main)
|
|
261
|
+
let previousBranch = baseBranch;
|
|
244
262
|
for (const issue of issues) {
|
|
245
263
|
const worktree = await ensureWorktree(issue.number, issue.title, verbose, packageManager, previousBranch);
|
|
246
264
|
if (worktree) {
|
|
@@ -264,7 +282,7 @@ async function ensureWorktreesChain(issues, verbose, packageManager) {
|
|
|
264
282
|
.filter((i) => worktrees.has(i.number))
|
|
265
283
|
.map((i) => `#${i.number}`)
|
|
266
284
|
.join(" → ");
|
|
267
|
-
console.log(chalk.gray(` Chain:
|
|
285
|
+
console.log(chalk.gray(` Chain: ${baseDisplay} → ${chainOrder}`));
|
|
268
286
|
}
|
|
269
287
|
return worktrees;
|
|
270
288
|
}
|
|
@@ -869,6 +887,8 @@ export async function runCommand(issues, options) {
|
|
|
869
887
|
// Determine if we should auto-detect phases from labels
|
|
870
888
|
const autoDetectPhases = !options.phases && settings.run.autoDetectPhases;
|
|
871
889
|
mergedOptions.autoDetectPhases = autoDetectPhases;
|
|
890
|
+
// Resolve base branch: CLI flag → settings.run.defaultBase → 'main'
|
|
891
|
+
const resolvedBaseBranch = options.base ?? settings.run.defaultBase ?? undefined;
|
|
872
892
|
// Parse issue numbers (or use batch mode)
|
|
873
893
|
let issueNumbers;
|
|
874
894
|
let batches = null;
|
|
@@ -953,6 +973,12 @@ export async function runCommand(issues, options) {
|
|
|
953
973
|
});
|
|
954
974
|
await logWriter.initialize(runConfig);
|
|
955
975
|
}
|
|
976
|
+
// Initialize state manager for persistent workflow state tracking
|
|
977
|
+
// State tracking is always enabled (unless dry run)
|
|
978
|
+
let stateManager = null;
|
|
979
|
+
if (!config.dryRun) {
|
|
980
|
+
stateManager = new StateManager({ verbose: config.verbose });
|
|
981
|
+
}
|
|
956
982
|
// Initialize shutdown manager for graceful interruption handling
|
|
957
983
|
const shutdown = new ShutdownManager();
|
|
958
984
|
// Register log writer finalization as cleanup task
|
|
@@ -986,12 +1012,18 @@ export async function runCommand(issues, options) {
|
|
|
986
1012
|
if (logWriter) {
|
|
987
1013
|
console.log(chalk.gray(` Logging: JSON (run ${logWriter.getRunId()?.slice(0, 8)}...)`));
|
|
988
1014
|
}
|
|
1015
|
+
if (stateManager) {
|
|
1016
|
+
console.log(chalk.gray(` State tracking: enabled`));
|
|
1017
|
+
}
|
|
989
1018
|
console.log(chalk.gray(` Issues: ${issueNumbers.map((n) => `#${n}`).join(", ")}`));
|
|
990
1019
|
// Worktree isolation is enabled by default for multi-issue runs
|
|
991
1020
|
const useWorktreeIsolation = mergedOptions.worktreeIsolation !== false && issueNumbers.length > 0;
|
|
992
1021
|
if (useWorktreeIsolation) {
|
|
993
1022
|
console.log(chalk.gray(` Worktree isolation: enabled`));
|
|
994
1023
|
}
|
|
1024
|
+
if (resolvedBaseBranch) {
|
|
1025
|
+
console.log(chalk.gray(` Base branch: ${resolvedBaseBranch}`));
|
|
1026
|
+
}
|
|
995
1027
|
if (mergedOptions.chain) {
|
|
996
1028
|
console.log(chalk.gray(` Chain mode: enabled (each issue branches from previous)`));
|
|
997
1029
|
}
|
|
@@ -1009,10 +1041,10 @@ export async function runCommand(issues, options) {
|
|
|
1009
1041
|
}));
|
|
1010
1042
|
// Use chain mode or standard worktree creation
|
|
1011
1043
|
if (mergedOptions.chain) {
|
|
1012
|
-
worktreeMap = await ensureWorktreesChain(issueData, config.verbose, manifest.packageManager);
|
|
1044
|
+
worktreeMap = await ensureWorktreesChain(issueData, config.verbose, manifest.packageManager, resolvedBaseBranch);
|
|
1013
1045
|
}
|
|
1014
1046
|
else {
|
|
1015
|
-
worktreeMap = await ensureWorktrees(issueData, config.verbose, manifest.packageManager);
|
|
1047
|
+
worktreeMap = await ensureWorktrees(issueData, config.verbose, manifest.packageManager, resolvedBaseBranch);
|
|
1016
1048
|
}
|
|
1017
1049
|
// Register cleanup tasks for newly created worktrees (not pre-existing ones)
|
|
1018
1050
|
for (const [issueNum, worktree] of worktreeMap.entries()) {
|
|
@@ -1038,7 +1070,7 @@ export async function runCommand(issues, options) {
|
|
|
1038
1070
|
for (let batchIdx = 0; batchIdx < batches.length; batchIdx++) {
|
|
1039
1071
|
const batch = batches[batchIdx];
|
|
1040
1072
|
console.log(chalk.blue(`\n Batch ${batchIdx + 1}/${batches.length}: Issues ${batch.map((n) => `#${n}`).join(", ")}`));
|
|
1041
|
-
const batchResults = await executeBatch(batch, config, logWriter, mergedOptions, issueInfoMap, worktreeMap, shutdown);
|
|
1073
|
+
const batchResults = await executeBatch(batch, config, logWriter, stateManager, mergedOptions, issueInfoMap, worktreeMap, shutdown);
|
|
1042
1074
|
results.push(...batchResults);
|
|
1043
1075
|
// Check if batch failed and we should stop
|
|
1044
1076
|
const batchFailed = batchResults.some((r) => !r.success);
|
|
@@ -1060,7 +1092,7 @@ export async function runCommand(issues, options) {
|
|
|
1060
1092
|
if (logWriter) {
|
|
1061
1093
|
logWriter.startIssue(issueNumber, issueInfo.title, issueInfo.labels);
|
|
1062
1094
|
}
|
|
1063
|
-
const result = await runIssueWithLogging(issueNumber, config, logWriter, issueInfo.labels, mergedOptions, worktreeInfo?.path, shutdown, mergedOptions.chain);
|
|
1095
|
+
const result = await runIssueWithLogging(issueNumber, config, logWriter, stateManager, issueInfo.title, issueInfo.labels, mergedOptions, worktreeInfo?.path, worktreeInfo?.branch, shutdown, mergedOptions.chain);
|
|
1064
1096
|
results.push(result);
|
|
1065
1097
|
// Complete issue logging
|
|
1066
1098
|
if (logWriter) {
|
|
@@ -1094,7 +1126,7 @@ export async function runCommand(issues, options) {
|
|
|
1094
1126
|
if (logWriter) {
|
|
1095
1127
|
logWriter.startIssue(issueNumber, issueInfo.title, issueInfo.labels);
|
|
1096
1128
|
}
|
|
1097
|
-
const result = await runIssueWithLogging(issueNumber, config, logWriter, issueInfo.labels, mergedOptions, worktreeInfo?.path, shutdown, false);
|
|
1129
|
+
const result = await runIssueWithLogging(issueNumber, config, logWriter, stateManager, issueInfo.title, issueInfo.labels, mergedOptions, worktreeInfo?.path, worktreeInfo?.branch, shutdown, false);
|
|
1098
1130
|
results.push(result);
|
|
1099
1131
|
// Complete issue logging
|
|
1100
1132
|
if (logWriter) {
|
|
@@ -1151,7 +1183,7 @@ export async function runCommand(issues, options) {
|
|
|
1151
1183
|
/**
|
|
1152
1184
|
* Execute a batch of issues
|
|
1153
1185
|
*/
|
|
1154
|
-
async function executeBatch(issueNumbers, config, logWriter, options, issueInfoMap, worktreeMap, shutdownManager) {
|
|
1186
|
+
async function executeBatch(issueNumbers, config, logWriter, stateManager, options, issueInfoMap, worktreeMap, shutdownManager) {
|
|
1155
1187
|
const results = [];
|
|
1156
1188
|
for (const issueNumber of issueNumbers) {
|
|
1157
1189
|
// Check if shutdown was triggered
|
|
@@ -1167,7 +1199,7 @@ async function executeBatch(issueNumbers, config, logWriter, options, issueInfoM
|
|
|
1167
1199
|
if (logWriter) {
|
|
1168
1200
|
logWriter.startIssue(issueNumber, issueInfo.title, issueInfo.labels);
|
|
1169
1201
|
}
|
|
1170
|
-
const result = await runIssueWithLogging(issueNumber, config, logWriter, issueInfo.labels, options, worktreeInfo?.path, shutdownManager, false);
|
|
1202
|
+
const result = await runIssueWithLogging(issueNumber, config, logWriter, stateManager, issueInfo.title, issueInfo.labels, options, worktreeInfo?.path, worktreeInfo?.branch, shutdownManager, false);
|
|
1171
1203
|
results.push(result);
|
|
1172
1204
|
// Complete issue logging
|
|
1173
1205
|
if (logWriter) {
|
|
@@ -1179,7 +1211,7 @@ async function executeBatch(issueNumbers, config, logWriter, options, issueInfoM
|
|
|
1179
1211
|
/**
|
|
1180
1212
|
* Execute all phases for a single issue with logging and quality loop
|
|
1181
1213
|
*/
|
|
1182
|
-
async function runIssueWithLogging(issueNumber, config, logWriter, labels, options, worktreePath, shutdownManager, chainMode) {
|
|
1214
|
+
async function runIssueWithLogging(issueNumber, config, logWriter, stateManager, issueTitle, labels, options, worktreePath, branch, shutdownManager, chainMode) {
|
|
1183
1215
|
const startTime = Date.now();
|
|
1184
1216
|
const phaseResults = [];
|
|
1185
1217
|
let loopTriggered = false;
|
|
@@ -1188,6 +1220,32 @@ async function runIssueWithLogging(issueNumber, config, logWriter, labels, optio
|
|
|
1188
1220
|
if (worktreePath) {
|
|
1189
1221
|
console.log(chalk.gray(` Worktree: ${worktreePath}`));
|
|
1190
1222
|
}
|
|
1223
|
+
// Initialize state tracking for this issue
|
|
1224
|
+
if (stateManager) {
|
|
1225
|
+
try {
|
|
1226
|
+
const existingState = await stateManager.getIssueState(issueNumber);
|
|
1227
|
+
if (!existingState) {
|
|
1228
|
+
await stateManager.initializeIssue(issueNumber, issueTitle, {
|
|
1229
|
+
worktree: worktreePath,
|
|
1230
|
+
branch,
|
|
1231
|
+
qualityLoop: config.qualityLoop,
|
|
1232
|
+
maxIterations: config.maxIterations,
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
else {
|
|
1236
|
+
// Update worktree info if it changed
|
|
1237
|
+
if (worktreePath && branch) {
|
|
1238
|
+
await stateManager.updateWorktreeInfo(issueNumber, worktreePath, branch);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
catch (error) {
|
|
1243
|
+
// State tracking errors shouldn't stop execution
|
|
1244
|
+
if (config.verbose) {
|
|
1245
|
+
console.log(chalk.yellow(` ⚠️ State tracking error: ${error}`));
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1191
1249
|
// Determine phases for this specific issue
|
|
1192
1250
|
let phases;
|
|
1193
1251
|
let detectedQualityLoop = false;
|
|
@@ -1205,6 +1263,15 @@ async function runIssueWithLogging(issueNumber, config, logWriter, labels, optio
|
|
|
1205
1263
|
// Run spec first to get recommended workflow
|
|
1206
1264
|
console.log(chalk.gray(` Running spec to determine workflow...`));
|
|
1207
1265
|
console.log(chalk.gray(` ⏳ spec...`));
|
|
1266
|
+
// Track spec phase start in state
|
|
1267
|
+
if (stateManager) {
|
|
1268
|
+
try {
|
|
1269
|
+
await stateManager.updatePhaseStatus(issueNumber, "spec", "in_progress");
|
|
1270
|
+
}
|
|
1271
|
+
catch {
|
|
1272
|
+
// State tracking errors shouldn't stop execution
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1208
1275
|
const specStartTime = new Date();
|
|
1209
1276
|
// Note: spec runs in main repo (not worktree) for planning
|
|
1210
1277
|
const specResult = await executePhase(issueNumber, "spec", config, sessionId, worktreePath, // Will be ignored for spec (non-isolated phase)
|
|
@@ -1212,6 +1279,15 @@ async function runIssueWithLogging(issueNumber, config, logWriter, labels, optio
|
|
|
1212
1279
|
const specEndTime = new Date();
|
|
1213
1280
|
if (specResult.sessionId) {
|
|
1214
1281
|
sessionId = specResult.sessionId;
|
|
1282
|
+
// Update session ID in state for resume capability
|
|
1283
|
+
if (stateManager) {
|
|
1284
|
+
try {
|
|
1285
|
+
await stateManager.updateSessionId(issueNumber, specResult.sessionId);
|
|
1286
|
+
}
|
|
1287
|
+
catch {
|
|
1288
|
+
// State tracking errors shouldn't stop execution
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1215
1291
|
}
|
|
1216
1292
|
phaseResults.push(specResult);
|
|
1217
1293
|
specAlreadyRan = true;
|
|
@@ -1224,6 +1300,18 @@ async function runIssueWithLogging(issueNumber, config, logWriter, labels, optio
|
|
|
1224
1300
|
: "failure", { error: specResult.error });
|
|
1225
1301
|
logWriter.logPhase(phaseLog);
|
|
1226
1302
|
}
|
|
1303
|
+
// Track spec phase completion in state
|
|
1304
|
+
if (stateManager) {
|
|
1305
|
+
try {
|
|
1306
|
+
const phaseStatus = specResult.success ? "completed" : "failed";
|
|
1307
|
+
await stateManager.updatePhaseStatus(issueNumber, "spec", phaseStatus, {
|
|
1308
|
+
error: specResult.error,
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
catch {
|
|
1312
|
+
// State tracking errors shouldn't stop execution
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1227
1315
|
if (!specResult.success) {
|
|
1228
1316
|
console.log(chalk.red(` ✗ spec: ${specResult.error}`));
|
|
1229
1317
|
const durationSeconds = (Date.now() - startTime) / 1000;
|
|
@@ -1294,12 +1382,30 @@ async function runIssueWithLogging(issueNumber, config, logWriter, labels, optio
|
|
|
1294
1382
|
let phasesFailed = false;
|
|
1295
1383
|
for (const phase of phases) {
|
|
1296
1384
|
console.log(chalk.gray(` ⏳ ${phase}...`));
|
|
1385
|
+
// Track phase start in state
|
|
1386
|
+
if (stateManager) {
|
|
1387
|
+
try {
|
|
1388
|
+
await stateManager.updatePhaseStatus(issueNumber, phase, "in_progress");
|
|
1389
|
+
}
|
|
1390
|
+
catch {
|
|
1391
|
+
// State tracking errors shouldn't stop execution
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1297
1394
|
const phaseStartTime = new Date();
|
|
1298
1395
|
const result = await executePhase(issueNumber, phase, config, sessionId, worktreePath, shutdownManager);
|
|
1299
1396
|
const phaseEndTime = new Date();
|
|
1300
1397
|
// Capture session ID for subsequent phases
|
|
1301
1398
|
if (result.sessionId) {
|
|
1302
1399
|
sessionId = result.sessionId;
|
|
1400
|
+
// Update session ID in state for resume capability
|
|
1401
|
+
if (stateManager) {
|
|
1402
|
+
try {
|
|
1403
|
+
await stateManager.updateSessionId(issueNumber, result.sessionId);
|
|
1404
|
+
}
|
|
1405
|
+
catch {
|
|
1406
|
+
// State tracking errors shouldn't stop execution
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1303
1409
|
}
|
|
1304
1410
|
phaseResults.push(result);
|
|
1305
1411
|
// Log phase result
|
|
@@ -1311,6 +1417,20 @@ async function runIssueWithLogging(issueNumber, config, logWriter, labels, optio
|
|
|
1311
1417
|
: "failure", { error: result.error });
|
|
1312
1418
|
logWriter.logPhase(phaseLog);
|
|
1313
1419
|
}
|
|
1420
|
+
// Track phase completion in state
|
|
1421
|
+
if (stateManager) {
|
|
1422
|
+
try {
|
|
1423
|
+
const phaseStatus = result.success
|
|
1424
|
+
? "completed"
|
|
1425
|
+
: result.error?.includes("Timeout")
|
|
1426
|
+
? "failed"
|
|
1427
|
+
: "failed";
|
|
1428
|
+
await stateManager.updatePhaseStatus(issueNumber, phase, phaseStatus, { error: result.error });
|
|
1429
|
+
}
|
|
1430
|
+
catch {
|
|
1431
|
+
// State tracking errors shouldn't stop execution
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1314
1434
|
if (result.success) {
|
|
1315
1435
|
const duration = result.durationSeconds
|
|
1316
1436
|
? ` (${formatDuration(result.durationSeconds)})`
|
|
@@ -1355,6 +1475,16 @@ async function runIssueWithLogging(issueNumber, config, logWriter, labels, optio
|
|
|
1355
1475
|
// Success is determined by whether all phases completed in any iteration,
|
|
1356
1476
|
// not whether all accumulated phase results passed (which would fail after loop recovery)
|
|
1357
1477
|
const success = completedSuccessfully;
|
|
1478
|
+
// Update final issue status in state
|
|
1479
|
+
if (stateManager) {
|
|
1480
|
+
try {
|
|
1481
|
+
const finalStatus = success ? "ready_for_merge" : "in_progress";
|
|
1482
|
+
await stateManager.updateIssueStatus(issueNumber, finalStatus);
|
|
1483
|
+
}
|
|
1484
|
+
catch {
|
|
1485
|
+
// State tracking errors shouldn't stop execution
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1358
1488
|
// Create checkpoint commit in chain mode after QA passes
|
|
1359
1489
|
if (success && chainMode && worktreePath) {
|
|
1360
1490
|
createCheckpointCommit(worktreePath, issueNumber, config.verbose);
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* sequant status - Show version and
|
|
2
|
+
* sequant status - Show version, configuration, and workflow state
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
4
|
+
export interface StatusCommandOptions {
|
|
5
|
+
/** Show only issues state */
|
|
6
|
+
issues?: boolean;
|
|
7
|
+
/** Show details for a specific issue */
|
|
8
|
+
issue?: number;
|
|
9
|
+
/** Output as JSON */
|
|
10
|
+
json?: boolean;
|
|
11
|
+
/** Rebuild state from run logs */
|
|
12
|
+
rebuild?: boolean;
|
|
13
|
+
/** Clean up stale/orphaned entries */
|
|
14
|
+
cleanup?: boolean;
|
|
15
|
+
/** Only show what would be cleaned (used with --cleanup) */
|
|
16
|
+
dryRun?: boolean;
|
|
17
|
+
/** Remove entries older than this many days (used with --cleanup) */
|
|
18
|
+
maxAge?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function statusCommand(options?: StatusCommandOptions): Promise<void>;
|