sequant 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +14 -3
- package/dist/bin/cli.js +97 -17
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/.mcp.json +1 -1
- package/dist/marketplace/external_plugins/sequant/README.md +56 -2
- package/dist/marketplace/external_plugins/sequant/hooks/parallel-marker.sh +50 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +20 -7
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +75 -10
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/trust-model.md +18 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +215 -390
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +4 -4
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +26 -6
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +3 -1
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +38 -2
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +337 -57
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +6 -6
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-review-checklist.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/fixtures/injection-issue-body.md +36 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +21 -7
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +11 -11
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +5 -5
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +2 -0
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
- package/dist/src/commands/assess-render.d.ts +23 -0
- package/dist/src/commands/assess-render.js +60 -0
- package/dist/src/commands/doctor.js +8 -13
- package/dist/src/commands/init.js +13 -1
- package/dist/src/commands/locks.js +2 -0
- package/dist/src/commands/logs.js +14 -5
- package/dist/src/commands/merge.js +154 -2
- package/dist/src/commands/ready-tui-adapter.js +6 -1
- package/dist/src/commands/ready.d.ts +25 -3
- package/dist/src/commands/ready.js +39 -13
- package/dist/src/commands/resume.d.ts +113 -0
- package/dist/src/commands/resume.js +193 -0
- package/dist/src/commands/run-display.js +37 -4
- package/dist/src/commands/run-flags.d.ts +42 -1
- package/dist/src/commands/run-flags.js +53 -1
- package/dist/src/commands/run-progress.js +26 -1
- package/dist/src/commands/run.js +11 -10
- package/dist/src/commands/state.js +7 -0
- package/dist/src/commands/status.d.ts +9 -0
- package/dist/src/commands/status.js +24 -1
- package/dist/src/commands/sync.js +90 -19
- package/dist/src/commands/update.js +28 -5
- package/dist/src/lib/ac-parser.d.ts +14 -0
- package/dist/src/lib/ac-parser.js +99 -5
- package/dist/src/lib/assess/index.d.ts +10 -0
- package/dist/src/lib/assess/index.js +9 -0
- package/dist/src/lib/assess/renderer.d.ts +64 -0
- package/dist/src/lib/assess/renderer.js +481 -0
- package/dist/src/lib/assess/types.d.ts +224 -0
- package/dist/src/lib/assess/types.js +241 -0
- package/dist/src/lib/ci/outputs.d.ts +15 -2
- package/dist/src/lib/ci/outputs.js +17 -5
- package/dist/src/lib/cli-flags.d.ts +33 -0
- package/dist/src/lib/cli-flags.js +46 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +21 -7
- package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
- package/dist/src/lib/cli-ui/run-renderer.js +62 -0
- package/dist/src/lib/errors.d.ts +78 -6
- package/dist/src/lib/errors.js +133 -15
- package/dist/src/lib/locks/index.d.ts +3 -3
- package/dist/src/lib/locks/index.js +2 -2
- package/dist/src/lib/locks/lock-manager.d.ts +20 -2
- package/dist/src/lib/locks/lock-manager.js +47 -3
- package/dist/src/lib/locks/types.d.ts +34 -3
- package/dist/src/lib/locks/types.js +16 -0
- package/dist/src/lib/mcp-config.d.ts +45 -0
- package/dist/src/lib/mcp-config.js +77 -1
- package/dist/src/lib/merge-check/combined-branch-test.d.ts +21 -2
- package/dist/src/lib/merge-check/combined-branch-test.js +233 -110
- package/dist/src/lib/merge-check/command-result.d.ts +36 -0
- package/dist/src/lib/merge-check/command-result.js +56 -0
- package/dist/src/lib/merge-check/index.d.ts +4 -0
- package/dist/src/lib/merge-check/index.js +1 -1
- package/dist/src/lib/merge-check/report.js +5 -1
- package/dist/src/lib/merge-check/types.d.ts +9 -0
- package/dist/src/lib/merge-check/watch.d.ts +121 -0
- package/dist/src/lib/merge-check/watch.js +215 -0
- package/dist/src/lib/qa/infra-blocked-ci.d.ts +90 -0
- package/dist/src/lib/qa/infra-blocked-ci.js +80 -0
- package/dist/src/lib/settings.d.ts +14 -0
- package/dist/src/lib/settings.js +61 -8
- package/dist/src/lib/shutdown.d.ts +42 -2
- package/dist/src/lib/shutdown.js +75 -5
- package/dist/src/lib/skills-check.d.ts +26 -0
- package/dist/src/lib/skills-check.js +31 -0
- package/dist/src/lib/stacks.d.ts +110 -1
- package/dist/src/lib/stacks.js +244 -5
- package/dist/src/lib/templates.d.ts +74 -0
- package/dist/src/lib/templates.js +171 -16
- package/dist/src/lib/test-tautology-detector.d.ts +9 -2
- package/dist/src/lib/test-tautology-detector.js +153 -12
- package/dist/src/lib/version-check.d.ts +31 -0
- package/dist/src/lib/version-check.js +45 -2
- package/dist/src/lib/workflow/batch-executor.d.ts +125 -6
- package/dist/src/lib/workflow/batch-executor.js +477 -44
- package/dist/src/lib/workflow/chain-resume.d.ts +13 -11
- package/dist/src/lib/workflow/chain-resume.js +24 -20
- package/dist/src/lib/workflow/completed-status.d.ts +52 -0
- package/dist/src/lib/workflow/completed-status.js +57 -0
- package/dist/src/lib/workflow/config-resolver.d.ts +26 -0
- package/dist/src/lib/workflow/config-resolver.js +54 -2
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +7 -0
- package/dist/src/lib/workflow/drivers/aider.d.ts +2 -0
- package/dist/src/lib/workflow/drivers/aider.js +2 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +14 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +20 -2
- package/dist/src/lib/workflow/git-diff-utils.d.ts +35 -3
- package/dist/src/lib/workflow/git-diff-utils.js +77 -4
- package/dist/src/lib/workflow/heartbeat.d.ts +38 -0
- package/dist/src/lib/workflow/heartbeat.js +90 -0
- package/dist/src/lib/workflow/log-writer.d.ts +44 -1
- package/dist/src/lib/workflow/log-writer.js +75 -6
- package/dist/src/lib/workflow/metrics-schema.d.ts +3 -3
- package/dist/src/lib/workflow/phase-executor.d.ts +203 -14
- package/dist/src/lib/workflow/phase-executor.js +457 -52
- package/dist/src/lib/workflow/phase-registry.js +2 -1
- package/dist/src/lib/workflow/platforms/github.d.ts +64 -0
- package/dist/src/lib/workflow/platforms/github.js +152 -0
- package/dist/src/lib/workflow/qa-cache.d.ts +6 -1
- package/dist/src/lib/workflow/qa-cache.js +22 -9
- package/dist/src/lib/workflow/ready-gate.d.ts +17 -4
- package/dist/src/lib/workflow/ready-gate.js +25 -9
- package/dist/src/lib/workflow/run-log-schema.d.ts +14 -0
- package/dist/src/lib/workflow/run-log-schema.js +47 -1
- package/dist/src/lib/workflow/run-orchestrator.d.ts +19 -0
- package/dist/src/lib/workflow/run-orchestrator.js +141 -20
- package/dist/src/lib/workflow/skills-preflight.d.ts +89 -0
- package/dist/src/lib/workflow/skills-preflight.js +112 -0
- package/dist/src/lib/workflow/state-cleanup.js +9 -2
- package/dist/src/lib/workflow/state-manager.d.ts +33 -0
- package/dist/src/lib/workflow/state-manager.js +83 -0
- package/dist/src/lib/workflow/state-schema.d.ts +26 -0
- package/dist/src/lib/workflow/state-schema.js +47 -0
- package/dist/src/lib/workflow/types.d.ts +126 -4
- package/dist/src/lib/workflow/types.js +1 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +28 -2
- package/dist/src/lib/workflow/worktree-manager.js +102 -23
- package/dist/src/mcp/tools/run.d.ts +9 -1
- package/dist/src/mcp/tools/run.js +20 -1
- package/dist/src/mcp/tools/status.js +6 -0
- package/dist/src/ui/tui/ElapsedTimer.d.ts +12 -3
- package/dist/src/ui/tui/ElapsedTimer.js +12 -10
- package/dist/src/ui/tui/IssueBox.js +10 -2
- package/dist/src/ui/tui/row-cap.js +2 -1
- package/dist/src/ui/tui/theme.d.ts +7 -0
- package/dist/src/ui/tui/theme.js +9 -0
- package/package.json +8 -7
- package/templates/hooks/parallel-marker.sh +50 -0
- package/templates/hooks/post-tool.sh +20 -7
- package/templates/hooks/pre-tool.sh +75 -10
- package/templates/scripts/cleanup-worktree.sh +182 -19
- package/templates/scripts/new-feature.sh +264 -16
- package/templates/skills/_shared/references/trust-model.md +18 -0
- package/templates/skills/assess/SKILL.md +215 -390
- package/templates/skills/assess/references/predicted-collision-detection.md +1 -1
- package/templates/skills/docs/SKILL.md +4 -4
- package/templates/skills/exec/SKILL.md +26 -6
- package/templates/skills/fullsolve/SKILL.md +3 -1
- package/templates/skills/loop/SKILL.md +38 -2
- package/templates/skills/merger/SKILL.md +2 -2
- package/templates/skills/qa/SKILL.md +337 -57
- package/templates/skills/qa/references/anti-pattern-detection.md +6 -6
- package/templates/skills/qa/references/call-site-review.md +2 -2
- package/templates/skills/qa/references/code-review-checklist.md +2 -2
- package/templates/skills/qa/references/fixtures/injection-issue-body.md +36 -0
- package/templates/skills/qa/references/quality-gates.md +21 -7
- package/templates/skills/qa/references/testing-requirements.md +1 -1
- package/templates/skills/qa/scripts/quality-checks.sh +11 -11
- package/templates/skills/release/SKILL.md +1 -1
- package/templates/skills/setup/SKILL.md +5 -5
- package/templates/skills/spec/SKILL.md +2 -0
- package/templates/skills/test/SKILL.md +1 -1
- package/dist/src/lib/phase-spinner.d.ts +0 -146
- package/dist/src/lib/phase-spinner.js +0 -255
- package/dist/src/lib/workflow/pr-operations.d.ts +0 -86
- package/dist/src/lib/workflow/pr-operations.js +0 -326
- package/dist/src/lib/workflow/run-summary.d.ts +0 -36
- package/dist/src/lib/workflow/run-summary.js +0 -142
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
{
|
|
9
9
|
"name": "sequant",
|
|
10
10
|
"description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees, quality gates, and an MCP server. Includes 17 skills, workflow MCP tools, and pre/post-tool hooks.",
|
|
11
|
-
"version": "2.
|
|
11
|
+
"version": "2.10.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": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees and quality gates, through spec → exec → qa phases.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.10.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sequant-io",
|
|
7
7
|
"email": "hello@sequant.io"
|
package/README.md
CHANGED
|
@@ -16,6 +16,14 @@ AI coding agents write code well, but leave you to run the workflow around it
|
|
|
16
16
|
|
|
17
17
|
See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CHANGELOG.md#migration-from-v1x) if upgrading from v1.x.
|
|
18
18
|
|
|
19
|
+
### What's new in 2.10
|
|
20
|
+
|
|
21
|
+
- **`--auto-wait <minutes>` rides out a rate-limit window** — opt in and a run whose limit window is hours out sleeps until it reopens and continues, instead of halting for a manual restart (#804). **Off by default**; the value is a *total* budget per issue, capped at 2 waits. Never waits on out-of-credits failures (credits are purchased, not waited out). The wait is in-process — for waits that must survive closing the terminal or a reboot, see halt-and-resume below; an exhausted `--auto-wait` budget still writes the halt record so `sequant resume` can pick up where it gave up. See [run-command.md](docs/reference/run-command.md#auto-wait-for-a-rate-limit-window).
|
|
22
|
+
- **Durable halt-and-resume + `sequant resume`** — a run that fails on an exhausted rate-limit window now writes a durable halt record (with its `resumeAt` time) and exits cleanly, releasing the per-issue lock. `sequant resume` re-enters after the window reopens, skipping completed phases and issues — safe to invoke from cron/launchd for unattended machines (recipes in [halt-and-resume.md](docs/reference/halt-and-resume.md)) (#892).
|
|
23
|
+
- **`--ready-gate` runs the post-QA ready gate inside `sequant run`** — opt in and, once an issue's standard phases succeed, `run` drives it through the same full-weight `qa → loop → qa` gate as `sequant ready` (to the configured `ready.policy`) **before** opening the PR, so the gate's fixes land in it. It automates the manual any-gaps/fix-gaps second look and **still stops at the human merge gate — it never merges** (#817). **Off by default**; without the flag the run path is unchanged (an `AC_MET_BUT_NOT_A_PLUS` verdict still breaks to PR per #749). Reuses `ready`'s policy, iteration cap, and stagnation guard — no new settings. See [run-command.md](docs/reference/run-command.md#ready-gate-post-qa-second-look).
|
|
24
|
+
- **`sequant merge --watch` waits for CI, then reports** — instead of polling checks by hand, `merge --watch` waits for each PR's CI checks to finish, then runs the merge-check and reports the result. It never merges (#818).
|
|
25
|
+
- **Stricter CLI contract for scripting** — malformed numeric flags (`--timeout 30m`, `--timeout abc`) are rejected with a clear error instead of silently coerced (#833, #845), and pre-flight rejections (uninitialized project, missing prerequisites) exit non-zero across `run`/`update`/`state`/`status`/`init` (#848). Runs terminated by a signal exit `128+signum` instead of `0` (#856).
|
|
26
|
+
|
|
19
27
|
### What's new in 2.9
|
|
20
28
|
|
|
21
29
|
- **`--chain` survives a failed link** — re-running a partially-completed chain resumes from its last good link, skipping the completed prefix and rebasing onto that committed tip instead of redoing hours of finished work (#760). A warn-by-default content pre-flight also runs before the first worktree is provisioned, flagging missing ACs, mis-ordered dependencies, predicted file overlaps, and closed issues; `--strict-preflight` makes any warning a hard stop (#762).
|
|
@@ -25,7 +33,7 @@ See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CH
|
|
|
25
33
|
### What's new in 2.8
|
|
26
34
|
|
|
27
35
|
- **Clearer failures when an agent stops early** — phases that hit a turn cap now preserve their partial work and halt cleanly for resume instead of discarding it (#739, #733), and rate-limit/out-of-credits failures are named for what they are (with reset time and credit-purchase hints) rather than buried under generic retry noise (#732).
|
|
28
|
-
- **Runtime Node-version guard** — `sequant` checks the running Node against its `engines.node` floor (`>=22.
|
|
36
|
+
- **Runtime Node-version guard** — `sequant` checks the running Node against its `engines.node` floor (`>=22.13.0`) at startup and exits with a friendly upgrade message instead of crashing later on a Node-22-only API (#734).
|
|
29
37
|
- **`/assess` avoids npx version skew** — it now emits `sequant run …` when a global install is on `PATH` (and the unchanged `npx sequant run …` otherwise), so copy-pasted commands don't silently run a stale binary (#740).
|
|
30
38
|
|
|
31
39
|
### What's new in 2.7
|
|
@@ -55,7 +63,7 @@ See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CH
|
|
|
55
63
|
- [GitHub CLI](https://cli.github.com/) — run `gh auth login`
|
|
56
64
|
- Git — for worktree-based isolation
|
|
57
65
|
|
|
58
|
-
**For the npm/CLI install path:** Node.js 22.
|
|
66
|
+
**For the npm/CLI install path:** Node.js 22.13+
|
|
59
67
|
|
|
60
68
|
**Optional MCP (Model Context Protocol) servers — enhanced features:**
|
|
61
69
|
- `chrome-devtools` — enables `/test` for browser-based UI testing
|
|
@@ -83,6 +91,8 @@ npx sequant init # install skills into your project
|
|
|
83
91
|
npx sequant doctor # verify setup
|
|
84
92
|
```
|
|
85
93
|
|
|
94
|
+
> **Commit `.claude/skills/`.** It is a runtime dependency, not a cache: `sequant run` phase agents load skills from that directory only, so a checkout (or cleanup) that drops it breaks every run. `sequant run` pre-flights the directory and fails fast with the fix (`sequant sync`) if it is missing.
|
|
95
|
+
|
|
86
96
|
### Your first run
|
|
87
97
|
|
|
88
98
|
Inside Claude Code, solve an issue end-to-end:
|
|
@@ -236,7 +246,7 @@ Most work goes through a handful of top-level commands. The rest are either pipe
|
|
|
236
246
|
| `/assess <issues…>` | Triage one or more issues; emits a dashboard + ready-to-paste `run` commands (6-action vocabulary). |
|
|
237
247
|
| `npx sequant run <issues…>` | Headless equivalent of `/fullsolve`; batches run in parallel. Add `-Q` for the quality loop. |
|
|
238
248
|
| `/qa <issue>` | Code review + quality gate; posts findings as issue comments. |
|
|
239
|
-
| `npx sequant merge <issues…>` | Batch integration QA before merging. |
|
|
249
|
+
| `npx sequant merge <issues…>` | Batch integration QA before merging. Add `--watch` to wait for each PR's CI checks to finish (configurable `--interval`/`--timeout`), then run the checks and report — it never merges. |
|
|
240
250
|
|
|
241
251
|
### Pipeline internals
|
|
242
252
|
|
|
@@ -247,6 +257,7 @@ Most work goes through a handful of top-level commands. The rest are either pipe
|
|
|
247
257
|
| Command | What it does |
|
|
248
258
|
|---------|--------------|
|
|
249
259
|
| `sequant ready <issue>` | Post-resolve full-weight A+ QA gate; drives to merge-readiness, then stops at the human merge gate (never merges). |
|
|
260
|
+
| `sequant resume` | Re-enter runs halted on a rate-limit window once it reopens; no-op before `resumeAt`, so it is safe to schedule via cron/launchd. See [halt-and-resume.md](docs/reference/halt-and-resume.md). |
|
|
250
261
|
| `/merger` | Multi-issue merge coordination. |
|
|
251
262
|
| `/improve` | Codebase analysis and improvement discovery. |
|
|
252
263
|
| `/security-review` | Deep security analysis. |
|
package/dist/bin/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { readFileSync } from "fs";
|
|
|
18
18
|
import { initCommand } from "../src/commands/init.js";
|
|
19
19
|
import { buildHomeStrayWarning, getInstallRoot, isHomeStrayInstall, isLocalNodeModulesInstall, } from "../src/lib/version-check.js";
|
|
20
20
|
import { configureUI, banner } from "../src/lib/cli-ui.js";
|
|
21
|
+
import { parseWholeNumber, parsePositiveSeconds, } from "../src/lib/cli-flags.js";
|
|
21
22
|
import { isCI, isStdoutTTY } from "../src/lib/tty.js";
|
|
22
23
|
import { detectPackageManagerSync, getPackageManagerCommands, } from "../src/lib/stacks.js";
|
|
23
24
|
// Read version from package.json dynamically
|
|
@@ -48,6 +49,7 @@ import { updateCommand } from "../src/commands/update.js";
|
|
|
48
49
|
import { doctorCommand } from "../src/commands/doctor.js";
|
|
49
50
|
import { statusCommand } from "../src/commands/status.js";
|
|
50
51
|
import { runCommand } from "../src/commands/run.js";
|
|
52
|
+
import { resumeCommand } from "../src/commands/resume.js";
|
|
51
53
|
import { logsCommand } from "../src/commands/logs.js";
|
|
52
54
|
import { statsCommand } from "../src/commands/stats.js";
|
|
53
55
|
import { dashboardCommand } from "../src/commands/dashboard.js";
|
|
@@ -60,6 +62,7 @@ import { locksListCommand, locksClearCommand, locksAcquireCommand, locksReleaseC
|
|
|
60
62
|
import { promptCommand } from "../src/commands/prompt.js";
|
|
61
63
|
import { watchCommand } from "../src/commands/watch.js";
|
|
62
64
|
import { abortCommand } from "../src/commands/abort.js";
|
|
65
|
+
import { assessRenderCommand } from "../src/commands/assess-render.js";
|
|
63
66
|
import { getManifest } from "../src/lib/manifest.js";
|
|
64
67
|
import { phaseRegistry } from "../src/lib/workflow/phase-registry.js";
|
|
65
68
|
/**
|
|
@@ -142,7 +145,7 @@ program
|
|
|
142
145
|
program
|
|
143
146
|
.command("sync")
|
|
144
147
|
.description("Sync skills and templates from the Sequant package (non-interactive)")
|
|
145
|
-
.option("-f, --force", "Sync even if versions match")
|
|
148
|
+
.option("-f, --force", "Sync even if versions match; also overwrites in-place customizations (e.g. the constitution)")
|
|
146
149
|
.option("-q, --quiet", "Suppress output")
|
|
147
150
|
.option("-d, --dry-run", "Show what sync would write without making changes (exits non-zero if work is pending)")
|
|
148
151
|
.action(syncCommand);
|
|
@@ -155,13 +158,13 @@ program
|
|
|
155
158
|
program
|
|
156
159
|
.command("status")
|
|
157
160
|
.description("Show Sequant version, configuration, and workflow state")
|
|
158
|
-
.argument("[issue]", "Issue number to show details for",
|
|
161
|
+
.argument("[issue]", "Issue number to show details for", parseWholeNumber("issue", { min: 1 }))
|
|
159
162
|
.option("--issues", "Show all tracked issues")
|
|
160
163
|
.option("--json", "Output as JSON")
|
|
161
164
|
.option("--rebuild", "Rebuild state from run logs")
|
|
162
165
|
.option("--cleanup", "Clean up stale/orphaned entries")
|
|
163
166
|
.option("--dry-run", "Preview cleanup without changes")
|
|
164
|
-
.option("--max-age <days>", "Remove entries older than N days",
|
|
167
|
+
.option("--max-age <days>", "Remove entries older than N days", parseWholeNumber("--max-age", { min: 1, unit: "days" }))
|
|
165
168
|
.option("--all", "Show all entries including expired; with --cleanup removes all orphaned")
|
|
166
169
|
.option("--offline", "Skip GitHub queries (pure local state)")
|
|
167
170
|
.action((issue, options) => {
|
|
@@ -179,7 +182,9 @@ program
|
|
|
179
182
|
.option("--sequential", "Stop on first issue failure (default: continue)")
|
|
180
183
|
.option("-d, --dry-run", "Preview without execution")
|
|
181
184
|
.option("-v, --verbose", "Verbose output with streaming")
|
|
182
|
-
|
|
185
|
+
// #833: `parseInt` here let `--timeout abc` reach `setTimeout` as `NaN`,
|
|
186
|
+
// which clamps to 0 and aborts every phase the moment it starts.
|
|
187
|
+
.option("--timeout <seconds>", "Timeout per phase in seconds", parsePositiveSeconds("--timeout"))
|
|
183
188
|
.option("--log-json", "Enable structured JSON logging (default: true)")
|
|
184
189
|
.option("--no-log", "Disable JSON logging for this run")
|
|
185
190
|
.option("--log-path <path>", "Custom log directory path")
|
|
@@ -189,7 +194,13 @@ program
|
|
|
189
194
|
// `runCommand` ORs `qualityLoopAlias` into `qualityLoop`. `-q` no longer maps
|
|
190
195
|
// to --quiet, which moved to `-s` to end the `-q`/`-Q` collision.
|
|
191
196
|
.addOption(new Option("-q, --quality-loop-alias", "Alias for -Q/--quality-loop").hideHelp())
|
|
192
|
-
|
|
197
|
+
// #833: a `NaN` bound makes `while (iteration < maxIterations)` in
|
|
198
|
+
// `batch-executor.ts` false on entry, so the issue silently runs zero phases.
|
|
199
|
+
.option("--max-iterations <n>", "Max iterations for quality loop (default: 3)", parseWholeNumber("--max-iterations", {
|
|
200
|
+
min: 1,
|
|
201
|
+
unit: "iterations",
|
|
202
|
+
unitSingular: "iteration",
|
|
203
|
+
}))
|
|
193
204
|
.option("--batch <issues>", 'Group of issues to run together (e.g., --batch "1 2" --batch "3")', (value, prev) => prev.concat([value]), [])
|
|
194
205
|
.option("--smart-tests", "Enable smart test detection (default)")
|
|
195
206
|
.option("--no-smart-tests", "Disable smart test detection")
|
|
@@ -198,17 +209,40 @@ program
|
|
|
198
209
|
.option("-s, --quiet", "Suppress version warnings and non-essential output (heartbeat-only)")
|
|
199
210
|
.option("--chain", "Chain issues: each successor is rebased onto the previous issue's committed work before it runs (implies --sequential)")
|
|
200
211
|
.option("--stacked", "Stack PRs: middle PRs target predecessor branch instead of main; first/last target main (implies --chain)")
|
|
201
|
-
|
|
212
|
+
// #795: deliberately a visible `.option()`, NOT `.hideHelp()` like
|
|
213
|
+
// `--experimental-tui` below. The two are different cases: `--qa-gate`
|
|
214
|
+
// shipped in #133 and was documented across four user-facing docs, so users
|
|
215
|
+
// need to find the deprecation notice; `--experimental-tui` was never
|
|
216
|
+
// advertised. A flag that still works but is absent from --help is the worst
|
|
217
|
+
// of both worlds — scripts keep passing it and nothing explains why to stop.
|
|
218
|
+
.option("--qa-gate", "DEPRECATED (#795): no-op, still accepted. --chain already halts on any failed issue, QA included")
|
|
202
219
|
.option("--strict-preflight", "Make --chain content pre-flight warnings (missing AC, dependency/overlap order, closed issues) fatal before any worktree is provisioned")
|
|
203
220
|
.option("--base <branch>", "Base branch for worktree creation (default: main or settings.run.defaultBase)")
|
|
204
221
|
.option("--no-mcp", "Disable MCP server injection in headless mode")
|
|
205
222
|
.option("--no-retry", "Disable automatic retry with MCP fallback (useful for debugging)")
|
|
223
|
+
.option("--auto-wait <minutes>", "Total minutes to wait for an exhausted rate-limit window to reopen instead of halting (default: 0, off)",
|
|
224
|
+
// #833: min 0 — #804 defines 0 as "off", so 0 is a meaningful value here
|
|
225
|
+
// and must keep parsing. `NaN` was silently coerced to 0 by
|
|
226
|
+
// `createAutoWaitLedger`, so `--auto-wait 30m` quietly bought 30 minutes.
|
|
227
|
+
parseWholeNumber("--auto-wait", {
|
|
228
|
+
min: 0,
|
|
229
|
+
unit: "minutes",
|
|
230
|
+
unitSingular: "minute",
|
|
231
|
+
}))
|
|
206
232
|
.option("--resume", "Resume from last completed phase (reads phase markers from GitHub)")
|
|
207
233
|
.option("--no-rebase", "Skip pre-PR rebase onto origin/main (use when you want to handle rebasing manually)")
|
|
208
234
|
.option("--no-pr", "Skip PR creation after successful QA (manual PR workflow)")
|
|
235
|
+
// #817: opt-in post-QA ready gate. Reuses the `sequant ready` engine and its
|
|
236
|
+
// bounds (policy from settings.ready.policy, iteration cap, stagnation guard,
|
|
237
|
+
// Non-Goals) — no new policy surface. Never merges; stops at the human gate.
|
|
238
|
+
.option("--ready-gate", "After phases succeed, run the post-QA ready gate (qa→loop→qa to the configured policy) — never merges, stops at the human merge gate")
|
|
209
239
|
.option("-f, --force", "Force re-execution of completed issues (bypass pre-flight state guard) and take over per-issue locks")
|
|
210
240
|
.option("--signal-other", "With --force, SIGTERM the prior PID holding the lock (same-host alive only)")
|
|
211
|
-
.option("--concurrency <n>", "Max concurrent issues in parallel mode (default: 3)",
|
|
241
|
+
.option("--concurrency <n>", "Max concurrent issues in parallel mode (default: 3)",
|
|
242
|
+
// #833: `run.ts` already rejects non-integers, but only after `parseInt`
|
|
243
|
+
// has silently turned `--concurrency 3x` into 3. Validating the raw string
|
|
244
|
+
// catches that; the downstream check stays as a programmatic backstop.
|
|
245
|
+
parseWholeNumber("--concurrency", { min: 1 }))
|
|
212
246
|
.option("--isolate-parallel", "Isolate parallel agent groups in separate worktrees (prevents file conflicts)")
|
|
213
247
|
.option("--reflect", "Analyze run results and suggest improvements")
|
|
214
248
|
.option("--agent <name>", 'Agent driver for phase execution (default: "claude-code")')
|
|
@@ -220,12 +254,26 @@ program
|
|
|
220
254
|
.addOption(new Option("--experimental-tui").hideHelp())
|
|
221
255
|
.option("--no-relay", "Disable interactive relay (#383); `sequant prompt` cannot reach this run")
|
|
222
256
|
.action(runCommand);
|
|
257
|
+
// Durable halt-and-resume re-entry (#892). Safe to invoke from cron/launchd:
|
|
258
|
+
// a no-op (exit 0) until a halted issue's `resumeAt` passes.
|
|
259
|
+
program
|
|
260
|
+
.command("resume")
|
|
261
|
+
.description("Re-enter runs halted on a rate-limit window (no-op until resumeAt; see docs/reference/halt-and-resume.md)")
|
|
262
|
+
.argument("[issues...]", "Issue numbers to resume (default: all halted)")
|
|
263
|
+
.option("-d, --dry-run", "Show what would be resumed without running")
|
|
264
|
+
// Arrow wrapper: commander passes the Command instance as a third
|
|
265
|
+
// positional, which must not land in resumeCommand's injectable deps param.
|
|
266
|
+
.action((issues, options) => resumeCommand(issues, options));
|
|
223
267
|
program
|
|
224
268
|
.command("prompt")
|
|
225
269
|
.description("Send a message into a running headless sequant session (#383)")
|
|
226
270
|
.argument("[args...]", '[<issue>] "<message>"')
|
|
227
271
|
.option("--type <type>", "Message type: query (default), directive, abort", "query")
|
|
228
|
-
.option("--wait <seconds>", "Block until a reply arrives or the timeout elapses (#645, Gap 4)",
|
|
272
|
+
.option("--wait <seconds>", "Block until a reply arrives or the timeout elapses (#645, Gap 4)", parseWholeNumber("--wait", {
|
|
273
|
+
min: 0,
|
|
274
|
+
unit: "seconds",
|
|
275
|
+
unitSingular: "second",
|
|
276
|
+
}))
|
|
229
277
|
.option("--json", "Output as JSON")
|
|
230
278
|
.action((args, options) => {
|
|
231
279
|
return promptCommand({
|
|
@@ -253,7 +301,15 @@ program
|
|
|
253
301
|
.description("Out-of-band abort: signal a running sequant session directly (#645)")
|
|
254
302
|
.argument("[issue]", "Issue number (auto-resolved when a single run is active)")
|
|
255
303
|
.option("--force", "Skip the SIGINT grace period; SIGTERM immediately")
|
|
256
|
-
.option("--grace <seconds>", "Seconds to wait after SIGINT before escalating (default: 10)",
|
|
304
|
+
.option("--grace <seconds>", "Seconds to wait after SIGINT before escalating (default: 10)",
|
|
305
|
+
// #833: min 0 — `--grace 0` means "escalate immediately" and is meaningful.
|
|
306
|
+
// `NaN` was worse than useless: `Math.max(0, NaN * 1000)` is `NaN`, so the
|
|
307
|
+
// grace period was skipped entirely and SIGTERM followed at once.
|
|
308
|
+
parseWholeNumber("--grace", {
|
|
309
|
+
min: 0,
|
|
310
|
+
unit: "seconds",
|
|
311
|
+
unitSingular: "second",
|
|
312
|
+
}))
|
|
257
313
|
.option("--json", "Output as JSON")
|
|
258
314
|
.action((issueArg, options) => {
|
|
259
315
|
const args = issueArg === undefined ? [] : [issueArg];
|
|
@@ -275,6 +331,9 @@ program
|
|
|
275
331
|
.option("--review", "Run Phase 1 + 2 + 3 AI briefing")
|
|
276
332
|
.option("--all", "Run all phases")
|
|
277
333
|
.option("--post", "Post report to GitHub as PR comments")
|
|
334
|
+
.option("--watch", "Poll each PR's CI checks until terminal, then run merge-check (never merges)")
|
|
335
|
+
.option("--interval <seconds>", "Watch poll interval in seconds (default 30)", parsePositiveSeconds("--interval"))
|
|
336
|
+
.option("--timeout <seconds>", "Watch give-up timeout in seconds (default 1800)", parsePositiveSeconds("--timeout"))
|
|
278
337
|
.option("--json", "Output as JSON")
|
|
279
338
|
.option("-v, --verbose", "Enable verbose output")
|
|
280
339
|
.action(mergeCommand);
|
|
@@ -283,13 +342,34 @@ program
|
|
|
283
342
|
.description("Post-resolve A+ QA gate — drive an issue to merge-readiness, then stop at the human merge gate (never merges)")
|
|
284
343
|
.argument("<issue>", "Issue number to drive to readiness")
|
|
285
344
|
.option("--policy <policy>", "Gate policy: 'ac' (default, stop at ACs met) or 'a-plus' (loop to READY_FOR_MERGE)")
|
|
286
|
-
.option("--max-iterations <n>", "Max QA passes before halting for human review (default: settings.run.maxIterations)",
|
|
287
|
-
|
|
288
|
-
|
|
345
|
+
.option("--max-iterations <n>", "Max QA passes before halting for human review (default: settings.run.maxIterations)",
|
|
346
|
+
// #833: `ready.ts` guards each of these three with `> 0`, so `NaN` already
|
|
347
|
+
// fell back to the default rather than reaching a timer. What it could not
|
|
348
|
+
// catch is the silent misparse — `--budget 10k` became 10, `--timeout 30m`
|
|
349
|
+
// became 30 seconds — and a fallback is not the same as telling the user.
|
|
350
|
+
parseWholeNumber("--max-iterations", {
|
|
351
|
+
min: 1,
|
|
352
|
+
unit: "iterations",
|
|
353
|
+
unitSingular: "iteration",
|
|
354
|
+
}))
|
|
355
|
+
.option("--budget <tokens>", "Token budget; halt cleanly with a 'needs human' message on exhaustion", parseWholeNumber("--budget", {
|
|
356
|
+
min: 1,
|
|
357
|
+
unit: "tokens",
|
|
358
|
+
unitSingular: "token",
|
|
359
|
+
}))
|
|
360
|
+
.option("--timeout <seconds>", "Timeout per phase in seconds", parsePositiveSeconds("--timeout"))
|
|
289
361
|
.option("--no-mcp", "Disable MCP server injection in headless mode")
|
|
290
362
|
.option("--json", "Output as JSON")
|
|
291
363
|
.option("-v, --verbose", "Enable verbose output")
|
|
292
364
|
.action((issue, options) => readyCommand(issue, options));
|
|
365
|
+
// #823: internal surface for the /assess skill, deliberately hidden from the
|
|
366
|
+
// top-level help — it takes a JSON payload no human hand-writes. `sequant
|
|
367
|
+
// assess-render --help` still works for debugging.
|
|
368
|
+
program
|
|
369
|
+
.command("assess-render", { hidden: true })
|
|
370
|
+
.description("(internal) Render an /assess AssessResult JSON payload to stdout")
|
|
371
|
+
.argument("<file>", "Path to an AssessResult JSON file")
|
|
372
|
+
.action(assessRenderCommand);
|
|
293
373
|
program
|
|
294
374
|
.command("conventions")
|
|
295
375
|
.description("View and manage codebase conventions")
|
|
@@ -301,9 +381,9 @@ program
|
|
|
301
381
|
.command("logs")
|
|
302
382
|
.description("View and analyze workflow run logs")
|
|
303
383
|
.option("-p, --path <path>", "Custom log directory path")
|
|
304
|
-
.option("-n, --last <n>", "Show last N runs",
|
|
384
|
+
.option("-n, --last <n>", "Show last N runs", parseWholeNumber("--last", { min: 1 }))
|
|
305
385
|
.option("--json", "Output as JSON")
|
|
306
|
-
.option("-i, --issue <number>", "Filter by issue number",
|
|
386
|
+
.option("-i, --issue <number>", "Filter by issue number", parseWholeNumber("--issue", { min: 1 }))
|
|
307
387
|
.option("--failed", "Show only failed runs")
|
|
308
388
|
.option("--rotate", "Rotate logs (delete oldest to meet thresholds)")
|
|
309
389
|
.option("-d, --dry-run", "Show what would be rotated without deleting")
|
|
@@ -322,7 +402,7 @@ program
|
|
|
322
402
|
program
|
|
323
403
|
.command("dashboard")
|
|
324
404
|
.description("Start visual workflow dashboard in browser")
|
|
325
|
-
.option("-p, --port <port>", "Port to run server on",
|
|
405
|
+
.option("-p, --port <port>", "Port to run server on", parseWholeNumber("--port", { min: 1 }))
|
|
326
406
|
.option("--no-open", "Don't automatically open browser")
|
|
327
407
|
.option("-v, --verbose", "Enable verbose logging")
|
|
328
408
|
.action(dashboardCommand);
|
|
@@ -330,7 +410,7 @@ program
|
|
|
330
410
|
.command("serve")
|
|
331
411
|
.description("Start MCP server for workflow orchestration")
|
|
332
412
|
.option("--transport <type>", "Transport type: stdio (default) or sse", "stdio")
|
|
333
|
-
.option("--port <port>", "Port for SSE transport (default: 3100)",
|
|
413
|
+
.option("--port <port>", "Port for SSE transport (default: 3100)", parseWholeNumber("--port", { min: 1 }))
|
|
334
414
|
.action(async (options) => {
|
|
335
415
|
const mod = await import("../src/commands/serve.js").catch(() => null);
|
|
336
416
|
if (!mod) {
|
|
@@ -364,7 +444,7 @@ stateCmd
|
|
|
364
444
|
.option("--json", "Output as JSON")
|
|
365
445
|
.option("-v, --verbose", "Enable verbose output")
|
|
366
446
|
.option("-d, --dry-run", "Preview cleanup without changes")
|
|
367
|
-
.option("--max-age <days>", "Remove entries older than N days",
|
|
447
|
+
.option("--max-age <days>", "Remove entries older than N days", parseWholeNumber("--max-age", { min: 1, unit: "days" }))
|
|
368
448
|
.option("--all", "Remove all orphaned entries (merged and abandoned)")
|
|
369
449
|
.action(stateCleanCommand);
|
|
370
450
|
// Per-issue concurrency locks (#625)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sequant",
|
|
3
3
|
"description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees and quality gates, through spec → exec → qa phases.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.10.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sequant-io",
|
|
7
7
|
"email": "hello@sequant.io"
|
|
@@ -6,7 +6,7 @@ AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-en
|
|
|
6
6
|
|
|
7
7
|
- **Git** with a GitHub remote
|
|
8
8
|
- **GitHub CLI** (`gh`) authenticated (`gh auth status`)
|
|
9
|
-
- **Node.js 22.
|
|
9
|
+
- **Node.js 22.13+** (for MCP server via `npx`)
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -31,7 +31,7 @@ sequant init
|
|
|
31
31
|
|
|
32
32
|
## What You Get
|
|
33
33
|
|
|
34
|
-
###
|
|
34
|
+
### 19 Workflow Skills
|
|
35
35
|
|
|
36
36
|
| Skill | Purpose |
|
|
37
37
|
|-------|---------|
|
|
@@ -51,6 +51,8 @@ sequant init
|
|
|
51
51
|
| `/security-review` | Deep security analysis |
|
|
52
52
|
| `/solve` | Generate the recommended workflow for one or more issues |
|
|
53
53
|
| `/merger` | Multi-issue integration and merge |
|
|
54
|
+
| `/release` | Version bump, git tag, GitHub release, npm publish |
|
|
55
|
+
| `/upstream` | Monitor Claude Code releases for breaking changes |
|
|
54
56
|
| `/setup` | Project initialization for plugin users |
|
|
55
57
|
|
|
56
58
|
### MCP Tools (automatic with plugin)
|
|
@@ -83,6 +85,58 @@ sequant ready # Boxed pre-flight: which issues are ready to run?
|
|
|
83
85
|
|
|
84
86
|
See the [`sequant ready` command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/ready-command.md) for the full pre-flight readiness check.
|
|
85
87
|
|
|
88
|
+
## Riding out a rate-limit window
|
|
89
|
+
|
|
90
|
+
By default, a rate limit whose window reopens hours from now halts the run, so a multi-hour job needs a manual restart. `--auto-wait <minutes>` opts into waiting instead:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
sequant run 42 --auto-wait 360 # wait up to 6 hours total for the window to reopen
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- **Off by default** (`0`) — the halt behavior is unchanged unless you ask for the wait.
|
|
97
|
+
- The value is a **total** budget per issue, not per occurrence, capped at 2 waits.
|
|
98
|
+
- **Never waits on out-of-credits failures** — credits are purchased, not waited out. (These do carry a reset timestamp, so the gate is the error type, not the timestamp.)
|
|
99
|
+
- The wait is shown live and Ctrl-C ends it promptly.
|
|
100
|
+
- **In-process only: it does not survive closing the terminal** — for waits that must survive a closed terminal or reboot, use halt-and-resume below.
|
|
101
|
+
- Worktree and issue locks are held throughout — deliberate, since Claude rate limits are account-wide and no other run could progress during the window anyway.
|
|
102
|
+
|
|
103
|
+
Also settable as `run.autoWaitMinutes` or `SEQUANT_AUTO_WAIT_MINUTES`. Full details in the [run command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/run-command.md#auto-wait-for-a-rate-limit-window).
|
|
104
|
+
|
|
105
|
+
### Durable recovery: `sequant resume`
|
|
106
|
+
|
|
107
|
+
Without `--auto-wait` (or once its budget is exhausted), a run that fails on a waitable rate-limit window writes a durable halt record with its reset time and exits cleanly, releasing the per-issue lock. `sequant resume` re-enters after the window reopens, skipping completed phases and issues — safe to invoke repeatedly, so a single cron/launchd entry handles unattended machines. Recipes and full mechanics in the [halt-and-resume reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/halt-and-resume.md).
|
|
108
|
+
|
|
109
|
+
## Wait for CI, then verify (`merge --watch`)
|
|
110
|
+
|
|
111
|
+
`sequant merge <issue> --watch` kills the "merge after green" polling loop: it waits for the PR's CI checks to finish before running merge-check, so the verdict is real the moment the command exits.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
sequant merge 818 --watch # poll CI to terminal, then run merge-check
|
|
115
|
+
sequant merge 818 --watch --scan --post # composes with depth flags and --post
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- **It never merges** — `--watch` only decides _when_ the existing report runs; the human merge gate stays.
|
|
119
|
+
- **Foreground only** — a plain poll loop, no daemon or OS notifications. Chain on the exit code to notify yourself.
|
|
120
|
+
- **Configurable** `--interval <seconds>` (default 30) and `--timeout <seconds>` (default 1800); a timeout exits with a distinct code (3).
|
|
121
|
+
- **Dispatch blocks short-circuit to BLOCKED** with the cause — merge conflicts (`CONFLICTING`), zero checks after a dispatch block, and a runner-never-started billing lockout — instead of polling until timeout.
|
|
122
|
+
|
|
123
|
+
Full details in the [merge command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/merge-command.md).
|
|
124
|
+
|
|
125
|
+
## Running the ready gate inside `run`
|
|
126
|
+
|
|
127
|
+
`--ready-gate` opts a `sequant run` into the same post-QA gate as `sequant ready`, without the second manual command — it automates the habitual any-gaps → fix-gaps second look:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
sequant run 42 --ready-gate # phases, then the gate, then a PR — never merges
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- **Off by default** — without the flag the run path is unchanged.
|
|
134
|
+
- Once the standard phases pass, the gate runs a full-weight `qa → loop → qa` pass (to `ready.policy`) **before** the PR opens, so its fixes land in the PR.
|
|
135
|
+
- **It never merges.** The run stops at the human merge gate with the issue `waiting_for_human_merge` (threshold reached) or `blocked` (a guard halted it).
|
|
136
|
+
- Reuses `ready`'s policy, iteration cap, and stagnation guard — **no new settings**.
|
|
137
|
+
|
|
138
|
+
Full details in the [run command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/run-command.md#ready-gate-post-qa-second-look).
|
|
139
|
+
|
|
86
140
|
## Documentation
|
|
87
141
|
|
|
88
142
|
- [Getting Started](https://github.com/sequant-io/sequant/tree/main/docs/getting-started)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Shared naming contract for /exec parallel-group marker files (#881).
|
|
3
|
+
#
|
|
4
|
+
# One writer (the /exec skill) and two readers (pre-tool.sh, post-tool.sh) must
|
|
5
|
+
# agree on (a) the marker filename and (b) how the owning project is identified.
|
|
6
|
+
# Before #881 the filename was a global constant, so a parallel group in ONE
|
|
7
|
+
# project silently redirected worktree enforcement for EVERY concurrent Claude
|
|
8
|
+
# session on the machine — the first matching marker in the shared temp dir won,
|
|
9
|
+
# regardless of which project wrote it. Keeping the scheme in this single sourced
|
|
10
|
+
# file is what stops the writer and the readers from drifting apart (AC-4).
|
|
11
|
+
#
|
|
12
|
+
# Marker filename: ${TMPDIR}/claude-parallel-<project-hash>-<group-id>.marker
|
|
13
|
+
# Marker contents: line 1 = worktree path, line 2 = owning project root.
|
|
14
|
+
|
|
15
|
+
# parallel_marker_project_root — the canonical identity of the current project,
|
|
16
|
+
# derived identically in skill-context and hook-context. CLAUDE_PROJECT_DIR is
|
|
17
|
+
# set by Claude Code for both the skill's shell and the hook's shell within the
|
|
18
|
+
# same session; the git-toplevel fallback resolves to the same worktree when it
|
|
19
|
+
# is unset. A mismatch here would silently re-introduce the cross-project
|
|
20
|
+
# collision this fix exists to close, so the two contexts MUST agree.
|
|
21
|
+
parallel_marker_project_root() {
|
|
22
|
+
if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
|
|
23
|
+
printf '%s' "$CLAUDE_PROJECT_DIR"
|
|
24
|
+
else
|
|
25
|
+
git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$PWD"
|
|
26
|
+
fi
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# parallel_marker_hash — a stable per-project hash used in the marker filename so
|
|
30
|
+
# two projects running parallel groups at once cannot collide (AC-1). Reuses the
|
|
31
|
+
# md5-with-md5sum-fallback idiom already used for the per-file lock in
|
|
32
|
+
# pre-tool.sh. printf (no trailing newline) keeps writer and reader identical.
|
|
33
|
+
parallel_marker_hash() {
|
|
34
|
+
local root
|
|
35
|
+
root="$(parallel_marker_project_root)"
|
|
36
|
+
printf '%s' "$root" | md5 -q 2>/dev/null || printf '%s' "$root" | md5sum | cut -d' ' -f1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# parallel_marker_prefix — the ${TMPDIR}-anchored, project-scoped filename prefix
|
|
40
|
+
# that both the writer and the readers glob on (AC-2). Honors $TMPDIR (macOS's
|
|
41
|
+
# per-user temp) with a /tmp fallback, matching the hooks' own _TMPDIR.
|
|
42
|
+
parallel_marker_prefix() {
|
|
43
|
+
local tmp="${TMPDIR:-/tmp}"
|
|
44
|
+
printf '%s/claude-parallel-%s-' "$tmp" "$(parallel_marker_hash)"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# parallel_marker_path <group-id> — the full marker path for one group.
|
|
48
|
+
parallel_marker_path() {
|
|
49
|
+
printf '%s%s.marker' "$(parallel_marker_prefix)" "$1"
|
|
50
|
+
}
|
|
@@ -77,22 +77,35 @@ mkdir -p "$_LOG_DIR" 2>/dev/null || _LOG_DIR="${_TMPDIR}"
|
|
|
77
77
|
TIMING_LOG="${_LOG_DIR}/claude-timing.log"
|
|
78
78
|
QUALITY_LOG="${_LOG_DIR}/claude-quality.log"
|
|
79
79
|
TESTS_LOG="${_LOG_DIR}/claude-tests.log"
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
# Parallel-group marker naming (#881). Source the shared helper next to this
|
|
82
|
+
# hook so the project-scoped prefix stays identical to the writer and pre-tool.sh.
|
|
83
|
+
_MARKER_HELPER="$(dirname "${BASH_SOURCE[0]:-$0}")/parallel-marker.sh"
|
|
84
|
+
if [[ -f "$_MARKER_HELPER" ]]; then
|
|
85
|
+
# shellcheck source=parallel-marker.sh disable=SC1091
|
|
86
|
+
source "$_MARKER_HELPER"
|
|
87
|
+
PARALLEL_MARKER_PREFIX="$(parallel_marker_prefix)"
|
|
88
|
+
else
|
|
89
|
+
PARALLEL_MARKER_PREFIX="${_TMPDIR}/claude-parallel-"
|
|
90
|
+
fi
|
|
81
91
|
|
|
82
92
|
# === AGENT ID DETECTION ===
|
|
83
|
-
# For parallel agents, detect group ID from marker files
|
|
84
|
-
#
|
|
93
|
+
# For parallel agents, detect group ID from marker files. The glob is scoped to
|
|
94
|
+
# the current project's marker prefix (#881) so a foreign project's marker never
|
|
95
|
+
# labels this session's timing rows.
|
|
85
96
|
AGENT_ID=""
|
|
86
97
|
IS_PARALLEL_AGENT="false"
|
|
98
|
+
_MARKER_BASE=$(basename "$PARALLEL_MARKER_PREFIX")
|
|
87
99
|
# Find marker files using find (works in both bash and zsh)
|
|
88
100
|
while IFS= read -r marker; do
|
|
89
101
|
if [[ -n "$marker" && -f "$marker" ]]; then
|
|
90
|
-
# Extract group ID
|
|
91
|
-
AGENT_ID=$(basename "$marker"
|
|
102
|
+
# Extract group ID: strip the project-scoped prefix and the suffix.
|
|
103
|
+
AGENT_ID=$(basename "$marker" .marker)
|
|
104
|
+
AGENT_ID=${AGENT_ID#"$_MARKER_BASE"}
|
|
92
105
|
IS_PARALLEL_AGENT="true"
|
|
93
106
|
break
|
|
94
107
|
fi
|
|
95
|
-
done < <(find "${_TMPDIR}" -maxdepth 1 -name "
|
|
108
|
+
done < <(find "${_TMPDIR}" -maxdepth 1 -name "${_MARKER_BASE}*.marker" 2>/dev/null)
|
|
96
109
|
|
|
97
110
|
# === TIMING END ===
|
|
98
111
|
# Include agent ID in log format if available (AC-4)
|
|
@@ -253,7 +266,7 @@ if [[ "${CLAUDE_HOOKS_COVERAGE:-}" == "true" ]]; then
|
|
|
253
266
|
COVERAGE_LOG="${_LOG_DIR}/claude-coverage.log"
|
|
254
267
|
|
|
255
268
|
# Get changed source files (excluding tests)
|
|
256
|
-
changed_files=$(git diff main...HEAD --name-only 2>/dev/null | grep -E '\.(ts|tsx|js|jsx)$' | grep -v -E '\.test\.|\.spec\.|__tests__' || true)
|
|
269
|
+
changed_files=$(git diff origin/main...HEAD --name-only 2>/dev/null | grep -E '\.(ts|tsx|js|jsx)$' | grep -v -E '\.test\.|\.spec\.|__tests__' || true)
|
|
257
270
|
|
|
258
271
|
if [[ -n "$changed_files" ]]; then
|
|
259
272
|
echo "$(date +%H:%M:%S) COVERAGE_ANALYSIS: Checking test coverage for changed files" >> "$QUALITY_LOG"
|