sequant 2.1.1 → 2.2.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 (45) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/bin/cli.js +1 -0
  4. package/dist/src/commands/init.d.ts +1 -0
  5. package/dist/src/commands/init.js +122 -3
  6. package/dist/src/commands/run-compat.d.ts +14 -0
  7. package/dist/src/commands/run-compat.js +12 -0
  8. package/dist/src/commands/run-display.d.ts +17 -0
  9. package/dist/src/commands/run-display.js +116 -0
  10. package/dist/src/commands/run.d.ts +4 -26
  11. package/dist/src/commands/run.js +47 -772
  12. package/dist/src/commands/status.js +24 -1
  13. package/dist/src/index.d.ts +11 -0
  14. package/dist/src/index.js +9 -0
  15. package/dist/src/lib/errors.d.ts +93 -0
  16. package/dist/src/lib/errors.js +97 -0
  17. package/dist/src/lib/settings.d.ts +236 -0
  18. package/dist/src/lib/settings.js +482 -37
  19. package/dist/src/lib/skill-version.d.ts +19 -0
  20. package/dist/src/lib/skill-version.js +68 -0
  21. package/dist/src/lib/templates.d.ts +1 -0
  22. package/dist/src/lib/templates.js +1 -1
  23. package/dist/src/lib/workflow/batch-executor.js +13 -5
  24. package/dist/src/lib/workflow/config-resolver.d.ts +50 -0
  25. package/dist/src/lib/workflow/config-resolver.js +167 -0
  26. package/dist/src/lib/workflow/error-classifier.d.ts +17 -7
  27. package/dist/src/lib/workflow/error-classifier.js +113 -15
  28. package/dist/src/lib/workflow/phase-executor.d.ts +31 -0
  29. package/dist/src/lib/workflow/phase-executor.js +143 -48
  30. package/dist/src/lib/workflow/run-log-schema.d.ts +12 -0
  31. package/dist/src/lib/workflow/run-log-schema.js +7 -1
  32. package/dist/src/lib/workflow/run-orchestrator.d.ts +161 -0
  33. package/dist/src/lib/workflow/run-orchestrator.js +510 -0
  34. package/dist/src/lib/workflow/worktree-manager.d.ts +4 -3
  35. package/dist/src/lib/workflow/worktree-manager.js +61 -11
  36. package/package.json +1 -1
  37. package/templates/skills/assess/SKILL.md +239 -77
  38. package/templates/skills/exec/SKILL.md +7 -68
  39. package/templates/skills/fullsolve/SKILL.md +303 -137
  40. package/templates/skills/qa/SKILL.md +42 -46
  41. package/templates/skills/qa/scripts/quality-checks.sh +47 -1
  42. package/templates/skills/spec/SKILL.md +183 -982
  43. package/templates/skills/spec/references/quality-checklist.md +75 -0
  44. package/templates/skills/test/SKILL.md +0 -27
  45. package/templates/skills/testgen/SKILL.md +0 -27
@@ -8,7 +8,7 @@
8
8
  {
9
9
  "name": "sequant",
10
10
  "description": "Structured workflow system for Claude Code - GitHub issue resolution with spec, exec, test, and QA phases. Includes 17 skills, MCP server with workflow tools, and pre/post-tool hooks.",
11
- "version": "2.1.0",
11
+ "version": "2.2.0",
12
12
  "author": {
13
13
  "name": "sequant-io",
14
14
  "email": "hello@sequant.io"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sequant",
3
3
  "description": "Structured workflow system for Claude Code - GitHub issue resolution with spec, exec, test, and QA phases",
4
- "version": "2.1.1",
4
+ "version": "2.2.0",
5
5
  "author": {
6
6
  "name": "sequant-io",
7
7
  "email": "hello@sequant.io"
package/dist/bin/cli.js CHANGED
@@ -86,6 +86,7 @@ program
86
86
  .option("--no-symlinks", "Use copies instead of symlinks for scripts/dev/ files")
87
87
  .option("--no-agents-md", "Skip AGENTS.md generation")
88
88
  .option("--mcp", "Add Sequant MCP server to detected clients (use with --yes)")
89
+ .option("--upgrade-skills", "Upgrade skill files from installed package templates (with diff preview)")
89
90
  .action(initCommand);
90
91
  program
91
92
  .command("update")
@@ -10,6 +10,7 @@ interface InitOptions {
10
10
  noSymlinks?: boolean;
11
11
  agentsMd?: boolean;
12
12
  mcp?: boolean;
13
+ upgradeSkills?: boolean;
13
14
  }
14
15
  export declare function initCommand(options: InitOptions): Promise<void>;
15
16
  export {};
@@ -2,13 +2,16 @@
2
2
  * sequant init - Initialize Sequant in a project
3
3
  */
4
4
  import chalk from "chalk";
5
+ import { diffLines } from "diff";
5
6
  import inquirer from "inquirer";
7
+ import { join, dirname } from "path";
8
+ import { readdir } from "fs/promises";
6
9
  import { ui, colors } from "../lib/cli-ui.js";
7
10
  import { detectStack, detectAllStacks, getStackConfig, detectPackageManager, getPackageManagerCommands, STACKS, } from "../lib/stacks.js";
8
11
  import { copyTemplates } from "../lib/templates.js";
9
12
  import { createManifest } from "../lib/manifest.js";
10
13
  import { saveConfig } from "../lib/config.js";
11
- import { createDefaultSettings } from "../lib/settings.js";
14
+ import { createDefaultSettings, generateSettingsReference, } from "../lib/settings.js";
12
15
  import { detectAndSaveConventions } from "../lib/conventions-detector.js";
13
16
  import { fileExists, ensureDir, readFile, writeFile } from "../lib/fs.js";
14
17
  import { generateAgentsMd, writeAgentsMd } from "../lib/agents-md.js";
@@ -70,6 +73,11 @@ function logDefault(label, value) {
70
73
  console.log(chalk.blue(`${label}: ${value} (default)`));
71
74
  }
72
75
  export async function initCommand(options) {
76
+ // Handle --upgrade-skills: update skill files from installed package templates
77
+ if (options.upgradeSkills) {
78
+ await upgradeSkills();
79
+ return;
80
+ }
73
81
  // Show banner
74
82
  console.log(ui.banner());
75
83
  console.log(colors.success("\nInitializing Sequant...\n"));
@@ -348,11 +356,12 @@ export async function initCommand(options) {
348
356
  await saveStackConfig(stackConfig);
349
357
  console.log(chalk.blue("📋 Saved multi-stack configuration"));
350
358
  }
351
- // Create default settings
359
+ // Create default settings + reference doc (AC-4)
352
360
  const settingsSpinner = ui.spinner("Creating default settings...");
353
361
  settingsSpinner.start();
354
362
  await createDefaultSettings();
355
- settingsSpinner.succeed("Created default settings");
363
+ await writeFile(".sequant/settings.reference.md", generateSettingsReference());
364
+ settingsSpinner.succeed("Created default settings + reference doc");
356
365
  // Detect codebase conventions
357
366
  const conventionsSpinner = ui.spinner("Detecting codebase conventions...");
358
367
  conventionsSpinner.start();
@@ -502,3 +511,113 @@ export async function initCommand(options) {
502
511
  }
503
512
  console.log(chalk.gray("\nDocumentation: https://github.com/sequant-io/sequant#readme\n"));
504
513
  }
514
+ /**
515
+ * Upgrade installed skill files from the sequant package's templates.
516
+ * Shows a diff preview for each changed file and asks for confirmation.
517
+ */
518
+ async function upgradeSkills() {
519
+ console.log(chalk.bold("\nUpgrading skills from package templates...\n"));
520
+ const installedDir = ".claude/skills";
521
+ if (!(await fileExists(installedDir))) {
522
+ console.log(chalk.red("No skills directory found. Run `sequant init` first."));
523
+ return;
524
+ }
525
+ // Resolve the package's templates/skills directory
526
+ const { getTemplatesDir } = await import("../lib/templates.js");
527
+ const templateSkillsDir = join(getTemplatesDir(), "skills");
528
+ // Collect all files from both directories
529
+ const changes = [];
530
+ const newFiles = [];
531
+ async function compareDir(templateDir, installedBaseDir, relativePrefix) {
532
+ let entries;
533
+ try {
534
+ entries = await readdir(templateDir, { withFileTypes: true });
535
+ }
536
+ catch {
537
+ return;
538
+ }
539
+ for (const entry of entries) {
540
+ const relPath = join(relativePrefix, entry.name);
541
+ const templatePath = join(templateDir, entry.name);
542
+ const installedPath = join(installedBaseDir, relPath);
543
+ if (entry.isDirectory()) {
544
+ await compareDir(templatePath, installedBaseDir, relPath);
545
+ }
546
+ else {
547
+ const templateContent = await readFile(templatePath);
548
+ const exists = await fileExists(installedPath);
549
+ if (!exists) {
550
+ newFiles.push({ path: relPath, content: templateContent });
551
+ }
552
+ else {
553
+ const installedContent = await readFile(installedPath);
554
+ if (installedContent !== templateContent) {
555
+ changes.push({
556
+ path: relPath,
557
+ installed: installedContent,
558
+ template: templateContent,
559
+ });
560
+ }
561
+ }
562
+ }
563
+ }
564
+ }
565
+ await compareDir(templateSkillsDir, installedDir, "");
566
+ if (changes.length === 0 && newFiles.length === 0) {
567
+ console.log(chalk.green("All skills are up to date."));
568
+ return;
569
+ }
570
+ // Show summary
571
+ console.log(chalk.bold("Changes found:"));
572
+ if (changes.length > 0) {
573
+ console.log(chalk.yellow(` Modified: ${changes.length} file(s)`));
574
+ }
575
+ if (newFiles.length > 0) {
576
+ console.log(chalk.green(` New: ${newFiles.length} file(s)`));
577
+ }
578
+ console.log();
579
+ // Show diffs for modified files
580
+ for (const change of changes) {
581
+ console.log(chalk.yellow(`--- ${change.path} ---`));
582
+ const diff = diffLines(change.installed, change.template);
583
+ for (const part of diff) {
584
+ if (part.added) {
585
+ process.stdout.write(chalk.green(part.value));
586
+ }
587
+ else if (part.removed) {
588
+ process.stdout.write(chalk.red(part.value));
589
+ }
590
+ // Skip unchanged lines in diff output
591
+ }
592
+ console.log();
593
+ }
594
+ // Show new files
595
+ for (const file of newFiles) {
596
+ console.log(chalk.green(`+++ ${file.path} (new)`));
597
+ }
598
+ // Ask for confirmation
599
+ const isInteractive = shouldUseInteractiveMode();
600
+ if (isInteractive) {
601
+ const { proceed } = await inquirer.prompt([
602
+ {
603
+ type: "confirm",
604
+ name: "proceed",
605
+ message: `Apply ${changes.length + newFiles.length} skill update(s)?`,
606
+ default: true,
607
+ },
608
+ ]);
609
+ if (!proceed) {
610
+ console.log(chalk.gray("Aborted."));
611
+ return;
612
+ }
613
+ }
614
+ // Apply changes
615
+ for (const change of changes) {
616
+ await writeFile(join(installedDir, change.path), change.template);
617
+ }
618
+ for (const file of newFiles) {
619
+ await ensureDir(dirname(join(installedDir, file.path)));
620
+ await writeFile(join(installedDir, file.path), file.content);
621
+ }
622
+ console.log(chalk.green(`\nUpgraded ${changes.length + newFiles.length} skill file(s).`));
623
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Backward-compatible re-exports from run.ts.
3
+ *
4
+ * Consumers that import workflow utilities from "commands/run" continue to work.
5
+ * New code should import directly from the source modules.
6
+ */
7
+ export { normalizeCommanderOptions } from "../lib/workflow/config-resolver.js";
8
+ export { parseQaVerdict, formatDuration, executePhaseWithRetry, } from "../lib/workflow/phase-executor.js";
9
+ export { detectDefaultBranch, checkWorktreeFreshness, removeStaleWorktree, listWorktrees, getWorktreeChangedFiles, getWorktreeDiffStats, readCacheMetrics, filterResumedPhases, ensureWorktree, createCheckpointCommit, reinstallIfLockfileChanged, rebaseBeforePR, createPR, } from "../lib/workflow/worktree-manager.js";
10
+ export type { WorktreeInfo, RebaseResult, PRCreationResult, } from "../lib/workflow/worktree-manager.js";
11
+ export { detectPhasesFromLabels, parseRecommendedWorkflow, determinePhasesForIssue, } from "../lib/workflow/phase-mapper.js";
12
+ export { getIssueInfo, sortByDependencies, parseBatches, getEnvConfig, executeBatch, runIssueWithLogging, } from "../lib/workflow/batch-executor.js";
13
+ export type { RunOptions } from "../lib/workflow/types.js";
14
+ export { logNonFatalWarning } from "../lib/workflow/run-orchestrator.js";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Backward-compatible re-exports from run.ts.
3
+ *
4
+ * Consumers that import workflow utilities from "commands/run" continue to work.
5
+ * New code should import directly from the source modules.
6
+ */
7
+ export { normalizeCommanderOptions } from "../lib/workflow/config-resolver.js";
8
+ export { parseQaVerdict, formatDuration, executePhaseWithRetry, } from "../lib/workflow/phase-executor.js";
9
+ export { detectDefaultBranch, checkWorktreeFreshness, removeStaleWorktree, listWorktrees, getWorktreeChangedFiles, getWorktreeDiffStats, readCacheMetrics, filterResumedPhases, ensureWorktree, createCheckpointCommit, reinstallIfLockfileChanged, rebaseBeforePR, createPR, } from "../lib/workflow/worktree-manager.js";
10
+ export { detectPhasesFromLabels, parseRecommendedWorkflow, determinePhasesForIssue, } from "../lib/workflow/phase-mapper.js";
11
+ export { getIssueInfo, sortByDependencies, parseBatches, getEnvConfig, executeBatch, runIssueWithLogging, } from "../lib/workflow/batch-executor.js";
12
+ export { logNonFatalWarning } from "../lib/workflow/run-orchestrator.js";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Display helpers for `sequant run` — pre-run config block + post-run summary.
3
+ *
4
+ * Kept separate from run.ts so the adapter stays thin (see AC-2 of #503).
5
+ */
6
+ import type { ResolvedRun, RunResult } from "../lib/workflow/run-orchestrator.js";
7
+ /**
8
+ * Print pre-run config block.
9
+ *
10
+ * Columnar alignment via 15-char label padding. Conditional rows only
11
+ * appear when non-default, matching the pre-#503 format.
12
+ */
13
+ export declare function displayConfig(r: ResolvedRun): void;
14
+ /**
15
+ * Print post-run summary: per-issue status, log path, reflection, tips.
16
+ */
17
+ export declare function displaySummary(result: RunResult): void;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Display helpers for `sequant run` — pre-run config block + post-run summary.
3
+ *
4
+ * Kept separate from run.ts so the adapter stays thin (see AC-2 of #503).
5
+ */
6
+ import chalk from "chalk";
7
+ import { ui, colors } from "../lib/cli-ui.js";
8
+ import { formatDuration } from "../lib/workflow/phase-executor.js";
9
+ import { analyzeRun, formatReflection } from "../lib/workflow/run-reflect.js";
10
+ /**
11
+ * Print pre-run config block.
12
+ *
13
+ * Columnar alignment via 15-char label padding. Conditional rows only
14
+ * appear when non-default, matching the pre-#503 format.
15
+ */
16
+ export function displayConfig(r) {
17
+ const pad = (label) => label.padEnd(15);
18
+ const row = (label, value) => console.log(chalk.gray(` ${pad(label)}${value}`));
19
+ row("Stack", r.stack);
20
+ if (r.autoDetectPhases) {
21
+ row("Phases", "auto-detect from labels");
22
+ }
23
+ else {
24
+ row("Phases", r.config.phases.join(" \u2192 "));
25
+ }
26
+ row("Mode", r.config.sequential
27
+ ? "sequential (stop-on-failure)"
28
+ : `parallel (concurrency: ${r.config.concurrency})`);
29
+ if (r.config.qualityLoop) {
30
+ row("Quality loop", `enabled (max ${r.config.maxIterations} iterations)`);
31
+ }
32
+ if (r.mergedOptions.testgen)
33
+ row("Testgen", "enabled");
34
+ if (r.config.noSmartTests)
35
+ row("Smart tests", "disabled");
36
+ if (r.config.dryRun) {
37
+ console.log(chalk.yellow(` ! DRY RUN - no actual execution`));
38
+ }
39
+ if (r.logEnabled)
40
+ row("Logging", "JSON");
41
+ if (r.stateEnabled)
42
+ row("State", "enabled");
43
+ if (r.mergedOptions.force) {
44
+ console.log(chalk.yellow(` ${pad("Force")}enabled (bypass state guard)`));
45
+ }
46
+ if (r.issueNumbers.length > 0) {
47
+ row("Issues", r.issueNumbers.map((n) => `#${n}`).join(", "));
48
+ }
49
+ if (r.worktreeIsolationEnabled) {
50
+ console.log(chalk.gray(` Worktree isolation: enabled`));
51
+ }
52
+ if (r.baseBranch) {
53
+ console.log(chalk.gray(` Base branch: ${r.baseBranch}`));
54
+ }
55
+ if (r.mergedOptions.chain) {
56
+ console.log(chalk.gray(` Chain mode: enabled (each issue branches from previous)`));
57
+ }
58
+ if (r.mergedOptions.qaGate) {
59
+ console.log(chalk.gray(` QA gate: enabled (chain waits for QA pass)`));
60
+ }
61
+ }
62
+ /**
63
+ * Print post-run summary: per-issue status, log path, reflection, tips.
64
+ */
65
+ export function displaySummary(result) {
66
+ const { results, logPath, config, mergedOptions } = result;
67
+ if (results.length === 0)
68
+ return;
69
+ const passed = results.filter((r) => r.success).length;
70
+ const failed = results.filter((r) => !r.success).length;
71
+ console.log("\n" + ui.divider());
72
+ console.log(colors.info(" Summary"));
73
+ console.log(ui.divider());
74
+ console.log(`\n ${colors.success(`${passed} passed`)} ${colors.muted("·")} ${colors.error(`${failed} failed`)}`);
75
+ for (const r of results) {
76
+ const status = r.success
77
+ ? ui.statusIcon("success")
78
+ : ui.statusIcon("error");
79
+ const duration = r.durationSeconds
80
+ ? colors.muted(` (${formatDuration(r.durationSeconds)})`)
81
+ : "";
82
+ const phases = r.phaseResults
83
+ .map((p) => (p.success ? colors.success(p.phase) : colors.error(p.phase)))
84
+ .join(" → ");
85
+ const loopInfo = r.loopTriggered ? colors.warning(" [loop]") : "";
86
+ const prInfo = r.prUrl ? colors.muted(` → PR #${r.prNumber}`) : "";
87
+ console.log(` ${status} #${r.issueNumber}: ${phases}${loopInfo}${prInfo}${duration}`);
88
+ }
89
+ console.log("");
90
+ if (logPath) {
91
+ console.log(colors.muted(` Log: ${logPath}`));
92
+ console.log("");
93
+ }
94
+ if (mergedOptions.reflect && results.length > 0) {
95
+ const reflection = analyzeRun({
96
+ results,
97
+ issueInfoMap: result.issueInfoMap,
98
+ runLog: result.logWriter?.getRunLog() ?? null,
99
+ config: { phases: config.phases, qualityLoop: config.qualityLoop },
100
+ });
101
+ const reflectionOutput = formatReflection(reflection);
102
+ if (reflectionOutput) {
103
+ console.log(reflectionOutput);
104
+ console.log("");
105
+ }
106
+ }
107
+ if (results.length > 1 && passed > 0 && !config.dryRun) {
108
+ console.log(colors.muted(" Tip: Verify batch integration before merging:"));
109
+ console.log(colors.muted(" sequant merge --check"));
110
+ console.log("");
111
+ }
112
+ if (config.dryRun) {
113
+ console.log(colors.warning(" ℹ️ This was a dry run. Use without --dry-run to execute."));
114
+ console.log("");
115
+ }
116
+ }
@@ -1,27 +1,5 @@
1
- /**
2
- * sequant run - Execute workflow for GitHub issues
3
- *
4
- * Orchestrator module that composes focused workflow modules:
5
- * - worktree-manager: Worktree lifecycle (ensure, list, cleanup, changed files)
6
- * - phase-executor: Phase execution with retry and failure handling
7
- * - phase-mapper: Label-to-phase detection and workflow parsing
8
- * - batch-executor: Batch execution, dependency sorting, issue logging
9
- */
10
- /** @internal Log a non-fatal warning: one-line summary always, detail in verbose. */
11
- export declare function logNonFatalWarning(message: string, error: unknown, verbose: boolean): void;
12
- import type { RunOptions } from "../lib/workflow/batch-executor.js";
13
- export { parseQaVerdict, formatDuration, executePhaseWithRetry, } from "../lib/workflow/phase-executor.js";
14
- export { detectDefaultBranch, checkWorktreeFreshness, removeStaleWorktree, listWorktrees, getWorktreeChangedFiles, getWorktreeDiffStats, readCacheMetrics, filterResumedPhases, ensureWorktree, createCheckpointCommit, reinstallIfLockfileChanged, rebaseBeforePR, createPR, } from "../lib/workflow/worktree-manager.js";
15
- export type { WorktreeInfo, RebaseResult, PRCreationResult, } from "../lib/workflow/worktree-manager.js";
16
- export { detectPhasesFromLabels, parseRecommendedWorkflow, determinePhasesForIssue, } from "../lib/workflow/phase-mapper.js";
17
- export { getIssueInfo, sortByDependencies, parseBatches, getEnvConfig, executeBatch, runIssueWithLogging, } from "../lib/workflow/batch-executor.js";
18
- export type { RunOptions } from "../lib/workflow/batch-executor.js";
19
- /**
20
- * Normalize Commander.js --no-X flags into typed RunOptions fields.
21
- * Replaces the `as any` cast (#402 AC-4).
22
- */
23
- export declare function normalizeCommanderOptions(options: RunOptions): RunOptions;
24
- /**
25
- * Main run command
26
- */
1
+ /** sequant run — Thin CLI adapter that delegates to RunOrchestrator. */
2
+ import type { RunOptions } from "../lib/workflow/types.js";
3
+ export * from "./run-compat.js";
4
+ /** Parse CLI args validate delegate to RunOrchestrator.run() → display summary. */
27
5
  export declare function runCommand(issues: string[], options: RunOptions): Promise<void>;