sequant 2.0.1 → 2.1.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bin/cli.js +2 -1
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/.mcp.json +6 -0
- package/dist/marketplace/external_plugins/sequant/README.md +58 -8
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +19 -8
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +36 -49
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +158 -48
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +354 -352
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +1155 -33
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +35 -4
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +2157 -104
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +386 -0
- package/dist/marketplace/external_plugins/sequant/skills/solve/SKILL.md +38 -664
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +505 -120
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +246 -1
- package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +138 -1
- package/dist/src/commands/dashboard.js +1 -1
- package/dist/src/commands/doctor.js +1 -1
- package/dist/src/commands/init.js +10 -10
- package/dist/src/commands/logs.js +1 -1
- package/dist/src/commands/run.js +49 -39
- package/dist/src/commands/state.js +3 -3
- package/dist/src/commands/status.js +5 -5
- package/dist/src/commands/sync.js +8 -8
- package/dist/src/commands/update.js +16 -16
- package/dist/src/lib/cli-ui.js +20 -19
- package/dist/src/lib/merge-check/index.js +2 -2
- package/dist/src/lib/settings.d.ts +8 -0
- package/dist/src/lib/settings.js +1 -0
- package/dist/src/lib/shutdown.js +1 -1
- package/dist/src/lib/templates.js +2 -0
- package/dist/src/lib/wizard.js +6 -4
- package/dist/src/lib/workflow/batch-executor.js +1 -1
- package/dist/src/lib/workflow/log-writer.js +6 -6
- package/dist/src/lib/workflow/metrics-writer.js +5 -3
- package/dist/src/lib/workflow/phase-executor.js +5 -1
- package/dist/src/lib/workflow/platforms/github.js +5 -1
- package/dist/src/lib/workflow/state-cleanup.js +1 -1
- package/dist/src/lib/workflow/state-manager.js +15 -13
- package/dist/src/lib/workflow/state-rebuild.js +2 -2
- package/dist/src/lib/workflow/types.d.ts +11 -0
- package/dist/src/lib/workflow/worktree-manager.js +40 -41
- package/dist/src/lib/worktree-isolation.d.ts +130 -0
- package/dist/src/lib/worktree-isolation.js +310 -0
- package/package.json +8 -8
- package/templates/agents/sequant-explorer.md +23 -0
- package/templates/agents/sequant-implementer.md +18 -0
- package/templates/agents/sequant-qa-checker.md +24 -0
- package/templates/agents/sequant-testgen.md +25 -0
- package/templates/scripts/cleanup-worktree.sh +18 -0
- package/templates/skills/_shared/references/subagent-types.md +158 -48
- package/templates/skills/exec/SKILL.md +72 -6
- package/templates/skills/qa/SKILL.md +8 -217
- package/templates/skills/spec/SKILL.md +446 -120
- package/templates/skills/testgen/SKILL.md +138 -1
package/dist/src/lib/settings.js
CHANGED
package/dist/src/lib/shutdown.js
CHANGED
|
@@ -138,7 +138,7 @@ export class ShutdownManager {
|
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
140
140
|
this._isShuttingDown = true;
|
|
141
|
-
this.output(chalk.yellow(`\n
|
|
141
|
+
this.output(chalk.yellow(`\n! Received ${signal}, shutting down gracefully...`));
|
|
142
142
|
// Abort all in-flight phases immediately
|
|
143
143
|
if (this.abortControllers.size > 0) {
|
|
144
144
|
const count = this.abortControllers.size;
|
|
@@ -205,6 +205,8 @@ export async function copyTemplates(stack, tokens, options = {}) {
|
|
|
205
205
|
}
|
|
206
206
|
// Copy skills
|
|
207
207
|
await copyDir(join(templatesDir, "skills"), ".claude/skills");
|
|
208
|
+
// Copy agent definitions
|
|
209
|
+
await copyDir(join(templatesDir, "agents"), ".claude/agents");
|
|
208
210
|
// Copy hooks
|
|
209
211
|
await copyDir(join(templatesDir, "hooks"), ".claude/hooks");
|
|
210
212
|
// Copy memory (constitution, etc.)
|
package/dist/src/lib/wizard.js
CHANGED
|
@@ -66,11 +66,13 @@ export function displayDependencyStatus(result) {
|
|
|
66
66
|
console.log(chalk.yellow(` ${chalk.yellow("!")} ${dep.displayName} - installed but not authenticated`));
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
|
-
console.log(chalk.green(` ${chalk.green("
|
|
69
|
+
console.log(chalk.green(` ${chalk.green("\u2714")} ${dep.displayName} - installed`));
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
|
-
const marker = dep.required
|
|
73
|
+
const marker = dep.required
|
|
74
|
+
? chalk.red("\u2716")
|
|
75
|
+
: chalk.yellow("\u00B7");
|
|
74
76
|
const status = dep.required
|
|
75
77
|
? "not installed (required)"
|
|
76
78
|
: "not installed (optional)";
|
|
@@ -135,7 +137,7 @@ export async function runSetupWizard(result, options = {}) {
|
|
|
135
137
|
};
|
|
136
138
|
}
|
|
137
139
|
// Ask if user wants to set up missing dependencies
|
|
138
|
-
let setupDeps
|
|
140
|
+
let setupDeps;
|
|
139
141
|
try {
|
|
140
142
|
const response = await inquirer.prompt([
|
|
141
143
|
{
|
|
@@ -181,7 +183,7 @@ export async function runSetupWizard(result, options = {}) {
|
|
|
181
183
|
console.log(` ${instruction}`);
|
|
182
184
|
}
|
|
183
185
|
console.log();
|
|
184
|
-
let action
|
|
186
|
+
let action;
|
|
185
187
|
try {
|
|
186
188
|
const response = await inquirer.prompt([
|
|
187
189
|
{
|
|
@@ -299,7 +299,7 @@ export async function runIssueWithLogging(ctx) {
|
|
|
299
299
|
catch (error) {
|
|
300
300
|
// State tracking errors shouldn't stop execution
|
|
301
301
|
if (config.verbose) {
|
|
302
|
-
log(chalk.yellow(`
|
|
302
|
+
log(chalk.yellow(` ! State tracking error: ${error}`));
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
}
|
|
@@ -53,7 +53,7 @@ export class LogWriter {
|
|
|
53
53
|
await this.ensureLogDirectory(userPath);
|
|
54
54
|
}
|
|
55
55
|
if (this.verbose && this.runLog) {
|
|
56
|
-
console.log(
|
|
56
|
+
console.log(`Log initialized: ${this.runLog.runId}`);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
@@ -79,7 +79,7 @@ export class LogWriter {
|
|
|
79
79
|
// Keep currentIssue in sync for callers that don't pass issueNumber
|
|
80
80
|
this.currentIssue = issueData;
|
|
81
81
|
if (this.verbose) {
|
|
82
|
-
console.log(
|
|
82
|
+
console.log(`Log started: issue #${issueNumber}`);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
@@ -102,7 +102,7 @@ export class LogWriter {
|
|
|
102
102
|
issue.status = "partial";
|
|
103
103
|
}
|
|
104
104
|
if (this.verbose) {
|
|
105
|
-
console.log(
|
|
105
|
+
console.log(`Log phase: ${phaseLog.phase} (${phaseLog.status}) - ${phaseLog.durationSeconds.toFixed(1)}s`);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
@@ -160,7 +160,7 @@ export class LogWriter {
|
|
|
160
160
|
this.currentIssue = null;
|
|
161
161
|
}
|
|
162
162
|
if (this.verbose) {
|
|
163
|
-
console.log(
|
|
163
|
+
console.log(`Log complete: issue #${issueLog.issueNumber} (${issueLog.status})`);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
@@ -197,13 +197,13 @@ export class LogWriter {
|
|
|
197
197
|
await this.writeLogFile(userPath, finalLog);
|
|
198
198
|
}
|
|
199
199
|
if (this.verbose) {
|
|
200
|
-
console.log(
|
|
200
|
+
console.log(`Log written: ${projectPath}`);
|
|
201
201
|
}
|
|
202
202
|
// Auto-rotate if needed
|
|
203
203
|
if (this.rotation.enabled) {
|
|
204
204
|
const result = rotateIfNeeded(this.logPath, this.rotation);
|
|
205
205
|
if (result.rotated && this.verbose) {
|
|
206
|
-
console.log(
|
|
206
|
+
console.log(`Log rotated: ${result.deletedCount} old log(s), reclaimed ${(result.bytesReclaimed / 1024).toFixed(1)} KB`);
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
return projectPath;
|
|
@@ -66,7 +66,9 @@ export class MetricsWriter {
|
|
|
66
66
|
}
|
|
67
67
|
catch (error) {
|
|
68
68
|
if (error instanceof SyntaxError) {
|
|
69
|
-
throw new Error(`Invalid JSON in metrics file: ${error.message}
|
|
69
|
+
throw new Error(`Invalid JSON in metrics file: ${error.message}`, {
|
|
70
|
+
cause: error,
|
|
71
|
+
});
|
|
70
72
|
}
|
|
71
73
|
throw error;
|
|
72
74
|
}
|
|
@@ -119,7 +121,7 @@ export class MetricsWriter {
|
|
|
119
121
|
metrics.runs.push(run);
|
|
120
122
|
await this.saveMetrics(metrics);
|
|
121
123
|
if (this.verbose) {
|
|
122
|
-
console.log(
|
|
124
|
+
console.log(`Metrics recorded: ${run.id.slice(0, 8)}... (${run.outcome})`);
|
|
123
125
|
}
|
|
124
126
|
return run;
|
|
125
127
|
}
|
|
@@ -162,7 +164,7 @@ export class MetricsWriter {
|
|
|
162
164
|
fs.unlinkSync(this.metricsPath);
|
|
163
165
|
this.cachedMetrics = null;
|
|
164
166
|
if (this.verbose) {
|
|
165
|
-
console.log(
|
|
167
|
+
console.log(`Metrics deleted: ${this.metricsPath}`);
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
170
|
}
|
|
@@ -329,6 +329,10 @@ async function executePhase(issueNumber, phase, config, sessionId, worktreePath,
|
|
|
329
329
|
if (config.failedAcs) {
|
|
330
330
|
env.SEQUANT_FAILED_ACS = config.failedAcs;
|
|
331
331
|
}
|
|
332
|
+
// Propagate parallel isolation mode to exec skill (#485)
|
|
333
|
+
if (config.isolateParallel) {
|
|
334
|
+
env.SEQUANT_ISOLATE_PARALLEL = "true";
|
|
335
|
+
}
|
|
332
336
|
// Track whether we're actively streaming verbose output
|
|
333
337
|
// Pausing spinner once per streaming session prevents truncation from rapid pause/resume cycles
|
|
334
338
|
// (Issue #283: ora's stop() clears the current line, which can truncate output when
|
|
@@ -508,7 +512,7 @@ delayFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
|
508
512
|
// This handles npx-based MCP servers that fail on first run due to cold-cache issues.
|
|
509
513
|
// Skip for `loop` phase — MCP is never the cause of loop failures (#488).
|
|
510
514
|
if (config.mcp && !lastResult.success && !skipColdStartRetry) {
|
|
511
|
-
console.log(chalk.yellow(`\n
|
|
515
|
+
console.log(chalk.yellow(`\n ! Phase failed with MCP enabled, retrying without MCP...`));
|
|
512
516
|
// Create config copy with MCP disabled
|
|
513
517
|
const configWithoutMcp = {
|
|
514
518
|
...config,
|
|
@@ -344,8 +344,12 @@ export class GitHubProvider {
|
|
|
344
344
|
stdio: ["pipe", "pipe", "pipe"],
|
|
345
345
|
timeout: 30000,
|
|
346
346
|
});
|
|
347
|
-
if (result.status !== 0 || !result.stdout)
|
|
347
|
+
if (result.status !== 0 || !result.stdout) {
|
|
348
|
+
if (result.stderr) {
|
|
349
|
+
console.error(`gh issue create failed: ${result.stderr.trim()}`);
|
|
350
|
+
}
|
|
348
351
|
return null;
|
|
352
|
+
}
|
|
349
353
|
const url = result.stdout.trim();
|
|
350
354
|
const numberMatch = url.match(/\/issues\/(\d+)$/);
|
|
351
355
|
const number = numberMatch ? parseInt(numberMatch[1], 10) : 0;
|
|
@@ -73,7 +73,7 @@ export async function cleanupStaleEntries(options = {}) {
|
|
|
73
73
|
if (issueState.worktree &&
|
|
74
74
|
!activeWorktrees.includes(issueState.worktree)) {
|
|
75
75
|
if (options.verbose) {
|
|
76
|
-
console.log(
|
|
76
|
+
console.log(`Orphaned: #${issueNum} (worktree not found: ${issueState.worktree})`);
|
|
77
77
|
}
|
|
78
78
|
// Check if this issue has a PR and if it's merged
|
|
79
79
|
let prMerged = false;
|
|
@@ -100,7 +100,7 @@ export class StateManager {
|
|
|
100
100
|
continue;
|
|
101
101
|
}
|
|
102
102
|
if (Date.now() - start > this.lockTimeout) {
|
|
103
|
-
throw new Error(`Timeout acquiring state lock after ${this.lockTimeout}ms
|
|
103
|
+
throw new Error(`Timeout acquiring state lock after ${this.lockTimeout}ms`, { cause: err });
|
|
104
104
|
}
|
|
105
105
|
// Wait and retry
|
|
106
106
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
@@ -150,7 +150,9 @@ export class StateManager {
|
|
|
150
150
|
}
|
|
151
151
|
catch (error) {
|
|
152
152
|
if (error instanceof SyntaxError) {
|
|
153
|
-
throw new Error(`Invalid JSON in state file: ${error.message}
|
|
153
|
+
throw new Error(`Invalid JSON in state file: ${error.message}`, {
|
|
154
|
+
cause: error,
|
|
155
|
+
});
|
|
154
156
|
}
|
|
155
157
|
throw error;
|
|
156
158
|
}
|
|
@@ -182,7 +184,7 @@ export class StateManager {
|
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
if (pruned.length > 0 && this.verbose) {
|
|
185
|
-
console.log(
|
|
187
|
+
console.log(`State: Pruned ${pruned.length} expired issue(s): ${pruned.map((k) => `#${k}`).join(", ")}`);
|
|
186
188
|
}
|
|
187
189
|
}
|
|
188
190
|
catch {
|
|
@@ -272,7 +274,7 @@ export class StateManager {
|
|
|
272
274
|
await this.saveState(state);
|
|
273
275
|
});
|
|
274
276
|
if (this.verbose) {
|
|
275
|
-
console.log(
|
|
277
|
+
console.log(`State: Initialized issue #${issueNumber}: ${title}`);
|
|
276
278
|
}
|
|
277
279
|
}
|
|
278
280
|
/**
|
|
@@ -313,7 +315,7 @@ export class StateManager {
|
|
|
313
315
|
await this.saveState(state);
|
|
314
316
|
});
|
|
315
317
|
if (this.verbose) {
|
|
316
|
-
console.log(
|
|
318
|
+
console.log(`State: Phase ${phase} → ${status} for issue #${issueNumber}`);
|
|
317
319
|
}
|
|
318
320
|
}
|
|
319
321
|
/**
|
|
@@ -335,7 +337,7 @@ export class StateManager {
|
|
|
335
337
|
await this.saveState(state);
|
|
336
338
|
});
|
|
337
339
|
if (this.verbose) {
|
|
338
|
-
console.log(
|
|
340
|
+
console.log(`State: Issue #${issueNumber} status → ${status}`);
|
|
339
341
|
}
|
|
340
342
|
}
|
|
341
343
|
/**
|
|
@@ -353,7 +355,7 @@ export class StateManager {
|
|
|
353
355
|
await this.saveState(state);
|
|
354
356
|
});
|
|
355
357
|
if (this.verbose) {
|
|
356
|
-
console.log(
|
|
358
|
+
console.log(`State: PR #${pr.number} linked to issue #${issueNumber}`);
|
|
357
359
|
}
|
|
358
360
|
}
|
|
359
361
|
/**
|
|
@@ -372,7 +374,7 @@ export class StateManager {
|
|
|
372
374
|
await this.saveState(state);
|
|
373
375
|
});
|
|
374
376
|
if (this.verbose) {
|
|
375
|
-
console.log(
|
|
377
|
+
console.log(`State: Worktree updated for issue #${issueNumber}: ${worktree}`);
|
|
376
378
|
}
|
|
377
379
|
}
|
|
378
380
|
/**
|
|
@@ -407,7 +409,7 @@ export class StateManager {
|
|
|
407
409
|
await this.saveState(state);
|
|
408
410
|
});
|
|
409
411
|
if (this.verbose) {
|
|
410
|
-
console.log(
|
|
412
|
+
console.log(`State: Loop iteration ${iteration} for issue #${issueNumber}`);
|
|
411
413
|
}
|
|
412
414
|
}
|
|
413
415
|
/**
|
|
@@ -423,7 +425,7 @@ export class StateManager {
|
|
|
423
425
|
await this.saveState(state);
|
|
424
426
|
});
|
|
425
427
|
if (this.verbose) {
|
|
426
|
-
console.log(
|
|
428
|
+
console.log(`State: Removed issue #${issueNumber} from state`);
|
|
427
429
|
}
|
|
428
430
|
}
|
|
429
431
|
// === Acceptance Criteria Operations ===
|
|
@@ -444,7 +446,7 @@ export class StateManager {
|
|
|
444
446
|
await this.saveState(state);
|
|
445
447
|
});
|
|
446
448
|
if (this.verbose) {
|
|
447
|
-
console.log(
|
|
449
|
+
console.log(`State: AC updated for issue #${issueNumber}: ${acceptanceCriteria.items.length} items`);
|
|
448
450
|
}
|
|
449
451
|
}
|
|
450
452
|
/**
|
|
@@ -487,7 +489,7 @@ export class StateManager {
|
|
|
487
489
|
await this.saveState(state);
|
|
488
490
|
});
|
|
489
491
|
if (this.verbose) {
|
|
490
|
-
console.log(
|
|
492
|
+
console.log(`State: AC ${acId} → ${status} for issue #${issueNumber}`);
|
|
491
493
|
}
|
|
492
494
|
}
|
|
493
495
|
// === Scope Assessment Operations ===
|
|
@@ -508,7 +510,7 @@ export class StateManager {
|
|
|
508
510
|
await this.saveState(state);
|
|
509
511
|
});
|
|
510
512
|
if (this.verbose) {
|
|
511
|
-
console.log(
|
|
513
|
+
console.log(`State: Scope assessment updated for issue #${issueNumber}: ${scopeAssessment.verdict}`);
|
|
512
514
|
}
|
|
513
515
|
}
|
|
514
516
|
/**
|
|
@@ -55,7 +55,7 @@ export async function rebuildStateFromLogs(options = {}) {
|
|
|
55
55
|
const log = RunLogSchema.safeParse(logData);
|
|
56
56
|
if (!log.success) {
|
|
57
57
|
if (options.verbose) {
|
|
58
|
-
console.log(
|
|
58
|
+
console.log(`! Invalid log format: ${file}`);
|
|
59
59
|
}
|
|
60
60
|
continue;
|
|
61
61
|
}
|
|
@@ -109,7 +109,7 @@ export async function rebuildStateFromLogs(options = {}) {
|
|
|
109
109
|
}
|
|
110
110
|
catch (err) {
|
|
111
111
|
if (options.verbose) {
|
|
112
|
-
console.log(
|
|
112
|
+
console.log(`! Error reading ${file}: ${err}`);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
}
|
|
@@ -71,6 +71,11 @@ export interface ExecutionConfig {
|
|
|
71
71
|
* Default: "claude-code"
|
|
72
72
|
*/
|
|
73
73
|
agent?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Isolate parallel agent groups in separate worktrees.
|
|
76
|
+
* Propagated as SEQUANT_ISOLATE_PARALLEL env var to exec skill.
|
|
77
|
+
*/
|
|
78
|
+
isolateParallel?: boolean;
|
|
74
79
|
/**
|
|
75
80
|
* Aider-specific configuration. Passed to AiderDriver when agent is "aider".
|
|
76
81
|
*/
|
|
@@ -228,6 +233,12 @@ export interface RunOptions {
|
|
|
228
233
|
* Default: "claude-code"
|
|
229
234
|
*/
|
|
230
235
|
agent?: string;
|
|
236
|
+
/**
|
|
237
|
+
* Isolate parallel agent groups in separate worktrees.
|
|
238
|
+
* When true, each agent in a parallel group gets its own sub-worktree.
|
|
239
|
+
* Resolution priority: CLI flag → settings.agents.isolateParallel → false
|
|
240
|
+
*/
|
|
241
|
+
isolateParallel?: boolean;
|
|
231
242
|
}
|
|
232
243
|
/**
|
|
233
244
|
* CLI arguments for run command
|