sequant 1.12.0 → 1.13.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 (48) hide show
  1. package/README.md +10 -8
  2. package/dist/bin/cli.js +12 -9
  3. package/dist/src/commands/doctor.js +25 -20
  4. package/dist/src/commands/init.js +152 -65
  5. package/dist/src/commands/logs.js +7 -6
  6. package/dist/src/commands/run.d.ts +13 -1
  7. package/dist/src/commands/run.js +75 -12
  8. package/dist/src/commands/stats.js +67 -48
  9. package/dist/src/commands/status.js +30 -12
  10. package/dist/src/index.d.ts +6 -0
  11. package/dist/src/index.js +4 -0
  12. package/dist/src/lib/cli-ui.d.ts +196 -0
  13. package/dist/src/lib/cli-ui.js +544 -0
  14. package/dist/src/lib/content-analyzer.d.ts +89 -0
  15. package/dist/src/lib/content-analyzer.js +437 -0
  16. package/dist/src/lib/phase-signal.d.ts +94 -0
  17. package/dist/src/lib/phase-signal.js +171 -0
  18. package/dist/src/lib/solve-comment-parser.d.ts +84 -0
  19. package/dist/src/lib/solve-comment-parser.js +200 -0
  20. package/dist/src/lib/stack-config.d.ts +51 -0
  21. package/dist/src/lib/stack-config.js +77 -0
  22. package/dist/src/lib/stacks.d.ts +52 -0
  23. package/dist/src/lib/stacks.js +173 -0
  24. package/dist/src/lib/templates.d.ts +2 -0
  25. package/dist/src/lib/templates.js +5 -2
  26. package/dist/src/lib/upstream/assessment.d.ts +70 -0
  27. package/dist/src/lib/upstream/assessment.js +385 -0
  28. package/dist/src/lib/upstream/index.d.ts +11 -0
  29. package/dist/src/lib/upstream/index.js +14 -0
  30. package/dist/src/lib/upstream/issues.d.ts +38 -0
  31. package/dist/src/lib/upstream/issues.js +267 -0
  32. package/dist/src/lib/upstream/relevance.d.ts +50 -0
  33. package/dist/src/lib/upstream/relevance.js +209 -0
  34. package/dist/src/lib/upstream/report.d.ts +29 -0
  35. package/dist/src/lib/upstream/report.js +391 -0
  36. package/dist/src/lib/upstream/types.d.ts +207 -0
  37. package/dist/src/lib/upstream/types.js +5 -0
  38. package/dist/src/lib/workflow/log-writer.d.ts +1 -1
  39. package/dist/src/lib/workflow/metrics-schema.d.ts +3 -3
  40. package/dist/src/lib/workflow/qa-cache.d.ts +199 -0
  41. package/dist/src/lib/workflow/qa-cache.js +440 -0
  42. package/dist/src/lib/workflow/run-log-schema.d.ts +34 -6
  43. package/dist/src/lib/workflow/run-log-schema.js +12 -1
  44. package/dist/src/lib/workflow/state-schema.d.ts +4 -4
  45. package/dist/src/lib/workflow/types.d.ts +4 -0
  46. package/package.json +6 -1
  47. package/templates/skills/qa/scripts/quality-checks.sh +509 -53
  48. package/templates/skills/spec/SKILL.md +107 -5
package/README.md CHANGED
@@ -194,7 +194,7 @@ npx sequant stats # View local workflow analytics
194
194
  npx sequant dashboard # Launch real-time workflow dashboard
195
195
  ```
196
196
 
197
- See [Run Command Options](docs/run-command.md), [State Command](docs/state-command.md), and [Analytics](docs/analytics.md) for details.
197
+ See [Run Command Options](docs/reference/run-command.md), [State Command](docs/reference/state-command.md), and [Analytics](docs/reference/analytics.md) for details.
198
198
 
199
199
  ---
200
200
 
@@ -211,7 +211,7 @@ See [Run Command Options](docs/run-command.md), [State Command](docs/state-comma
211
211
  }
212
212
  ```
213
213
 
214
- See [Customization Guide](docs/customization.md) for all options.
214
+ See [Customization Guide](docs/guides/customization.md) for all options.
215
215
 
216
216
  ---
217
217
 
@@ -228,13 +228,15 @@ See [Customization Guide](docs/customization.md) for all options.
228
228
 
229
229
  ## Documentation
230
230
 
231
+ - [Quickstart](docs/guides/quickstart.md) — 5-minute guide
232
+ - [Complete Workflow](docs/guides/workflow.md) — Full workflow including post-QA patterns
231
233
  - [Getting Started](docs/getting-started/installation.md)
232
- - [What We've Built](docs/what-weve-built.md) — Comprehensive project overview
234
+ - [What We've Built](docs/internal/what-weve-built.md) — Comprehensive project overview
233
235
  - [Workflow Concepts](docs/concepts/workflow-phases.md)
234
- - [Run Command](docs/run-command.md)
235
- - [Feature Branch Workflows](docs/feature-branch-workflow.md)
236
- - [Customization](docs/customization.md)
237
- - [Plugin Updates & Versioning](docs/plugin-updates.md)
236
+ - [Run Command](docs/reference/run-command.md)
237
+ - [Git Workflows](docs/guides/git-workflows.md)
238
+ - [Customization](docs/guides/customization.md)
239
+ - [Plugin Updates & Versioning](docs/internal/plugin-updates.md)
238
240
  - [Troubleshooting](docs/troubleshooting.md)
239
241
 
240
242
  Stack guides: [Next.js](docs/stacks/nextjs.md) · [Rust](docs/stacks/rust.md) · [Python](docs/stacks/python.md) · [Go](docs/stacks/go.md)
@@ -268,7 +270,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
268
270
 
269
271
  ### Telemetry
270
272
 
271
- Sequant does not collect any usage telemetry. See [docs/telemetry.md](docs/telemetry.md) for details.
273
+ Sequant does not collect any usage telemetry. See [docs/reference/telemetry.md](docs/reference/telemetry.md) for details.
272
274
 
273
275
  ---
274
276
 
package/dist/bin/cli.js CHANGED
@@ -11,6 +11,8 @@ import { dirname, resolve } from "path";
11
11
  import { readFileSync } from "fs";
12
12
  import { initCommand } from "../src/commands/init.js";
13
13
  import { isLocalNodeModulesInstall } from "../src/lib/version-check.js";
14
+ import { configureUI, banner } from "../src/lib/cli-ui.js";
15
+ import { isCI, isStdoutTTY } from "../src/lib/tty.js";
14
16
  // Read version from package.json dynamically
15
17
  // Works from both source (bin/) and compiled (dist/bin/) locations
16
18
  function getVersion() {
@@ -46,6 +48,15 @@ const program = new Command();
46
48
  if (process.argv.includes("--no-color")) {
47
49
  process.env.FORCE_COLOR = "0";
48
50
  }
51
+ // Configure UI early based on environment and flags
52
+ configureUI({
53
+ noColor: process.argv.includes("--no-color") || !!process.env.NO_COLOR,
54
+ jsonMode: process.argv.includes("--json"),
55
+ verbose: process.argv.includes("--verbose") || process.argv.includes("-v"),
56
+ isTTY: isStdoutTTY(),
57
+ isCI: isCI(),
58
+ minimal: process.env.SEQUANT_MINIMAL === "1",
59
+ });
49
60
  // Warn if running from local node_modules (not npx cache or global)
50
61
  // This helps users who accidentally have a stale local install
51
62
  if (!process.argv.includes("--quiet") && isLocalNodeModulesInstall()) {
@@ -176,14 +187,6 @@ stateCmd
176
187
  program.parse();
177
188
  // Show help if no command provided
178
189
  if (!process.argv.slice(2).length) {
179
- console.log(chalk.green(`
180
- ╔═══════════════════════════════════════════════════════════╗
181
- ║ ║
182
- ║ ${chalk.bold("Sequant")} - Quantize your development workflow ║
183
- ║ ║
184
- ║ Sequential AI phases with quality gates ║
185
- ║ ║
186
- ╚═══════════════════════════════════════════════════════════╝
187
- `));
190
+ console.log(banner());
188
191
  program.help();
189
192
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import chalk from "chalk";
5
5
  import { execSync } from "child_process";
6
+ import { ui, colors } from "../lib/cli-ui.js";
6
7
  import { fileExists, isExecutable } from "../lib/fs.js";
7
8
  import { getManifest } from "../lib/manifest.js";
8
9
  import { commandExists, isGhAuthenticated, isNativeWindows, isWSL, checkOptionalMcpServers, getMcpServersConfig, OPTIONAL_MCP_SERVERS, } from "../lib/system.js";
@@ -67,7 +68,8 @@ export function checkClosedIssues() {
67
68
  return missingCommitIssues;
68
69
  }
69
70
  export async function doctorCommand(options = {}) {
70
- console.log(chalk.blue("\n🔍 Running health checks...\n"));
71
+ console.log(ui.headerBox("SEQUANT HEALTH CHECK"));
72
+ console.log();
71
73
  const checks = [];
72
74
  // Track gh availability and auth for conditional checks later
73
75
  let ghAvailable = false;
@@ -369,22 +371,22 @@ export async function doctorCommand(options = {}) {
369
371
  }
370
372
  }
371
373
  }
372
- // Display results
374
+ // Display results with status icons
373
375
  let passCount = 0;
374
376
  let warnCount = 0;
375
377
  let failCount = 0;
376
378
  for (const check of checks) {
377
- const icon = check.status === "pass"
378
- ? chalk.green("")
379
+ const statusType = check.status === "pass"
380
+ ? "success"
379
381
  : check.status === "warn"
380
- ? chalk.yellow("")
381
- : chalk.red("");
382
+ ? "warning"
383
+ : "error";
382
384
  const color = check.status === "pass"
383
- ? chalk.green
385
+ ? colors.success
384
386
  : check.status === "warn"
385
- ? chalk.yellow
386
- : chalk.red;
387
- console.log(`${icon} ${chalk.bold(check.name)}: ${color(check.message)}`);
387
+ ? colors.warning
388
+ : colors.error;
389
+ console.log(` ${ui.statusIcon(statusType)} ${chalk.bold(check.name)}: ${color(check.message)}`);
388
390
  if (check.status === "pass")
389
391
  passCount++;
390
392
  else if (check.status === "warn")
@@ -392,21 +394,24 @@ export async function doctorCommand(options = {}) {
392
394
  else
393
395
  failCount++;
394
396
  }
395
- // Summary
396
- console.log(chalk.bold("\nSummary:"));
397
- console.log(chalk.green(` ✓ Passed: ${passCount}`));
398
- if (warnCount > 0)
399
- console.log(chalk.yellow(` ⚠ Warnings: ${warnCount}`));
400
- if (failCount > 0)
401
- console.log(chalk.red(` ✗ Failed: ${failCount}`));
397
+ // Summary with boxed output
398
+ const totalChecks = passCount + warnCount + failCount;
399
+ let summaryTitle;
400
+ let summaryMessage;
402
401
  if (failCount > 0) {
403
- console.log(chalk.red("\n❌ Some checks failed. Run `sequant init` to fix."));
402
+ summaryTitle = `${failCount} check${failCount > 1 ? "s" : ""} failed`;
403
+ summaryMessage = `Passed: ${passCount}/${totalChecks}\nWarnings: ${warnCount}\nFailed: ${failCount}\n\nRun \`sequant init\` to fix issues.`;
404
+ console.log("\n" + ui.errorBox(summaryTitle, summaryMessage));
404
405
  process.exit(1);
405
406
  }
406
407
  else if (warnCount > 0) {
407
- console.log(chalk.yellow("\n⚠️ Some warnings found but Sequant should work."));
408
+ summaryTitle = `All checks passed (${warnCount} warning${warnCount > 1 ? "s" : ""})`;
409
+ summaryMessage = `Passed: ${passCount}/${totalChecks}\nWarnings: ${warnCount}\n\nSequant should work correctly.`;
410
+ console.log("\n" + ui.warningBox(summaryTitle, summaryMessage));
408
411
  }
409
412
  else {
410
- console.log(chalk.green("\n✅ All checks passed!"));
413
+ summaryTitle = `All ${totalChecks} checks passed!`;
414
+ summaryMessage = `Your Sequant installation is healthy.`;
415
+ console.log("\n" + ui.successBox(summaryTitle, summaryMessage));
411
416
  }
412
417
  }
@@ -3,7 +3,8 @@
3
3
  */
4
4
  import chalk from "chalk";
5
5
  import inquirer from "inquirer";
6
- import { detectStack, getStackConfig, detectPackageManager, getPackageManagerCommands, } from "../lib/stacks.js";
6
+ import { ui, colors } from "../lib/cli-ui.js";
7
+ import { detectStack, detectAllStacks, getStackConfig, detectPackageManager, getPackageManagerCommands, STACKS, } from "../lib/stacks.js";
7
8
  import { copyTemplates } from "../lib/templates.js";
8
9
  import { createManifest } from "../lib/manifest.js";
9
10
  import { saveConfig } from "../lib/config.js";
@@ -12,6 +13,7 @@ import { fileExists, ensureDir, readFile, writeFile } from "../lib/fs.js";
12
13
  import { commandExists, isGhAuthenticated, getInstallHint, } from "../lib/system.js";
13
14
  import { shouldUseInteractiveMode, getNonInteractiveReason, } from "../lib/tty.js";
14
15
  import { checkAllDependencies, displayDependencyStatus, runSetupWizard, shouldRunSetupWizard, } from "../lib/wizard.js";
16
+ import { saveStackConfig } from "../lib/stack-config.js";
15
17
  /**
16
18
  * Check prerequisites and display warnings
17
19
  */
@@ -66,7 +68,9 @@ function logDefault(label, value) {
66
68
  console.log(chalk.blue(`📦 ${label}: ${value} (default)`));
67
69
  }
68
70
  export async function initCommand(options) {
69
- console.log(chalk.green("\n🚀 Initializing Sequant...\n"));
71
+ // Show banner
72
+ console.log(ui.banner());
73
+ console.log(colors.success("\nInitializing Sequant...\n"));
70
74
  // Determine if we should use interactive mode
71
75
  const useInteractive = shouldUseInteractiveMode(options.interactive);
72
76
  const skipPrompts = options.yes || !useInteractive;
@@ -139,54 +143,116 @@ export async function initCommand(options) {
139
143
  }
140
144
  // Detect or prompt for stack
141
145
  let stack = options.stack;
146
+ let additionalStacks = [];
142
147
  if (!stack) {
143
- const detected = await detectStack();
144
- if (detected && skipPrompts) {
145
- stack = detected;
146
- logDefault("Detected stack", stack);
147
- }
148
- else if (detected && !skipPrompts) {
149
- const { confirmedStack } = await inquirer.prompt([
148
+ // Check for multi-stack project in interactive mode
149
+ const allDetectedStacks = !skipPrompts ? await detectAllStacks() : [];
150
+ if (allDetectedStacks.length > 1 && !skipPrompts) {
151
+ // Multi-stack project detected - show checkbox selection
152
+ console.log(chalk.blue(`\n🔍 Detected ${allDetectedStacks.length} stacks in this project:`));
153
+ for (const ds of allDetectedStacks) {
154
+ const location = ds.path ? ` (${ds.path}/)` : " (root)";
155
+ console.log(chalk.gray(` • ${STACKS[ds.stack]?.displayName || ds.stack}${location}`));
156
+ }
157
+ console.log();
158
+ // Multi-stack selection prompt
159
+ const { selectedStacks } = await inquirer.prompt([
150
160
  {
151
- type: "list",
152
- name: "confirmedStack",
153
- message: `Detected ${detected} project. Is this correct?`,
154
- choices: [
155
- { name: `Yes, use ${detected}`, value: detected },
156
- { name: "No, let me choose", value: null },
157
- ],
161
+ type: "checkbox",
162
+ name: "selectedStacks",
163
+ message: "Select stacks to include in your constitution:",
164
+ choices: allDetectedStacks.map((ds) => ({
165
+ name: `${STACKS[ds.stack]?.displayName || ds.stack}${ds.path ? ` (${ds.path}/)` : " (root)"}`,
166
+ value: ds.stack,
167
+ checked: true, // Pre-select all detected stacks
168
+ })),
169
+ validate: (answer) => {
170
+ if (answer.length < 1) {
171
+ return "You must select at least one stack.";
172
+ }
173
+ return true;
174
+ },
158
175
  },
159
176
  ]);
160
- stack = confirmedStack;
161
- }
162
- if (!stack && skipPrompts) {
163
- // No detection and skipping prompts: use generic as default
164
- stack = "generic";
165
- logDefault("Using stack", stack);
177
+ // First selected stack is the primary
178
+ if (selectedStacks.length > 0) {
179
+ stack = selectedStacks[0];
180
+ additionalStacks = selectedStacks.slice(1);
181
+ }
182
+ // Confirm or change primary stack if multiple selected
183
+ if (selectedStacks.length > 1) {
184
+ const { primaryStack } = await inquirer.prompt([
185
+ {
186
+ type: "list",
187
+ name: "primaryStack",
188
+ message: "Which stack should be the primary? (determines dev URL and commands)",
189
+ choices: selectedStacks.map((s) => ({
190
+ name: STACKS[s]?.displayName || s,
191
+ value: s,
192
+ })),
193
+ },
194
+ ]);
195
+ stack = primaryStack;
196
+ additionalStacks = selectedStacks.filter((s) => s !== primaryStack);
197
+ }
166
198
  }
167
- else if (!stack) {
168
- const { selectedStack } = await inquirer.prompt([
169
- {
170
- type: "list",
171
- name: "selectedStack",
172
- message: "Select your project stack:",
173
- choices: [
174
- { name: "Next.js / React", value: "nextjs" },
175
- { name: "Astro", value: "astro" },
176
- { name: "SvelteKit", value: "sveltekit" },
177
- { name: "Remix", value: "remix" },
178
- { name: "Nuxt", value: "nuxt" },
179
- { name: "Rust", value: "rust" },
180
- { name: "Python", value: "python" },
181
- { name: "Go", value: "go" },
182
- { name: "Generic (no stack-specific config)", value: "generic" },
183
- ],
184
- },
185
- ]);
186
- stack = selectedStack;
199
+ else {
200
+ // Single stack detection (original behavior)
201
+ const detected = await detectStack();
202
+ if (detected && skipPrompts) {
203
+ stack = detected;
204
+ logDefault("Detected stack", stack);
205
+ }
206
+ else if (detected && !skipPrompts) {
207
+ const { confirmedStack } = await inquirer.prompt([
208
+ {
209
+ type: "list",
210
+ name: "confirmedStack",
211
+ message: `Detected ${detected} project. Is this correct?`,
212
+ choices: [
213
+ { name: `Yes, use ${detected}`, value: detected },
214
+ { name: "No, let me choose", value: null },
215
+ ],
216
+ },
217
+ ]);
218
+ stack = confirmedStack;
219
+ }
220
+ if (!stack && skipPrompts) {
221
+ // No detection and skipping prompts: use generic as default
222
+ stack = "generic";
223
+ logDefault("Using stack", stack);
224
+ }
225
+ else if (!stack) {
226
+ const { selectedStack } = await inquirer.prompt([
227
+ {
228
+ type: "list",
229
+ name: "selectedStack",
230
+ message: "Select your project stack:",
231
+ choices: [
232
+ { name: "Next.js / React", value: "nextjs" },
233
+ { name: "Astro", value: "astro" },
234
+ { name: "SvelteKit", value: "sveltekit" },
235
+ { name: "Remix", value: "remix" },
236
+ { name: "Nuxt", value: "nuxt" },
237
+ { name: "Rust", value: "rust" },
238
+ { name: "Python", value: "python" },
239
+ { name: "Go", value: "go" },
240
+ { name: "Generic (no stack-specific config)", value: "generic" },
241
+ ],
242
+ },
243
+ ]);
244
+ stack = selectedStack;
245
+ }
187
246
  }
188
247
  }
189
- console.log(chalk.blue(`\n📋 Stack: ${stack}`));
248
+ // Display selected stacks
249
+ if (additionalStacks.length > 0) {
250
+ console.log(chalk.blue(`\n📋 Primary Stack: ${stack}`));
251
+ console.log(chalk.blue(` Additional: ${additionalStacks.join(", ")}`));
252
+ }
253
+ else {
254
+ console.log(chalk.blue(`\n📋 Stack: ${stack}`));
255
+ }
190
256
  // Detect package manager
191
257
  const packageManager = await detectPackageManager();
192
258
  if (packageManager) {
@@ -236,21 +302,24 @@ export async function initCommand(options) {
236
302
  return;
237
303
  }
238
304
  }
239
- // Create directories
240
- console.log(chalk.blue("\n📁 Creating directories..."));
305
+ // Create directories with spinner
306
+ const dirSpinner = ui.spinner("Creating directories...");
307
+ dirSpinner.start();
241
308
  await ensureDir(".claude/skills");
242
309
  await ensureDir(".claude/hooks");
243
310
  await ensureDir(".claude/memory");
244
311
  await ensureDir(".claude/.sequant");
245
312
  await ensureDir(".sequant/logs");
246
313
  await ensureDir("scripts/dev");
314
+ dirSpinner.succeed("Created directories");
247
315
  // Update .gitignore
248
316
  const gitignoreUpdated = await updateGitignore();
249
317
  if (gitignoreUpdated) {
250
- console.log(chalk.blue("📝 Updated .gitignore with Sequant entries"));
318
+ ui.printStatus("success", "Updated .gitignore with Sequant entries");
251
319
  }
252
320
  // Save config with tokens
253
- console.log(chalk.blue("💾 Saving configuration..."));
321
+ const configSpinner = ui.spinner("Saving configuration...");
322
+ configSpinner.start();
254
323
  const pmConfig = packageManager
255
324
  ? getPackageManagerCommands(packageManager)
256
325
  : getPackageManagerCommands("npm");
@@ -263,18 +332,33 @@ export async function initCommand(options) {
263
332
  stack: stack,
264
333
  initialized: new Date().toISOString(),
265
334
  });
335
+ configSpinner.succeed("Saved configuration");
336
+ // Save multi-stack configuration if additional stacks selected
337
+ if (additionalStacks.length > 0) {
338
+ const stackConfig = {
339
+ primary: { name: stack },
340
+ additional: additionalStacks.map((name) => ({ name })),
341
+ };
342
+ await saveStackConfig(stackConfig);
343
+ console.log(chalk.blue("📋 Saved multi-stack configuration"));
344
+ }
266
345
  // Create default settings
267
- console.log(chalk.blue("⚙️ Creating default settings..."));
346
+ const settingsSpinner = ui.spinner("Creating default settings...");
347
+ settingsSpinner.start();
268
348
  await createDefaultSettings();
349
+ settingsSpinner.succeed("Created default settings");
269
350
  // Copy templates (with symlinks for scripts unless --no-symlinks)
270
- console.log(chalk.blue("📄 Copying templates..."));
351
+ const templatesSpinner = ui.spinner("Copying templates...");
352
+ templatesSpinner.start();
271
353
  const { scriptsSymlinked, symlinkResults } = await copyTemplates(stack, tokens, {
272
354
  noSymlinks: options.noSymlinks,
273
355
  force: options.force,
356
+ additionalStacks,
274
357
  });
358
+ templatesSpinner.succeed("Copied templates");
275
359
  // Report symlink status
276
360
  if (scriptsSymlinked) {
277
- console.log(chalk.blue("🔗 Created symlinks for scripts/dev/"));
361
+ ui.printStatus("success", "Created symlinks for scripts/dev/");
278
362
  }
279
363
  else if (!options.noSymlinks && symlinkResults) {
280
364
  // Some symlinks may have fallen back to copies
@@ -294,8 +378,10 @@ export async function initCommand(options) {
294
378
  }
295
379
  }
296
380
  // Create manifest
297
- console.log(chalk.blue("📋 Creating manifest..."));
381
+ const manifestSpinner = ui.spinner("Creating manifest...");
382
+ manifestSpinner.start();
298
383
  await createManifest(stack, packageManager ?? undefined);
384
+ manifestSpinner.succeed("Created manifest");
299
385
  // Build optional suggestions section
300
386
  const optionalSuggestions = suggestions.filter((s) => s.startsWith("Optional"));
301
387
  const optionalSection = optionalSuggestions.length > 0
@@ -306,19 +392,20 @@ export async function initCommand(options) {
306
392
  const prereqReminder = hasRemainingIssues
307
393
  ? `\n${chalk.yellow("⚠️ Remember to install missing dependencies before using issue workflows.")}\n${chalk.gray(" Run 'sequant doctor' to verify your setup.\n")}`
308
394
  : "";
309
- // Success message
310
- console.log(chalk.green(`
311
- Sequant initialized successfully!
312
- ${prereqReminder}
313
- ${chalk.bold("Next steps:")}
314
- 1. Review .claude/memory/constitution.md and customize for your project
315
- 2. Start using workflow commands in Claude Code:
395
+ // Success message with boxed output
396
+ const nextStepsContent = `${chalk.bold("Next steps:")}
397
+ 1. Review .claude/memory/constitution.md
398
+ 2. Start using workflow commands:
316
399
 
317
- ${chalk.cyan("/spec 123")} - Plan implementation for issue #123
318
- ${chalk.cyan("/exec 123")} - Implement the feature
319
- ${chalk.cyan("/qa 123")} - Quality review
320
- ${optionalSection}
321
- ${chalk.bold("Documentation:")}
322
- https://github.com/admarble/sequant#readme
323
- `));
400
+ ${chalk.cyan("/spec 123")} - Plan implementation
401
+ ${chalk.cyan("/exec 123")} - Implement the feature
402
+ ${chalk.cyan("/qa 123")} - Quality review`;
403
+ console.log("\n" + ui.successBox("Sequant initialized successfully!", nextStepsContent));
404
+ if (prereqReminder) {
405
+ console.log(prereqReminder);
406
+ }
407
+ if (optionalSection) {
408
+ console.log(optionalSection);
409
+ }
410
+ console.log(chalk.gray("\nDocumentation: https://github.com/admarble/sequant#readme\n"));
324
411
  }
@@ -7,6 +7,7 @@ import chalk from "chalk";
7
7
  import * as fs from "fs";
8
8
  import * as path from "path";
9
9
  import * as os from "os";
10
+ import { ui, colors } from "../lib/cli-ui.js";
10
11
  import { RunLogSchema, LOG_PATHS, } from "../lib/workflow/run-log-schema.js";
11
12
  import { manualRotate, getLogStats, formatBytes, } from "../lib/workflow/log-rotation.js";
12
13
  import { getSettings } from "../lib/settings.js";
@@ -177,17 +178,17 @@ export async function logsCommand(options) {
177
178
  await handleRotation(logDir, options.dryRun ?? false);
178
179
  return;
179
180
  }
180
- console.log(chalk.blue("\n📝 Sequant Run Logs\n"));
181
- console.log(chalk.gray(` Log directory: ${logDir}`));
181
+ console.log(ui.headerBox("SEQUANT RUN LOGS"));
182
+ console.log(colors.muted(`\n Log directory: ${logDir}`));
182
183
  // List log files
183
184
  const logFiles = listLogFiles(logDir);
184
185
  if (logFiles.length === 0) {
185
- console.log(chalk.yellow("\n No logs found."));
186
- console.log(chalk.gray(" Run `npx sequant run <issues>` to generate logs."));
186
+ console.log(colors.warning("\n No logs found."));
187
+ console.log(colors.muted(" Run `npx sequant run <issues>` to generate logs."));
187
188
  console.log("");
188
189
  return;
189
190
  }
190
- console.log(chalk.gray(` Found ${logFiles.length} log file(s)\n`));
191
+ console.log(colors.muted(` Found ${logFiles.length} log file(s)\n`));
191
192
  // Parse all logs
192
193
  const logs = logFiles
193
194
  .map((filename) => {
@@ -215,7 +216,7 @@ export async function logsCommand(options) {
215
216
  displayLogSummary(log, filename);
216
217
  }
217
218
  // Summary
218
- console.log(chalk.blue("\n" + "━".repeat(50)));
219
+ console.log("\n" + ui.divider());
219
220
  const totalPassed = filteredLogs.reduce((sum, { log }) => sum + log.summary.passed, 0);
220
221
  const totalFailed = filteredLogs.reduce((sum, { log }) => sum + log.summary.failed, 0);
221
222
  const totalDuration = filteredLogs.reduce((sum, { log }) => sum + log.summary.totalDurationSeconds, 0);
@@ -4,7 +4,19 @@
4
4
  * Runs the Sequant workflow (/spec → /exec → /qa) for one or more issues
5
5
  * using the Claude Agent SDK for proper skill invocation.
6
6
  */
7
- import { Phase } from "../lib/workflow/types.js";
7
+ import { Phase, QaVerdict } from "../lib/workflow/types.js";
8
+ /**
9
+ * Parse QA verdict from phase output
10
+ *
11
+ * Looks for verdict patterns in the QA output:
12
+ * - "### Verdict: READY_FOR_MERGE"
13
+ * - "**Verdict:** AC_NOT_MET"
14
+ * - "Verdict: AC_MET_BUT_NOT_A_PLUS"
15
+ *
16
+ * @param output - The captured output from QA phase
17
+ * @returns The parsed verdict or null if not found
18
+ */
19
+ export declare function parseQaVerdict(output: string): QaVerdict | null;
8
20
  /**
9
21
  * List all active worktrees with their branches
10
22
  */