sequant 2.0.1 → 2.1.1

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 (59) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/bin/cli.js +2 -1
  4. package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
  5. package/dist/marketplace/external_plugins/sequant/.mcp.json +6 -0
  6. package/dist/marketplace/external_plugins/sequant/README.md +58 -8
  7. package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +19 -8
  8. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +36 -49
  9. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +158 -48
  10. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +354 -352
  11. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +1155 -33
  12. package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +35 -4
  13. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +2157 -104
  14. package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +1 -1
  15. package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +386 -0
  16. package/dist/marketplace/external_plugins/sequant/skills/solve/SKILL.md +38 -664
  17. package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +505 -120
  18. package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +246 -1
  19. package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +138 -1
  20. package/dist/src/commands/dashboard.js +1 -1
  21. package/dist/src/commands/doctor.js +1 -1
  22. package/dist/src/commands/init.js +10 -10
  23. package/dist/src/commands/logs.js +1 -1
  24. package/dist/src/commands/run.js +49 -39
  25. package/dist/src/commands/state.js +3 -3
  26. package/dist/src/commands/status.js +5 -5
  27. package/dist/src/commands/sync.js +8 -8
  28. package/dist/src/commands/update.js +16 -16
  29. package/dist/src/lib/cli-ui.js +20 -19
  30. package/dist/src/lib/mcp-config.js +1 -1
  31. package/dist/src/lib/merge-check/index.js +2 -2
  32. package/dist/src/lib/settings.d.ts +8 -0
  33. package/dist/src/lib/settings.js +1 -0
  34. package/dist/src/lib/shutdown.js +1 -1
  35. package/dist/src/lib/templates.js +2 -0
  36. package/dist/src/lib/wizard.js +6 -4
  37. package/dist/src/lib/workflow/batch-executor.js +1 -1
  38. package/dist/src/lib/workflow/log-writer.js +6 -6
  39. package/dist/src/lib/workflow/metrics-writer.js +5 -3
  40. package/dist/src/lib/workflow/phase-executor.js +5 -1
  41. package/dist/src/lib/workflow/platforms/github.js +5 -1
  42. package/dist/src/lib/workflow/state-cleanup.js +1 -1
  43. package/dist/src/lib/workflow/state-manager.js +15 -13
  44. package/dist/src/lib/workflow/state-rebuild.js +2 -2
  45. package/dist/src/lib/workflow/types.d.ts +11 -0
  46. package/dist/src/lib/workflow/worktree-manager.js +40 -41
  47. package/dist/src/lib/worktree-isolation.d.ts +130 -0
  48. package/dist/src/lib/worktree-isolation.js +310 -0
  49. package/package.json +8 -8
  50. package/templates/agents/sequant-explorer.md +23 -0
  51. package/templates/agents/sequant-implementer.md +18 -0
  52. package/templates/agents/sequant-qa-checker.md +24 -0
  53. package/templates/agents/sequant-testgen.md +25 -0
  54. package/templates/scripts/cleanup-worktree.sh +18 -0
  55. package/templates/skills/_shared/references/subagent-types.md +158 -48
  56. package/templates/skills/exec/SKILL.md +72 -6
  57. package/templates/skills/qa/SKILL.md +8 -217
  58. package/templates/skills/spec/SKILL.md +446 -120
  59. package/templates/skills/testgen/SKILL.md +138 -1
@@ -44,6 +44,14 @@ export interface AgentSettings {
44
44
  * Default: "haiku"
45
45
  */
46
46
  model: "haiku" | "sonnet" | "opus";
47
+ /**
48
+ * Isolate parallel agent groups in separate worktrees.
49
+ * When true, each agent in a parallel group gets its own sub-worktree,
50
+ * eliminating file conflicts structurally. Changes are merged back
51
+ * into the issue worktree after all agents complete.
52
+ * Default: false (opt-in for v1)
53
+ */
54
+ isolateParallel: boolean;
47
55
  }
48
56
  /**
49
57
  * Aider-specific settings for the aider agent driver.
@@ -30,6 +30,7 @@ export const DEFAULT_ROTATION_SETTINGS = {
30
30
  export const DEFAULT_AGENT_SETTINGS = {
31
31
  parallel: false,
32
32
  model: "haiku",
33
+ isolateParallel: false,
33
34
  };
34
35
  /**
35
36
  * Default trivial thresholds for scope assessment
@@ -138,7 +138,7 @@ export class ShutdownManager {
138
138
  return;
139
139
  }
140
140
  this._isShuttingDown = true;
141
- this.output(chalk.yellow(`\n⚠️ Received ${signal}, shutting down gracefully...`));
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.)
@@ -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("")} ${dep.displayName} - installed`));
69
+ console.log(chalk.green(` ${chalk.green("\u2714")} ${dep.displayName} - installed`));
70
70
  }
71
71
  }
72
72
  else {
73
- const marker = dep.required ? chalk.red("✗") : chalk.yellow("○");
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 = false;
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 = "skip";
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(` ⚠️ State tracking error: ${error}`));
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(`📝 Log initialized: ${this.runLog.runId}`);
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(`📝 Started logging issue #${issueNumber}`);
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(`📝 Logged phase: ${phaseLog.phase} (${phaseLog.status}) - ${phaseLog.durationSeconds.toFixed(1)}s`);
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(`📝 Completed issue #${issueLog.issueNumber} (${issueLog.status})`);
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(`📝 Log written: ${projectPath}`);
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(`📝 Rotated ${result.deletedCount} old log(s), reclaimed ${(result.bytesReclaimed / 1024).toFixed(1)} KB`);
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(`📊 Recorded run: ${run.id.slice(0, 8)}... (${run.outcome})`);
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(`📊 Metrics deleted: ${this.metricsPath}`);
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 ⚠️ Phase failed with MCP enabled, retrying without MCP...`));
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(`🔍 Orphaned: #${issueNum} (worktree not found: ${issueState.worktree})`);
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(`📊 Pruned ${pruned.length} expired issue(s): ${pruned.map((k) => `#${k}`).join(", ")}`);
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(`📊 Initialized issue #${issueNumber}: ${title}`);
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(`📊 Phase ${phase} → ${status} for issue #${issueNumber}`);
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(`📊 Issue #${issueNumber} status → ${status}`);
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(`📊 PR #${pr.number} linked to issue #${issueNumber}`);
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(`📊 Worktree updated for issue #${issueNumber}: ${worktree}`);
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(`📊 Loop iteration ${iteration} for issue #${issueNumber}`);
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(`📊 Removed issue #${issueNumber} from state`);
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(`📊 AC updated for issue #${issueNumber}: ${acceptanceCriteria.items.length} items`);
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(`📊 AC ${acId} → ${status} for issue #${issueNumber}`);
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(`📊 Scope assessment updated for issue #${issueNumber}: ${scopeAssessment.verdict}`);
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(`⚠️ Invalid log format: ${file}`);
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(`⚠️ Error reading ${file}: ${err}`);
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