sequant 2.7.0 → 2.9.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 (106) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +18 -2
  4. package/dist/bin/cli.d.ts +1 -1
  5. package/dist/bin/cli.js +12 -2
  6. package/dist/bin/preflight.d.ts +21 -0
  7. package/dist/bin/preflight.js +45 -0
  8. package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
  9. package/dist/marketplace/external_plugins/sequant/README.md +2 -0
  10. package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +18 -3
  11. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +330 -57
  12. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/force-push.md +34 -0
  13. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +117 -19
  14. package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +9 -6
  15. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +29 -0
  16. package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +1 -1
  17. package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +100 -2
  18. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +24 -0
  19. package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +285 -0
  20. package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +202 -0
  21. package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +287 -0
  22. package/dist/marketplace/external_plugins/sequant/skills/qa/references/test-quality-checklist.md +272 -0
  23. package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +40 -0
  24. package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +95 -11
  25. package/dist/marketplace/external_plugins/sequant/skills/references/shared/framework-gotchas.md +186 -0
  26. package/dist/marketplace/external_plugins/sequant/skills/reflect/SKILL.md +27 -13
  27. package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +80 -68
  28. package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +31 -15
  29. package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +669 -0
  30. package/dist/marketplace/external_plugins/sequant/skills/spec/references/verification-criteria.md +1 -1
  31. package/dist/marketplace/external_plugins/sequant/skills/test/references/browser-testing-patterns.md +423 -0
  32. package/dist/marketplace/external_plugins/sequant/skills/upstream/SKILL.md +419 -0
  33. package/dist/src/commands/logs.js +6 -1
  34. package/dist/src/commands/run-display.d.ts +20 -0
  35. package/dist/src/commands/run-display.js +80 -1
  36. package/dist/src/commands/stats.js +47 -0
  37. package/dist/src/lib/assess-collision-detect.d.ts +19 -2
  38. package/dist/src/lib/assess-collision-detect.js +68 -4
  39. package/dist/src/lib/cli-ui/run-renderer.js +17 -9
  40. package/dist/src/lib/errors.d.ts +91 -0
  41. package/dist/src/lib/errors.js +118 -0
  42. package/dist/src/lib/manifest.js +1 -17
  43. package/dist/src/lib/version-check.d.ts +19 -0
  44. package/dist/src/lib/version-check.js +45 -5
  45. package/dist/src/lib/workflow/batch-executor.d.ts +13 -0
  46. package/dist/src/lib/workflow/batch-executor.js +142 -24
  47. package/dist/src/lib/workflow/chain-preflight.d.ts +89 -0
  48. package/dist/src/lib/workflow/chain-preflight.js +199 -0
  49. package/dist/src/lib/workflow/chain-resume.d.ts +116 -0
  50. package/dist/src/lib/workflow/chain-resume.js +166 -0
  51. package/dist/src/lib/workflow/dependency-markers.d.ts +29 -0
  52. package/dist/src/lib/workflow/dependency-markers.js +79 -0
  53. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +17 -0
  54. package/dist/src/lib/workflow/drivers/claude-code.d.ts +29 -0
  55. package/dist/src/lib/workflow/drivers/claude-code.js +136 -8
  56. package/dist/src/lib/workflow/error-classifier.d.ts +9 -2
  57. package/dist/src/lib/workflow/error-classifier.js +14 -1
  58. package/dist/src/lib/workflow/log-writer.d.ts +1 -1
  59. package/dist/src/lib/workflow/log-writer.js +6 -8
  60. package/dist/src/lib/workflow/metrics-schema.d.ts +39 -0
  61. package/dist/src/lib/workflow/metrics-schema.js +16 -0
  62. package/dist/src/lib/workflow/metrics-writer.d.ts +2 -1
  63. package/dist/src/lib/workflow/phase-executor.d.ts +50 -0
  64. package/dist/src/lib/workflow/phase-executor.js +151 -17
  65. package/dist/src/lib/workflow/run-log-schema.d.ts +26 -0
  66. package/dist/src/lib/workflow/run-log-schema.js +52 -1
  67. package/dist/src/lib/workflow/run-orchestrator.d.ts +14 -0
  68. package/dist/src/lib/workflow/run-orchestrator.js +291 -30
  69. package/dist/src/lib/workflow/state-manager.d.ts +1 -0
  70. package/dist/src/lib/workflow/state-manager.js +6 -0
  71. package/dist/src/lib/workflow/state-schema.d.ts +3 -0
  72. package/dist/src/lib/workflow/state-schema.js +7 -0
  73. package/dist/src/lib/workflow/status-derivation.d.ts +30 -0
  74. package/dist/src/lib/workflow/status-derivation.js +27 -0
  75. package/dist/src/lib/workflow/types.d.ts +40 -0
  76. package/dist/src/lib/workflow/worktree-manager.d.ts +43 -1
  77. package/dist/src/lib/workflow/worktree-manager.js +103 -33
  78. package/dist/src/mcp/tools/run.d.ts +2 -0
  79. package/dist/src/mcp/tools/run.js +2 -0
  80. package/dist/src/ui/tui/theme.d.ts +18 -4
  81. package/dist/src/ui/tui/theme.js +18 -4
  82. package/package.json +5 -6
  83. package/templates/hooks/post-tool.sh +18 -3
  84. package/templates/hooks/pre-tool.sh +330 -57
  85. package/templates/scripts/cleanup-worktree.sh +103 -14
  86. package/templates/skills/_shared/references/force-push.md +34 -0
  87. package/templates/skills/assess/SKILL.md +117 -19
  88. package/templates/skills/assess/references/predicted-collision-detection.md +9 -6
  89. package/templates/skills/exec/SKILL.md +29 -0
  90. package/templates/skills/fullsolve/SKILL.md +1 -1
  91. package/templates/skills/loop/SKILL.md +100 -2
  92. package/templates/skills/qa/SKILL.md +24 -0
  93. package/templates/skills/qa/references/anti-pattern-detection.md +285 -0
  94. package/templates/skills/qa/references/call-site-review.md +202 -0
  95. package/templates/skills/qa/references/quality-gates.md +287 -0
  96. package/templates/skills/qa/references/test-quality-checklist.md +272 -0
  97. package/templates/skills/qa/references/testing-requirements.md +40 -0
  98. package/templates/skills/qa/scripts/quality-checks.sh +95 -11
  99. package/templates/skills/references/shared/framework-gotchas.md +186 -0
  100. package/templates/skills/reflect/SKILL.md +27 -13
  101. package/templates/skills/reflect/references/documentation-tiers.md +80 -68
  102. package/templates/skills/reflect/references/phase-reflection.md +31 -15
  103. package/templates/skills/release/SKILL.md +669 -0
  104. package/templates/skills/spec/references/verification-criteria.md +1 -1
  105. package/templates/skills/test/references/browser-testing-patterns.md +423 -0
  106. package/templates/skills/upstream/SKILL.md +419 -0
@@ -43,6 +43,19 @@ export interface RebaseResult {
43
43
  /** Error message if rebase failed */
44
44
  error?: string;
45
45
  }
46
+ /**
47
+ * Result of rebasing a worktree onto a local branch (chain successor → predecessor).
48
+ */
49
+ export interface LocalRebaseResult {
50
+ /** Whether the rebase was attempted */
51
+ performed: boolean;
52
+ /** Whether the rebase succeeded (branch now contains `ontoBranch`'s commits) */
53
+ success: boolean;
54
+ /** Whether a merge conflict caused the rebase to be aborted */
55
+ conflict: boolean;
56
+ /** Error message if the rebase failed */
57
+ error?: string;
58
+ }
46
59
  /**
47
60
  * Result of PR creation
48
61
  */
@@ -176,6 +189,17 @@ export declare function createCheckpointCommit(worktreePath: string, issueNumber
176
189
  * @internal Exported for testing
177
190
  */
178
191
  export declare function reinstallIfLockfileChanged(worktreePath: string, packageManager: string | undefined, verbose: boolean, preRebaseRef?: string): boolean;
192
+ /**
193
+ * Rebase a worktree's branch onto a *local* branch ref (e.g. a chain
194
+ * predecessor's feature branch), NOT origin/main. Used to chain a successor
195
+ * onto its predecessor's committed work at execution time (#748), and to
196
+ * re-chain a pre-existing worktree onto its chain base.
197
+ *
198
+ * On conflict, aborts the rebase to restore the original branch state and
199
+ * returns `{ success: false, conflict: true }` so callers can warn rather than
200
+ * silently treat a broken link as healthy.
201
+ */
202
+ export declare function rebaseOntoLocalBranch(worktreePath: string, ontoBranch: string, verbose?: boolean): LocalRebaseResult;
179
203
  /**
180
204
  * Rebase the worktree branch onto the base branch before PR creation.
181
205
  * This ensures the branch is up-to-date and prevents lockfile drift.
@@ -189,6 +213,24 @@ export declare function reinstallIfLockfileChanged(worktreePath: string, package
189
213
  * @internal Exported for testing
190
214
  */
191
215
  export declare function rebaseBeforePR(worktreePath: string, issueNumber: number, packageManager: string | undefined, verbose: boolean, baseBranch?: string): RebaseResult;
216
+ /**
217
+ * Build the automated PR body for `sequant run`.
218
+ *
219
+ * Pure and I/O-free, kept separate from {@link createPR} (which shells out to
220
+ * `gh`) so the body — including the non-A+ QA note (#749) — is unit-testable
221
+ * directly.
222
+ *
223
+ * @param issueNumber Issue the PR closes
224
+ * @param opts.stackManifest Appended before the trailer under --stacked (#605)
225
+ * @param opts.qaVerdict QA verdict for the run; a non-A+ stopping state
226
+ * (anything other than `READY_FOR_MERGE`) surfaces a note so a human reviewer
227
+ * sees why the run broke to PR without reaching A+ (#749).
228
+ * @internal Exported for testing
229
+ */
230
+ export declare function buildAutomatedPRBody(issueNumber: number, opts?: {
231
+ stackManifest?: string;
232
+ qaVerdict?: string;
233
+ }): string;
192
234
  /**
193
235
  * Push branch and create a PR after successful QA.
194
236
  *
@@ -210,4 +252,4 @@ export declare function rebaseBeforePR(worktreePath: string, issueNumber: number
210
252
  export declare function createPR(worktreePath: string, issueNumber: number, issueTitle: string, branch: string, verbose: boolean, labels?: string[], stackOptions?: {
211
253
  prBase?: string;
212
254
  stackManifest?: string;
213
- }): PRCreationResult;
255
+ }, qaVerdict?: string): PRCreationResult;
@@ -365,21 +365,14 @@ export async function ensureWorktree(issueNumber, title, verbose, packageManager
365
365
  if (verbose) {
366
366
  console.log(chalk.gray(` Rebasing existing worktree onto chain base (${baseBranch})...`));
367
367
  }
368
- const rebaseResult = spawnSync("git", ["-C", existingPath, "rebase", baseBranch], { stdio: "pipe" });
369
- if (rebaseResult.status !== 0) {
370
- const rebaseError = rebaseResult.stderr.toString();
371
- // Check if it's a conflict
372
- if (rebaseError.includes("CONFLICT") ||
373
- rebaseError.includes("could not apply")) {
368
+ const rebase = rebaseOntoLocalBranch(existingPath, baseBranch, verbose);
369
+ if (!rebase.success) {
370
+ if (rebase.conflict) {
374
371
  console.log(chalk.yellow(` ! Rebase conflict detected. Aborting rebase and keeping original branch state.`));
375
372
  console.log(chalk.yellow(` ℹ️ Branch ${branch} is not properly chained. Manual rebase may be required.`));
376
- // Abort the rebase to restore branch state
377
- spawnSync("git", ["-C", existingPath, "rebase", "--abort"], {
378
- stdio: "pipe",
379
- });
380
373
  }
381
374
  else {
382
- console.log(chalk.yellow(` ! Rebase failed: ${rebaseError.trim()}`));
375
+ console.log(chalk.yellow(` ! Rebase failed: ${rebase.error ?? ""}`));
383
376
  console.log(chalk.yellow(` ℹ️ Continuing with branch in its original state.`));
384
377
  }
385
378
  return {
@@ -390,9 +383,6 @@ export async function ensureWorktree(issueNumber, title, verbose, packageManager
390
383
  rebased: false,
391
384
  };
392
385
  }
393
- if (verbose) {
394
- console.log(chalk.green(` ✔ Existing worktree rebased onto ${baseBranch}`));
395
- }
396
386
  return {
397
387
  issue: issueNumber,
398
388
  path: existingPath,
@@ -700,9 +690,10 @@ passed QA in chain mode. It serves as a recovery point if later issues fail.`;
700
690
  const commitResult = spawnSync("git", ["-C", worktreePath, "commit", "-m", commitMessage], { stdio: "pipe" });
701
691
  if (commitResult.status !== 0) {
702
692
  const error = commitResult.stderr.toString();
703
- if (verbose) {
704
- console.log(chalk.yellow(` ! Could not create checkpoint commit: ${error}`));
705
- }
693
+ // #760: chain resume rebases the next link onto this checkpoint, so a
694
+ // commit failure is surfaced regardless of --verbose (the call site adds
695
+ // the resume-impact warning; here we show the underlying git error).
696
+ console.log(chalk.yellow(` ! Could not create checkpoint commit for #${issueNumber}: ${error.trim()}`));
706
697
  return false;
707
698
  }
708
699
  console.log(chalk.green(` 📌 Checkpoint commit created for #${issueNumber}`));
@@ -766,6 +757,39 @@ export function reinstallIfLockfileChanged(worktreePath, packageManager, verbose
766
757
  console.log(chalk.green(` ✔ Dependencies reinstalled`));
767
758
  return true;
768
759
  }
760
+ /**
761
+ * Rebase a worktree's branch onto a *local* branch ref (e.g. a chain
762
+ * predecessor's feature branch), NOT origin/main. Used to chain a successor
763
+ * onto its predecessor's committed work at execution time (#748), and to
764
+ * re-chain a pre-existing worktree onto its chain base.
765
+ *
766
+ * On conflict, aborts the rebase to restore the original branch state and
767
+ * returns `{ success: false, conflict: true }` so callers can warn rather than
768
+ * silently treat a broken link as healthy.
769
+ */
770
+ export function rebaseOntoLocalBranch(worktreePath, ontoBranch, verbose = false) {
771
+ const rebaseResult = spawnSync("git", ["-C", worktreePath, "rebase", ontoBranch], { stdio: "pipe" });
772
+ if (rebaseResult.status === 0) {
773
+ if (verbose) {
774
+ console.log(chalk.green(` ✔ Rebased worktree onto ${ontoBranch}`));
775
+ }
776
+ return { performed: true, success: true, conflict: false };
777
+ }
778
+ const rebaseError = rebaseResult.stderr.toString();
779
+ const isConflict = rebaseError.includes("CONFLICT") || rebaseError.includes("could not apply");
780
+ if (isConflict) {
781
+ // Abort to restore the original branch state.
782
+ spawnSync("git", ["-C", worktreePath, "rebase", "--abort"], {
783
+ stdio: "pipe",
784
+ });
785
+ }
786
+ return {
787
+ performed: true,
788
+ success: false,
789
+ conflict: isConflict,
790
+ error: rebaseError.trim(),
791
+ };
792
+ }
769
793
  /**
770
794
  * Rebase the worktree branch onto the base branch before PR creation.
771
795
  * This ensures the branch is up-to-date and prevents lockfile drift.
@@ -832,6 +856,63 @@ export function rebaseBeforePR(worktreePath, issueNumber, packageManager, verbos
832
856
  reinstalled,
833
857
  };
834
858
  }
859
+ /**
860
+ * Render a non-A+ QA verdict as a PR-body note, or `null` for an A+ / absent
861
+ * verdict.
862
+ *
863
+ * Only stopping states that still break to a PR reach this path
864
+ * (`AC_MET_BUT_NOT_A_PLUS`, `NEEDS_VERIFICATION`); `READY_FOR_MERGE` and an
865
+ * absent verdict produce no note. (#749)
866
+ */
867
+ function qaVerdictNote(verdict) {
868
+ if (!verdict || verdict === "READY_FOR_MERGE")
869
+ return null;
870
+ switch (verdict) {
871
+ case "AC_MET_BUT_NOT_A_PLUS":
872
+ return `> **QA verdict: AC_MET_BUT_NOT_A_PLUS** — acceptance criteria met, but not A+. The run broke to this PR for review rather than entering the quality loop; see the QA review comment for improvement notes.`;
873
+ case "NEEDS_VERIFICATION":
874
+ return `> **QA verdict: NEEDS_VERIFICATION** — acceptance criteria met, pending external verification (e.g. CI or a manual test). See the QA review comment.`;
875
+ default:
876
+ return `> **QA verdict: ${verdict}** — see the QA review comment.`;
877
+ }
878
+ }
879
+ /**
880
+ * Build the automated PR body for `sequant run`.
881
+ *
882
+ * Pure and I/O-free, kept separate from {@link createPR} (which shells out to
883
+ * `gh`) so the body — including the non-A+ QA note (#749) — is unit-testable
884
+ * directly.
885
+ *
886
+ * @param issueNumber Issue the PR closes
887
+ * @param opts.stackManifest Appended before the trailer under --stacked (#605)
888
+ * @param opts.qaVerdict QA verdict for the run; a non-A+ stopping state
889
+ * (anything other than `READY_FOR_MERGE`) surfaces a note so a human reviewer
890
+ * sees why the run broke to PR without reaching A+ (#749).
891
+ * @internal Exported for testing
892
+ */
893
+ export function buildAutomatedPRBody(issueNumber, opts) {
894
+ const bodyLines = [
895
+ `## Summary`,
896
+ ``,
897
+ `Automated PR for issue #${issueNumber}.`,
898
+ ``,
899
+ `Fixes #${issueNumber}`,
900
+ ``,
901
+ ];
902
+ // #749: surface a non-A+ QA verdict in the PR body (not just the run log) so
903
+ // a reviewer sees why the run broke to PR rather than reaching A+.
904
+ const note = qaVerdictNote(opts?.qaVerdict);
905
+ if (note) {
906
+ bodyLines.push(note, ``);
907
+ }
908
+ // #605 AC-4: emit stack manifest before the trailer so reviewers see the
909
+ // chain at the top of the body. Manifest is only set under --stacked.
910
+ if (opts?.stackManifest) {
911
+ bodyLines.push(opts.stackManifest, ``);
912
+ }
913
+ bodyLines.push(`---`, `🤖 Generated by \`sequant run\``);
914
+ return bodyLines.join("\n");
915
+ }
835
916
  /**
836
917
  * Push branch and create a PR after successful QA.
837
918
  *
@@ -850,7 +931,7 @@ export function rebaseBeforePR(worktreePath, issueNumber, packageManager, verbos
850
931
  * @returns PRCreationResult with PR info or error
851
932
  * @internal Exported for testing
852
933
  */
853
- export function createPR(worktreePath, issueNumber, issueTitle, branch, verbose, labels, stackOptions) {
934
+ export function createPR(worktreePath, issueNumber, issueTitle, branch, verbose, labels, stackOptions, qaVerdict) {
854
935
  const github = new GitHubProvider();
855
936
  // Step 1: Check for existing PR on this branch
856
937
  const existingPRInfo = github.viewPRByBranchSync(branch, worktreePath);
@@ -886,21 +967,10 @@ export function createPR(worktreePath, issueNumber, issueTitle, branch, verbose,
886
967
  const isBug = labels?.some((l) => /^bug/i.test(l));
887
968
  const prefix = isBug ? "fix" : "feat";
888
969
  const prTitle = `${prefix}(#${issueNumber}): ${issueTitle}`;
889
- const bodyLines = [
890
- `## Summary`,
891
- ``,
892
- `Automated PR for issue #${issueNumber}.`,
893
- ``,
894
- `Fixes #${issueNumber}`,
895
- ``,
896
- ];
897
- // #605 AC-4: emit stack manifest before the trailer so reviewers see the
898
- // chain at the top of the body. Manifest is only set under --stacked.
899
- if (stackOptions?.stackManifest) {
900
- bodyLines.push(stackOptions.stackManifest, ``);
901
- }
902
- bodyLines.push(`---`, `🤖 Generated by \`sequant run\``);
903
- const prBody = bodyLines.join("\n");
970
+ const prBody = buildAutomatedPRBody(issueNumber, {
971
+ stackManifest: stackOptions?.stackManifest,
972
+ qaVerdict,
973
+ });
904
974
  const prResult = github.createPRCliSync(prTitle, prBody, branch, worktreePath, stackOptions?.prBase);
905
975
  if (prResult.exitCode !== 0) {
906
976
  const prError = prResult.stderr.trim() || "Unknown error";
@@ -32,6 +32,8 @@ interface RunToolResponse {
32
32
  total: number;
33
33
  passed: number;
34
34
  failed: number;
35
+ /** Issues that ended `partial` — timed out, no failure, no recovery (#766). */
36
+ partial: number;
35
37
  durationSeconds: number;
36
38
  };
37
39
  phases: string;
@@ -182,6 +182,7 @@ export function buildStructuredResponse(runLog, rawOutput, overallStatus, exitCo
182
182
  total: runLog.summary.totalIssues,
183
183
  passed: runLog.summary.passed,
184
184
  failed: runLog.summary.failed,
185
+ partial: runLog.summary.partial ?? 0,
185
186
  durationSeconds: runLog.summary.totalDurationSeconds,
186
187
  },
187
188
  phases: phasesRan || runLog.config.phases.join(","),
@@ -232,6 +233,7 @@ function buildFallbackResponse(stdout, issueNumbers, overallStatus, phases, exit
232
233
  total: issueNumbers.length,
233
234
  passed: overallStatus === "success" ? issueNumbers.length : 0,
234
235
  failed: overallStatus === "failure" ? issueNumbers.length : 0,
236
+ partial: 0,
235
237
  durationSeconds: 0,
236
238
  },
237
239
  phases,
@@ -12,8 +12,10 @@ import type { IssueStatus, PhaseStatus } from "../../lib/workflow/run-state.js";
12
12
  * - `BRAND_GREEN` is the accent/success green (`--color-accent`).
13
13
  *
14
14
  * Used to brand the two color signals that matter most at a glance — the
15
- * live/active phase and success — while issue-distinction (border rotation),
16
- * failure (red), and dividers (gray) stay on robust named ANSI colors.
15
+ * live/active phase and success — while issue-distinction (border rotation)
16
+ * and failure (red) stay on robust named ANSI colors. The muted gray for
17
+ * secondary chrome is a fixed mid-gray (`DIVIDER_COLOR`) chosen for WCAG
18
+ * contrast rather than the too-dim ANSI bright-black.
17
19
  *
18
20
  * Ink/chalk auto-downsamples hex to the nearest ANSI color on terminals
19
21
  * without truecolor, and `NO_COLOR` still strips all color, so these degrade
@@ -24,8 +26,20 @@ export declare const BRAND_GREEN: "#10b981";
24
26
  /** Border-color palette rotated by issue start order. */
25
27
  export declare const BORDER_ROTATION: readonly ["cyan", "magenta", "blue", "yellow"];
26
28
  export type BorderColor = (typeof BORDER_ROTATION)[number] | typeof BRAND_GREEN | typeof BRAND_ORANGE | "red" | "gray";
27
- /** Gray used for horizontal dividers inside each box. */
28
- export declare const DIVIDER_COLOR: "gray";
29
+ /**
30
+ * Muted gray for secondary chrome: dividers, field labels (`branch`/`now`/
31
+ * `log`), the `phase N/total` + elapsed line, the `last activity` stamp, and
32
+ * the terminal status line.
33
+ *
34
+ * Lifted off ANSI `"gray"` (bright-black), which the brand's own dark theme
35
+ * renders at ~2.4:1 — below WCAG AA (4.5) and even the 3.0 large-text/UI floor.
36
+ * This fixed mid-gray clears AA (~4.9:1) on the dark theme while staying above
37
+ * 3.0 on light terminals (~3.4:1). It still degrades gracefully: chalk
38
+ * downsamples the hex to the nearest ANSI color on non-truecolor terminals, and
39
+ * `NO_COLOR` strips it entirely. The not-yet-started phase glyph stays on ANSI
40
+ * `"gray"` (see `phaseStatusColor`) so pending work remains the most recessed.
41
+ */
42
+ export declare const DIVIDER_COLOR: "#8B8B9A";
29
43
  /** Brand orange for the live/active phase spinner — the one element the eye
30
44
  * tracks. Border rotation still distinguishes concurrent issues. */
31
45
  export declare const ACTIVE_PHASE_COLOR: "#FF8012";
@@ -11,8 +11,10 @@
11
11
  * - `BRAND_GREEN` is the accent/success green (`--color-accent`).
12
12
  *
13
13
  * Used to brand the two color signals that matter most at a glance — the
14
- * live/active phase and success — while issue-distinction (border rotation),
15
- * failure (red), and dividers (gray) stay on robust named ANSI colors.
14
+ * live/active phase and success — while issue-distinction (border rotation)
15
+ * and failure (red) stay on robust named ANSI colors. The muted gray for
16
+ * secondary chrome is a fixed mid-gray (`DIVIDER_COLOR`) chosen for WCAG
17
+ * contrast rather than the too-dim ANSI bright-black.
16
18
  *
17
19
  * Ink/chalk auto-downsamples hex to the nearest ANSI color on terminals
18
20
  * without truecolor, and `NO_COLOR` still strips all color, so these degrade
@@ -22,8 +24,20 @@ export const BRAND_ORANGE = "#FF8012";
22
24
  export const BRAND_GREEN = "#10b981";
23
25
  /** Border-color palette rotated by issue start order. */
24
26
  export const BORDER_ROTATION = ["cyan", "magenta", "blue", "yellow"];
25
- /** Gray used for horizontal dividers inside each box. */
26
- export const DIVIDER_COLOR = "gray";
27
+ /**
28
+ * Muted gray for secondary chrome: dividers, field labels (`branch`/`now`/
29
+ * `log`), the `phase N/total` + elapsed line, the `last activity` stamp, and
30
+ * the terminal status line.
31
+ *
32
+ * Lifted off ANSI `"gray"` (bright-black), which the brand's own dark theme
33
+ * renders at ~2.4:1 — below WCAG AA (4.5) and even the 3.0 large-text/UI floor.
34
+ * This fixed mid-gray clears AA (~4.9:1) on the dark theme while staying above
35
+ * 3.0 on light terminals (~3.4:1). It still degrades gracefully: chalk
36
+ * downsamples the hex to the nearest ANSI color on non-truecolor terminals, and
37
+ * `NO_COLOR` strips it entirely. The not-yet-started phase glyph stays on ANSI
38
+ * `"gray"` (see `phaseStatusColor`) so pending work remains the most recessed.
39
+ */
40
+ export const DIVIDER_COLOR = "#8B8B9A";
27
41
  /** Brand orange for the live/active phase spinner — the one element the eye
28
42
  * tracks. Border rotation still distinguishes concurrent issues. */
29
43
  export const ACTIVE_PHASE_COLOR = BRAND_ORANGE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sequant",
3
- "version": "2.7.0",
3
+ "version": "2.9.0",
4
4
  "description": "AI coding agent orchestrator — resolve GitHub issues end-to-end with isolated git worktrees, quality gates, and an MCP server. Works with Claude Code or Aider.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,10 +25,11 @@
25
25
  "dev": "tsx bin/cli.ts",
26
26
  "test": "vitest run",
27
27
  "lint": "eslint src/ bin/ --max-warnings 0",
28
- "sync:skills": "cp -r templates/skills/* .claude/skills/",
28
+ "sync:skills": "npx tsx scripts/check-skill-sync.ts --fix",
29
29
  "sync:hooks": "bash scripts/sync-hooks.sh",
30
- "validate:skills": "for skill in templates/skills/*/; do npx skills-ref validate \"$skill\"; done",
30
+ "validate:skills": "for skill in templates/skills/*/; do case \"$skill\" in *_shared*|*/references/*) continue;; esac; npx skills-ref validate \"$skill\"; done",
31
31
  "lint:skill-calls": "npx tsx scripts/lint-skill-calls.ts",
32
+ "lint:skill-sync": "npx tsx scripts/check-skill-sync.ts",
32
33
  "prepare:marketplace": "npx tsx scripts/prepare-marketplace.ts",
33
34
  "validate:marketplace": "npx tsx scripts/prepare-marketplace.ts --validate-only",
34
35
  "prepublishOnly": "npm run build"
@@ -96,7 +97,6 @@
96
97
  "cli-table3": "^0.6.5",
97
98
  "commander": "^14.0.3",
98
99
  "diff": "^9.0.0",
99
- "gradient-string": "^3.0.0",
100
100
  "hono": "^4.12.1",
101
101
  "ink": "^7.0.1",
102
102
  "inquirer": "^14.0.1",
@@ -111,9 +111,8 @@
111
111
  },
112
112
  "devDependencies": {
113
113
  "@eslint/js": "^10.0.1",
114
- "@types/gradient-string": "^1.1.6",
115
114
  "@types/inquirer": "^9.0.7",
116
- "@types/node": "^25.4.0",
115
+ "@types/node": "^26.0.1",
117
116
  "@types/react": "^19.2.14",
118
117
  "@typescript-eslint/eslint-plugin": "^8.58.0",
119
118
  "@typescript-eslint/parser": "^8.58.0",
@@ -51,13 +51,28 @@ fi
51
51
 
52
52
  _TMPDIR="${TMPDIR:-/tmp}"
53
53
 
54
- # Use CLAUDE_PLUGIN_DATA for persistent logs (survives plugin updates)
55
- if [[ -n "${CLAUDE_PLUGIN_DATA}" ]]; then
54
+ # Log sink (#763 AC-5c: blocked-command history must survive to be a useful
55
+ # regression corpus, so $TMPDIR which macOS purges — is a last resort only).
56
+ #
57
+ # This block MUST stay identical to the one in pre-tool.sh. The two hooks
58
+ # write the *same* claude-timing.log (START there, END here) and the same
59
+ # claude-quality.log, so any divergence silently splits every START/END pair
60
+ # across two files.
61
+ #
62
+ # Every candidate is absolute and lives outside the repo. A repo-local or
63
+ # relative path is wrong three times over: it resolves against the hook's cwd
64
+ # (which Claude Code does not pin), it yields one sink per directory instead
65
+ # of the single corpus AC-5 asks for, and — worst — creating it inside a repo
66
+ # makes `git status --porcelain` non-empty, which silently defeats pre-tool.sh's
67
+ # no-changes guard that reads exactly that output.
68
+ if [[ -n "${CLAUDE_PLUGIN_DATA:-}" ]]; then
56
69
  _LOG_DIR="${CLAUDE_PLUGIN_DATA}/logs"
57
- mkdir -p "$_LOG_DIR"
70
+ elif [[ -n "${HOME:-}" ]]; then
71
+ _LOG_DIR="${HOME}/.sequant/logs"
58
72
  else
59
73
  _LOG_DIR="${_TMPDIR}"
60
74
  fi
75
+ mkdir -p "$_LOG_DIR" 2>/dev/null || _LOG_DIR="${_TMPDIR}"
61
76
 
62
77
  TIMING_LOG="${_LOG_DIR}/claude-timing.log"
63
78
  QUALITY_LOG="${_LOG_DIR}/claude-quality.log"