sequant 2.2.0 → 2.3.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 +73 -0
- package/dist/bin/cli.js +94 -9
- package/dist/src/commands/doctor.d.ts +25 -0
- package/dist/src/commands/doctor.js +36 -1
- package/dist/src/commands/locks.d.ts +67 -0
- package/dist/src/commands/locks.js +290 -0
- package/dist/src/commands/merge.js +11 -0
- package/dist/src/commands/prompt.d.ts +39 -0
- package/dist/src/commands/prompt.js +179 -0
- package/dist/src/commands/run-display.d.ts +11 -2
- package/dist/src/commands/run-display.js +62 -28
- package/dist/src/commands/run-progress.d.ts +32 -0
- package/dist/src/commands/run-progress.js +76 -0
- package/dist/src/commands/run.js +80 -18
- package/dist/src/commands/stats.d.ts +2 -0
- package/dist/src/commands/stats.js +94 -8
- package/dist/src/commands/status.js +12 -0
- package/dist/src/commands/watch.d.ts +16 -0
- package/dist/src/commands/watch.js +147 -0
- package/dist/src/lib/ac-linter.d.ts +1 -1
- package/dist/src/lib/ac-linter.js +81 -0
- package/dist/src/lib/assess-collision-detect.d.ts +91 -0
- package/dist/src/lib/assess-collision-detect.js +217 -0
- package/dist/src/lib/assess-comment-parser.d.ts +59 -1
- package/dist/src/lib/assess-comment-parser.js +124 -2
- package/dist/src/lib/cli-ui/format.d.ts +19 -0
- package/dist/src/lib/cli-ui/format.js +34 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +181 -0
- package/dist/src/lib/cli-ui/run-renderer-types.js +7 -0
- package/dist/src/lib/cli-ui/run-renderer.d.ts +239 -0
- package/dist/src/lib/cli-ui/run-renderer.js +1173 -0
- package/dist/src/lib/heuristics/behavior-rule-detector.d.ts +94 -0
- package/dist/src/lib/heuristics/behavior-rule-detector.js +467 -0
- package/dist/src/lib/locks/index.d.ts +7 -0
- package/dist/src/lib/locks/index.js +5 -0
- package/dist/src/lib/locks/lock-manager.d.ts +168 -0
- package/dist/src/lib/locks/lock-manager.js +433 -0
- package/dist/src/lib/locks/types.d.ts +59 -0
- package/dist/src/lib/locks/types.js +31 -0
- package/dist/src/lib/qa/markdown-only-ci.d.ts +46 -0
- package/dist/src/lib/qa/markdown-only-ci.js +74 -0
- package/dist/src/lib/relay/activation.d.ts +60 -0
- package/dist/src/lib/relay/activation.js +122 -0
- package/dist/src/lib/relay/archive.d.ts +34 -0
- package/dist/src/lib/relay/archive.js +106 -0
- package/dist/src/lib/relay/frame.d.ts +20 -0
- package/dist/src/lib/relay/frame.js +76 -0
- package/dist/src/lib/relay/index.d.ts +13 -0
- package/dist/src/lib/relay/index.js +13 -0
- package/dist/src/lib/relay/paths.d.ts +43 -0
- package/dist/src/lib/relay/paths.js +59 -0
- package/dist/src/lib/relay/pid.d.ts +34 -0
- package/dist/src/lib/relay/pid.js +72 -0
- package/dist/src/lib/relay/reader.d.ts +35 -0
- package/dist/src/lib/relay/reader.js +115 -0
- package/dist/src/lib/relay/types.d.ts +68 -0
- package/dist/src/lib/relay/types.js +76 -0
- package/dist/src/lib/relay/writer.d.ts +48 -0
- package/dist/src/lib/relay/writer.js +113 -0
- package/dist/src/lib/settings.d.ts +31 -1
- package/dist/src/lib/settings.js +18 -3
- package/dist/src/lib/version-check.d.ts +60 -5
- package/dist/src/lib/version-check.js +97 -9
- package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
- package/dist/src/lib/workflow/batch-executor.js +248 -175
- package/dist/src/lib/workflow/config-resolver.js +4 -0
- package/dist/src/lib/workflow/heartbeat.d.ts +71 -0
- package/dist/src/lib/workflow/heartbeat.js +194 -0
- package/dist/src/lib/workflow/phase-executor.d.ts +62 -8
- package/dist/src/lib/workflow/phase-executor.js +157 -16
- package/dist/src/lib/workflow/phase-mapper.d.ts +3 -2
- package/dist/src/lib/workflow/phase-mapper.js +17 -20
- package/dist/src/lib/workflow/platforms/github.d.ts +1 -1
- package/dist/src/lib/workflow/platforms/github.js +20 -3
- package/dist/src/lib/workflow/pr-status.d.ts +18 -2
- package/dist/src/lib/workflow/pr-status.js +41 -9
- package/dist/src/lib/workflow/qa-stagnation.d.ts +117 -0
- package/dist/src/lib/workflow/qa-stagnation.js +179 -0
- package/dist/src/lib/workflow/run-orchestrator.d.ts +39 -0
- package/dist/src/lib/workflow/run-orchestrator.js +340 -15
- package/dist/src/lib/workflow/run-reflect.js +1 -1
- package/dist/src/lib/workflow/run-state.d.ts +71 -0
- package/dist/src/lib/workflow/run-state.js +14 -0
- package/dist/src/lib/workflow/state-cleanup.d.ts +13 -5
- package/dist/src/lib/workflow/state-cleanup.js +17 -5
- package/dist/src/lib/workflow/state-manager.d.ts +12 -1
- package/dist/src/lib/workflow/state-manager.js +37 -0
- package/dist/src/lib/workflow/state-schema.d.ts +62 -0
- package/dist/src/lib/workflow/state-schema.js +35 -1
- package/dist/src/lib/workflow/types.d.ts +74 -1
- package/dist/src/lib/workflow/worktree-manager.d.ts +8 -1
- package/dist/src/lib/workflow/worktree-manager.js +15 -6
- package/dist/src/mcp/tools/run.d.ts +44 -0
- package/dist/src/mcp/tools/run.js +104 -13
- package/dist/src/ui/tui/App.d.ts +14 -0
- package/dist/src/ui/tui/App.js +41 -0
- package/dist/src/ui/tui/ElapsedTimer.d.ts +10 -0
- package/dist/src/ui/tui/ElapsedTimer.js +31 -0
- package/dist/src/ui/tui/Header.d.ts +6 -0
- package/dist/src/ui/tui/Header.js +15 -0
- package/dist/src/ui/tui/IssueBox.d.ts +16 -0
- package/dist/src/ui/tui/IssueBox.js +68 -0
- package/dist/src/ui/tui/Spinner.d.ts +9 -0
- package/dist/src/ui/tui/Spinner.js +18 -0
- package/dist/src/ui/tui/index.d.ts +15 -0
- package/dist/src/ui/tui/index.js +29 -0
- package/dist/src/ui/tui/theme.d.ts +29 -0
- package/dist/src/ui/tui/theme.js +52 -0
- package/dist/src/ui/tui/truncate.d.ts +11 -0
- package/dist/src/ui/tui/truncate.js +31 -0
- package/package.json +10 -3
- package/templates/agents/sequant-explorer.md +1 -0
- package/templates/agents/sequant-qa-checker.md +2 -1
- package/templates/agents/sequant-testgen.md +1 -0
- package/templates/hooks/post-tool.sh +11 -0
- package/templates/hooks/pre-tool.sh +18 -9
- package/templates/hooks/relay-check.sh +107 -0
- package/templates/relay/frame.txt +11 -0
- package/templates/scripts/cleanup-worktree.sh +25 -3
- package/templates/scripts/new-feature.sh +6 -0
- package/templates/skills/_shared/references/behavior-rule-detection.md +205 -0
- package/templates/skills/_shared/references/subagent-types.md +21 -8
- package/templates/skills/assess/SKILL.md +103 -49
- package/templates/skills/assess/references/predicted-collision-detection.md +109 -0
- package/templates/skills/docs/SKILL.md +141 -22
- package/templates/skills/exec/SKILL.md +10 -8
- package/templates/skills/fullsolve/SKILL.md +79 -5
- package/templates/skills/loop/SKILL.md +28 -0
- package/templates/skills/merger/SKILL.md +621 -0
- package/templates/skills/qa/SKILL.md +727 -8
- package/templates/skills/setup/SKILL.md +6 -0
- package/templates/skills/spec/SKILL.md +52 -0
- package/templates/skills/spec/references/parallel-groups.md +7 -0
- package/templates/skills/spec/references/recommended-workflow.md +4 -2
- package/templates/skills/testgen/SKILL.md +24 -17
|
@@ -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.
|
|
11
|
+
"version": "2.3.0",
|
|
12
12
|
"author": {
|
|
13
13
|
"name": "sequant-io",
|
|
14
14
|
"email": "hello@sequant.io"
|
package/README.md
CHANGED
|
@@ -207,14 +207,87 @@ npx sequant status # Show version and config
|
|
|
207
207
|
npx sequant run <issues...> # Execute workflow
|
|
208
208
|
npx sequant merge <issues...> # Batch integration QA before merging
|
|
209
209
|
npx sequant state <cmd> # Manage workflow state (init/rebuild/clean)
|
|
210
|
+
npx sequant locks <cmd> # Inspect/clear per-issue concurrency locks
|
|
210
211
|
npx sequant stats # View local workflow analytics
|
|
211
212
|
npx sequant dashboard # Launch real-time workflow dashboard
|
|
212
213
|
```
|
|
213
214
|
|
|
215
|
+
Cohort filtering: `npx sequant stats --label docs --since 2026-01-01`. See [Analytics — Filtering by Label or Date](docs/reference/analytics.md#filtering-by-label-or-date) for details.
|
|
216
|
+
|
|
214
217
|
See [Run Command Options](docs/reference/run-command.md), [Merge Command](docs/reference/merge-command.md), [State Command](docs/reference/state-command.md), and [Analytics](docs/reference/analytics.md) for details.
|
|
215
218
|
|
|
216
219
|
---
|
|
217
220
|
|
|
221
|
+
## Concurrency
|
|
222
|
+
|
|
223
|
+
Sequant prevents two sessions from working on the same GitHub issue at the
|
|
224
|
+
same time. When `sequant run` starts, each issue claims a per-issue lock at
|
|
225
|
+
`.sequant/locks/<issue>.lock` containing the holder's PID, hostname, start
|
|
226
|
+
time, and command. A second session attempting the same issue is skipped
|
|
227
|
+
with a clear error and the rest of the batch continues.
|
|
228
|
+
|
|
229
|
+
**Stale recovery.** Locks are auto-cleared in three situations:
|
|
230
|
+
|
|
231
|
+
1. Same host, PID no longer alive → cleared immediately (covers SIGKILL and
|
|
232
|
+
crashes).
|
|
233
|
+
2. Cross-host, lock older than 2 hours → cleared by age.
|
|
234
|
+
3. Manual: `sequant locks clear <issue>` (with safety check by default).
|
|
235
|
+
|
|
236
|
+
**Taking over an active session.** `sequant run --force <issue>` writes a
|
|
237
|
+
new lock claiming the issue. Add `--signal-other` to also SIGTERM the prior
|
|
238
|
+
PID (same host, alive only). Plain `--force` does not signal — use it when
|
|
239
|
+
you already know the other session is dead.
|
|
240
|
+
|
|
241
|
+
**Inspecting locks.**
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
npx sequant locks list # Show every active lock
|
|
245
|
+
npx sequant locks clear 123 # Clear lock for #123 (refuses fresh)
|
|
246
|
+
npx sequant locks clear 123 --force # Clear unconditionally
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Skill wiring** (`/fullsolve`, `/assess`). The `/fullsolve` skill claims the
|
|
250
|
+
lock at Phase 0.3, releases it at Phase 5.5, AND releases on every halt
|
|
251
|
+
branch (spec failure, exec exhausted, etc.) so an aborted run frees the
|
|
252
|
+
lock immediately. `/assess` probes it read-only and surfaces a dashboard
|
|
253
|
+
warning when any issue is in use. Both use these subcommands directly from
|
|
254
|
+
bash:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
npx sequant locks acquire 123 --command="/fullsolve 123" --skip-pid-check
|
|
258
|
+
npx sequant locks release 123 # idempotent; safe on every error path
|
|
259
|
+
npx sequant locks check 123 --json # exit 1 when held, prints holder JSON
|
|
260
|
+
npx sequant locks check-batch 100 101 102 # /assess: emits ⚠ lines for held issues only
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`--skip-pid-check` is required for skill shells: the Node process that runs
|
|
264
|
+
`locks acquire` exits immediately, so its PID is dead before the lock is
|
|
265
|
+
released. With the flag set, stale detection falls back to age-only on the
|
|
266
|
+
holder's own host. The default skill-lock TTL is **6h** (separate from the
|
|
267
|
+
2h cross-host TTL) — long enough to cover virtually every `/fullsolve` run
|
|
268
|
+
including multi-iteration QA loops. Override per-process via
|
|
269
|
+
`SEQUANT_SKILL_LOCK_TTL_MS=<milliseconds>`.
|
|
270
|
+
|
|
271
|
+
A skill that crashes mid-run leaves at most a 6h orphan; clear it manually
|
|
272
|
+
with `sequant locks clear <issue>` to recover sooner. The skill's explicit
|
|
273
|
+
release calls on every halt branch (see `.claude/skills/fullsolve/SKILL.md`
|
|
274
|
+
Phase 0.3 release contract) mean this corner case should be rare in practice.
|
|
275
|
+
|
|
276
|
+
**Read-only commands** (`status`, `merge`, `/assess`) warn when an issue is
|
|
277
|
+
locked but do not block.
|
|
278
|
+
|
|
279
|
+
**MCP / orchestrator mode.** When the `SEQUANT_ORCHESTRATOR` env var is set
|
|
280
|
+
(in-process or remote MCP-driven runs), all lock operations are no-ops —
|
|
281
|
+
the orchestrator caller is responsible for any coordination.
|
|
282
|
+
|
|
283
|
+
**Caveats.** The lock relies on `open(O_CREAT | O_EXCL)` and is reliable on
|
|
284
|
+
local filesystems. NFS and other network filesystems may not honor those
|
|
285
|
+
semantics; users on networked repos may see false positives. The
|
|
286
|
+
`SEQUANT_LOCKS_DIR` env var overrides the lock directory (used in tests
|
|
287
|
+
and unusual layouts).
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
218
291
|
## Configuration
|
|
219
292
|
|
|
220
293
|
```json
|
package/dist/bin/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import { fileURLToPath } from "url";
|
|
|
10
10
|
import { dirname, resolve } from "path";
|
|
11
11
|
import { readFileSync } from "fs";
|
|
12
12
|
import { initCommand } from "../src/commands/init.js";
|
|
13
|
-
import { isLocalNodeModulesInstall } from "../src/lib/version-check.js";
|
|
13
|
+
import { buildHomeStrayWarning, getInstallRoot, isHomeStrayInstall, isLocalNodeModulesInstall, } from "../src/lib/version-check.js";
|
|
14
14
|
import { configureUI, banner } from "../src/lib/cli-ui.js";
|
|
15
15
|
import { isCI, isStdoutTTY } from "../src/lib/tty.js";
|
|
16
16
|
import { detectPackageManagerSync, getPackageManagerCommands, } from "../src/lib/stacks.js";
|
|
@@ -47,6 +47,9 @@ import { stateInitCommand, stateRebuildCommand, stateCleanCommand, } from "../sr
|
|
|
47
47
|
import { syncCommand, areSkillsOutdated } from "../src/commands/sync.js";
|
|
48
48
|
import { mergeCommand } from "../src/commands/merge.js";
|
|
49
49
|
import { conventionsCommand } from "../src/commands/conventions.js";
|
|
50
|
+
import { locksListCommand, locksClearCommand, locksAcquireCommand, locksReleaseCommand, locksCheckCommand, locksCheckBatchCommand, } from "../src/commands/locks.js";
|
|
51
|
+
import { promptCommand } from "../src/commands/prompt.js";
|
|
52
|
+
import { watchCommand } from "../src/commands/watch.js";
|
|
50
53
|
import { getManifest } from "../src/lib/manifest.js";
|
|
51
54
|
const program = new Command();
|
|
52
55
|
// Handle --no-color before parsing
|
|
@@ -62,13 +65,22 @@ configureUI({
|
|
|
62
65
|
isCI: isCI(),
|
|
63
66
|
minimal: process.env.SEQUANT_MINIMAL === "1",
|
|
64
67
|
});
|
|
65
|
-
// Warn if running from
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Warn if running from a problematic install location.
|
|
69
|
+
// The home-stray case ($HOME/node_modules/sequant) gets a distinct warning
|
|
70
|
+
// because it pollutes resolution for every subdirectory of $HOME, which the
|
|
71
|
+
// generic "local node_modules" message doesn't communicate. Resolve the
|
|
72
|
+
// install root once and pass it to both predicates to avoid a second walk.
|
|
73
|
+
if (!process.argv.includes("--quiet")) {
|
|
74
|
+
const installRoot = getInstallRoot();
|
|
75
|
+
if (installRoot && isHomeStrayInstall(installRoot)) {
|
|
76
|
+
console.warn(chalk.yellow(buildHomeStrayWarning(installRoot)));
|
|
77
|
+
}
|
|
78
|
+
else if (isLocalNodeModulesInstall()) {
|
|
79
|
+
const pmCommands = getPackageManagerCommands(detectPackageManagerSync());
|
|
80
|
+
console.warn(chalk.yellow("! Running sequant from local node_modules\n" +
|
|
81
|
+
" For latest version: npx sequant@latest\n" +
|
|
82
|
+
` To remove local: ${pmCommands.removePkg} sequant\n`));
|
|
83
|
+
}
|
|
72
84
|
}
|
|
73
85
|
program
|
|
74
86
|
.name("sequant")
|
|
@@ -104,6 +116,7 @@ program
|
|
|
104
116
|
.command("doctor")
|
|
105
117
|
.description("Check your Sequant installation for issues")
|
|
106
118
|
.option("--skip-issue-check", "Skip closed-issue verification check")
|
|
119
|
+
.option("-q, --quiet", "Suppress informational warnings")
|
|
107
120
|
.action(doctorCommand);
|
|
108
121
|
program
|
|
109
122
|
.command("status")
|
|
@@ -142,8 +155,10 @@ program
|
|
|
142
155
|
.option("--smart-tests", "Enable smart test detection (default)")
|
|
143
156
|
.option("--no-smart-tests", "Disable smart test detection")
|
|
144
157
|
.option("--testgen", "Run testgen phase after spec")
|
|
158
|
+
.option("--security-review", "Run security-review phase after spec")
|
|
145
159
|
.option("--quiet", "Suppress version warnings and non-essential output")
|
|
146
160
|
.option("--chain", "Chain issues: each branches from previous (implies --sequential)")
|
|
161
|
+
.option("--stacked", "Stack PRs: middle PRs target predecessor branch instead of main; first/last target main (implies --chain)")
|
|
147
162
|
.option("--qa-gate", "Wait for QA pass before starting next issue in chain (requires --chain)")
|
|
148
163
|
.option("--base <branch>", "Base branch for worktree creation (default: main or settings.run.defaultBase)")
|
|
149
164
|
.option("--no-mcp", "Disable MCP server injection in headless mode")
|
|
@@ -151,12 +166,41 @@ program
|
|
|
151
166
|
.option("--resume", "Resume from last completed phase (reads phase markers from GitHub)")
|
|
152
167
|
.option("--no-rebase", "Skip pre-PR rebase onto origin/main (use when you want to handle rebasing manually)")
|
|
153
168
|
.option("--no-pr", "Skip PR creation after successful QA (manual PR workflow)")
|
|
154
|
-
.option("-f, --force", "Force re-execution of completed issues (bypass pre-flight state guard)")
|
|
169
|
+
.option("-f, --force", "Force re-execution of completed issues (bypass pre-flight state guard) and take over per-issue locks")
|
|
170
|
+
.option("--signal-other", "With --force, SIGTERM the prior PID holding the lock (same-host alive only)")
|
|
155
171
|
.option("--concurrency <n>", "Max concurrent issues in parallel mode (default: 3)", parseInt)
|
|
156
172
|
.option("--isolate-parallel", "Isolate parallel agent groups in separate worktrees (prevents file conflicts)")
|
|
157
173
|
.option("--reflect", "Analyze run results and suggest improvements")
|
|
158
174
|
.option("--agent <name>", 'Agent driver for phase execution (default: "claude-code")')
|
|
175
|
+
.option("--experimental-tui", "Render live multi-issue dashboard (requires TTY; falls back to linear output when piped)")
|
|
176
|
+
.option("--no-relay", "Disable interactive relay (#383); `sequant prompt` cannot reach this run")
|
|
159
177
|
.action(runCommand);
|
|
178
|
+
program
|
|
179
|
+
.command("prompt")
|
|
180
|
+
.description("Send a message into a running headless sequant session (#383)")
|
|
181
|
+
.argument("[args...]", '[<issue>] "<message>"')
|
|
182
|
+
.option("--type <type>", "Message type: query (default), directive, abort", "query")
|
|
183
|
+
.option("--json", "Output as JSON")
|
|
184
|
+
.action((args, options) => {
|
|
185
|
+
return promptCommand({
|
|
186
|
+
args,
|
|
187
|
+
options: {
|
|
188
|
+
type: options.type,
|
|
189
|
+
json: Boolean(options.json),
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
program
|
|
194
|
+
.command("watch")
|
|
195
|
+
.description("Tail the relay outbox for replies from a running sequant session (#383)")
|
|
196
|
+
.argument("<issue>", "Issue number to watch")
|
|
197
|
+
.option("--json", "Output as JSON lines")
|
|
198
|
+
.action((issueArg, options) => {
|
|
199
|
+
return watchCommand({
|
|
200
|
+
args: [issueArg],
|
|
201
|
+
options: { json: Boolean(options.json) },
|
|
202
|
+
});
|
|
203
|
+
});
|
|
160
204
|
program
|
|
161
205
|
.command("merge")
|
|
162
206
|
.description("Batch-level integration QA — verify feature branches before merging")
|
|
@@ -195,6 +239,8 @@ program
|
|
|
195
239
|
.option("--csv", "Output as CSV")
|
|
196
240
|
.option("--json", "Output as JSON")
|
|
197
241
|
.option("--detailed", "Show detailed analytics (QA verdicts, trends, label segmentation)")
|
|
242
|
+
.option("--label <name>", "Filter to runs whose issues carry the given label")
|
|
243
|
+
.option("--since <date>", "Filter to runs with startTime on/after YYYY-MM-DD (UTC)")
|
|
198
244
|
.action(statsCommand);
|
|
199
245
|
program
|
|
200
246
|
.command("dashboard")
|
|
@@ -244,6 +290,45 @@ stateCmd
|
|
|
244
290
|
.option("--max-age <days>", "Remove entries older than N days", parseInt)
|
|
245
291
|
.option("--all", "Remove all orphaned entries (merged and abandoned)")
|
|
246
292
|
.action(stateCleanCommand);
|
|
293
|
+
// Per-issue concurrency locks (#625)
|
|
294
|
+
const locksCmd = program
|
|
295
|
+
.command("locks")
|
|
296
|
+
.description("Inspect and clear per-issue concurrency locks");
|
|
297
|
+
locksCmd
|
|
298
|
+
.command("list")
|
|
299
|
+
.description("List active locks with staleness metadata")
|
|
300
|
+
.option("--json", "Output as JSON")
|
|
301
|
+
.action(locksListCommand);
|
|
302
|
+
locksCmd
|
|
303
|
+
.command("clear <issue>")
|
|
304
|
+
.description("Manually clear the lock for an issue (safety check unless --force)")
|
|
305
|
+
.option("-f, --force", "Skip safety check; clear even a fresh same-host alive lock")
|
|
306
|
+
.option("--json", "Output as JSON")
|
|
307
|
+
.action(locksClearCommand);
|
|
308
|
+
locksCmd
|
|
309
|
+
.command("acquire <issue>")
|
|
310
|
+
.description("Claim the lock for an issue (used by /fullsolve, /assess; exits 1 if held)")
|
|
311
|
+
.option("--command <command>", "Human-readable command label", "unknown")
|
|
312
|
+
.option("--skip-pid-check", "Mark the lock so stale recovery skips same-host PID checks (use from skill shells)")
|
|
313
|
+
.option("-f, --force", "Take over even if another holder is alive")
|
|
314
|
+
.option("--signal-other", "When forcing, SIGTERM the prior same-host holder if alive")
|
|
315
|
+
.option("--json", "Output as JSON")
|
|
316
|
+
.action(locksAcquireCommand);
|
|
317
|
+
locksCmd
|
|
318
|
+
.command("release <issue>")
|
|
319
|
+
.description("Release a lock previously acquired on this host (skill or current process)")
|
|
320
|
+
.option("--json", "Output as JSON")
|
|
321
|
+
.action(locksReleaseCommand);
|
|
322
|
+
locksCmd
|
|
323
|
+
.command("check <issue>")
|
|
324
|
+
.description("Read-only probe: print lock holder if any; exit 1 when held (for /assess)")
|
|
325
|
+
.option("--json", "Output as JSON")
|
|
326
|
+
.action(locksCheckCommand);
|
|
327
|
+
locksCmd
|
|
328
|
+
.command("check-batch <issues...>")
|
|
329
|
+
.description("Batch read-only probe: emit canonical ⚠ warning lines for held issues (for /assess dashboard)")
|
|
330
|
+
.option("--json", "Output as JSON instead of canonical text lines")
|
|
331
|
+
.action(locksCheckBatchCommand);
|
|
247
332
|
// Auto-sync skills after npm upgrade (version mismatch detection)
|
|
248
333
|
// Only triggers when skills were previously synced (has .sequant-version marker).
|
|
249
334
|
// Projects that manage skills manually (no marker) are not affected.
|
|
@@ -3,7 +3,32 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export interface DoctorOptions {
|
|
5
5
|
skipIssueCheck?: boolean;
|
|
6
|
+
/** Suppress informational warnings (e.g., upstream subagent routing notice) */
|
|
7
|
+
quiet?: boolean;
|
|
6
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Upstream subagent routing bug notice (anthropics/claude-code#43869).
|
|
11
|
+
* Subagent `model:` declarations are silently ignored; agents run on the
|
|
12
|
+
* parent session's model. Exported as a constant so tests can match it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const UPSTREAM_SUBAGENT_WARNING: {
|
|
15
|
+
readonly primary: "WARN: Claude Code #43869 — subagent model: declarations are ignored. Agents currently run on parent model.";
|
|
16
|
+
readonly url: "https://github.com/anthropics/claude-code/issues/43869";
|
|
17
|
+
readonly inertFieldNote: "Note: agents.model is currently inert (see #43869).";
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Emit the upstream subagent-routing bug notice and (when applicable) the
|
|
21
|
+
* `agents.model` inert-field note. Honors `--quiet`.
|
|
22
|
+
*
|
|
23
|
+
* AC-2: primary line + link, suppressible via `quiet`.
|
|
24
|
+
* AC-4: when the user's `agents.model` differs from the shipped default,
|
|
25
|
+
* append a second-line note flagging the field as inert.
|
|
26
|
+
*/
|
|
27
|
+
export declare function emitUpstreamWarning(opts: {
|
|
28
|
+
quiet?: boolean;
|
|
29
|
+
agentsModel: string;
|
|
30
|
+
defaultAgentsModel: string;
|
|
31
|
+
}): void;
|
|
7
32
|
interface ClosedIssue {
|
|
8
33
|
number: number;
|
|
9
34
|
title: string;
|
|
@@ -8,11 +8,40 @@ import { GitHubProvider } from "../lib/workflow/platforms/github.js";
|
|
|
8
8
|
import { fileExists, isExecutable } from "../lib/fs.js";
|
|
9
9
|
import { getManifest } from "../lib/manifest.js";
|
|
10
10
|
import { commandExists, isGhAuthenticated, isNativeWindows, isWSL, checkOptionalMcpServers, getMcpServersConfig, OPTIONAL_MCP_SERVERS, } from "../lib/system.js";
|
|
11
|
-
import { getSettings } from "../lib/settings.js";
|
|
11
|
+
import { getSettings, DEFAULT_AGENT_SETTINGS } from "../lib/settings.js";
|
|
12
12
|
import { checkVersionThorough, getVersionWarning, } from "../lib/version-check.js";
|
|
13
13
|
import { areSkillsOutdated } from "./sync.js";
|
|
14
14
|
import { readAgentsMd, checkAgentsMdConsistency, AGENTS_MD_PATH, } from "../lib/agents-md.js";
|
|
15
15
|
import { readFile } from "../lib/fs.js";
|
|
16
|
+
// TODO: remove when anthropics/claude-code#43869 closes
|
|
17
|
+
/**
|
|
18
|
+
* Upstream subagent routing bug notice (anthropics/claude-code#43869).
|
|
19
|
+
* Subagent `model:` declarations are silently ignored; agents run on the
|
|
20
|
+
* parent session's model. Exported as a constant so tests can match it.
|
|
21
|
+
*/
|
|
22
|
+
export const UPSTREAM_SUBAGENT_WARNING = {
|
|
23
|
+
primary: "WARN: Claude Code #43869 — subagent model: declarations are ignored. Agents currently run on parent model.",
|
|
24
|
+
url: "https://github.com/anthropics/claude-code/issues/43869",
|
|
25
|
+
inertFieldNote: "Note: agents.model is currently inert (see #43869).",
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Emit the upstream subagent-routing bug notice and (when applicable) the
|
|
29
|
+
* `agents.model` inert-field note. Honors `--quiet`.
|
|
30
|
+
*
|
|
31
|
+
* AC-2: primary line + link, suppressible via `quiet`.
|
|
32
|
+
* AC-4: when the user's `agents.model` differs from the shipped default,
|
|
33
|
+
* append a second-line note flagging the field as inert.
|
|
34
|
+
*/
|
|
35
|
+
export function emitUpstreamWarning(opts) {
|
|
36
|
+
if (opts.quiet)
|
|
37
|
+
return;
|
|
38
|
+
console.log("");
|
|
39
|
+
console.log(chalk.yellow(` ! ${UPSTREAM_SUBAGENT_WARNING.primary}`));
|
|
40
|
+
console.log(chalk.yellow(` ${UPSTREAM_SUBAGENT_WARNING.url}`));
|
|
41
|
+
if (opts.agentsModel !== opts.defaultAgentsModel) {
|
|
42
|
+
console.log(chalk.yellow(` ${UPSTREAM_SUBAGENT_WARNING.inertFieldNote}`));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
16
45
|
/**
|
|
17
46
|
* Labels that indicate an issue should be skipped from closed-issue verification
|
|
18
47
|
* (case-insensitive matching)
|
|
@@ -492,6 +521,12 @@ export async function doctorCommand(options = {}) {
|
|
|
492
521
|
else
|
|
493
522
|
failCount++;
|
|
494
523
|
}
|
|
524
|
+
// Upstream subagent routing notice (AC-2/AC-4)
|
|
525
|
+
emitUpstreamWarning({
|
|
526
|
+
quiet: options.quiet,
|
|
527
|
+
agentsModel: settings.agents.model,
|
|
528
|
+
defaultAgentsModel: DEFAULT_AGENT_SETTINGS.model,
|
|
529
|
+
});
|
|
495
530
|
// Summary with boxed output
|
|
496
531
|
const totalChecks = passCount + warnCount + failCount;
|
|
497
532
|
let summaryTitle;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sequant locks` — inspect and clear per-issue concurrency locks (#625).
|
|
3
|
+
*/
|
|
4
|
+
export interface LocksListOptions {
|
|
5
|
+
json?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface LocksClearOptions {
|
|
8
|
+
force?: boolean;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface LocksAcquireOptions {
|
|
12
|
+
command?: string;
|
|
13
|
+
skipPidCheck?: boolean;
|
|
14
|
+
force?: boolean;
|
|
15
|
+
signalOther?: boolean;
|
|
16
|
+
json?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface LocksReleaseOptions {
|
|
19
|
+
json?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface LocksCheckOptions {
|
|
22
|
+
json?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface LocksCheckBatchOptions {
|
|
25
|
+
json?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** `sequant locks list` — print every active lock with staleness metadata. */
|
|
28
|
+
export declare function locksListCommand(options?: LocksListOptions): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* `sequant locks clear <issue>` — remove a lock manually.
|
|
31
|
+
* By default refuses to clear a fresh same-host lock whose PID is alive;
|
|
32
|
+
* pass `--force` to override.
|
|
33
|
+
*/
|
|
34
|
+
export declare function locksClearCommand(issueArg: string, options?: LocksClearOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* `sequant locks acquire <issue>` — claim the lock from a shell context
|
|
37
|
+
* (e.g. a skill SKILL.md). Use `--skip-pid-check` for skill shells whose
|
|
38
|
+
* Node PID dies between acquire and release; stale recovery then falls back
|
|
39
|
+
* to age-only (2h).
|
|
40
|
+
*
|
|
41
|
+
* Exit codes:
|
|
42
|
+
* 0 — acquired
|
|
43
|
+
* 1 — locked by another holder (printed to stderr unless --json)
|
|
44
|
+
* 2 — invalid arguments
|
|
45
|
+
*/
|
|
46
|
+
export declare function locksAcquireCommand(issueArg: string, options?: LocksAcquireOptions): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* `sequant locks release <issue>` — release a lock previously acquired by
|
|
49
|
+
* a skill shell on this host. Refuses to release locks held by a foreign
|
|
50
|
+
* host or by a different process (use `locks clear --force` for that).
|
|
51
|
+
*/
|
|
52
|
+
export declare function locksReleaseCommand(issueArg: string, options?: LocksReleaseOptions): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* `sequant locks check <issue>` — read-only lock probe for `/assess`-style
|
|
55
|
+
* skills. Prints holder info if any, exit code 0 when free, 1 when held.
|
|
56
|
+
*/
|
|
57
|
+
export declare function locksCheckCommand(issueArg: string, options?: LocksCheckOptions): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* `sequant locks check-batch <issue1> <issue2> ...` — read-only batch probe
|
|
60
|
+
* used by `/assess`. Text mode emits one canonical warning line per held
|
|
61
|
+
* issue (nothing for unheld), so the skill can paste the output directly
|
|
62
|
+
* above its dashboard. JSON mode emits a structured `{ warnings: [...] }`.
|
|
63
|
+
*
|
|
64
|
+
* Exit code is always 0 — `/assess` is read-only and should never abort
|
|
65
|
+
* on locked issues; the warning is informational.
|
|
66
|
+
*/
|
|
67
|
+
export declare function locksCheckBatchCommand(issueArgs: string[], options?: LocksCheckBatchOptions): Promise<void>;
|