sequant 2.8.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 +23 -4
- package/dist/bin/cli.js +99 -18
- 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 +58 -2
- package/dist/marketplace/external_plugins/sequant/hooks/parallel-marker.sh +50 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +38 -10
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +405 -67
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/trust-model.md +18 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +250 -344
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +10 -7
- 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 +4 -2
- 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/reflect/SKILL.md +27 -13
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +80 -68
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +31 -15
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +11 -3
- 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/spec/references/verification-criteria.md +1 -1
- 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 +20 -6
- 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.d.ts +20 -0
- package/dist/src/commands/run-display.js +117 -5
- 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/stats.js +47 -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/assess-collision-detect.d.ts +19 -2
- package/dist/src/lib/assess-collision-detect.js +68 -4
- 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 +79 -9
- package/dist/src/lib/errors.d.ts +84 -6
- package/dist/src/lib/errors.js +142 -17
- 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/manifest.js +1 -17
- 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 +46 -7
- package/dist/src/lib/workflow/batch-executor.d.ts +138 -6
- package/dist/src/lib/workflow/batch-executor.js +550 -54
- package/dist/src/lib/workflow/chain-preflight.d.ts +89 -0
- package/dist/src/lib/workflow/chain-preflight.js +199 -0
- package/dist/src/lib/workflow/chain-resume.d.ts +118 -0
- package/dist/src/lib/workflow/chain-resume.js +170 -0
- 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/dependency-markers.d.ts +29 -0
- package/dist/src/lib/workflow/dependency-markers.js +79 -0
- 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 +21 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +50 -8
- package/dist/src/lib/workflow/error-classifier.d.ts +9 -2
- package/dist/src/lib/workflow/error-classifier.js +14 -1
- 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 +81 -14
- package/dist/src/lib/workflow/metrics-schema.d.ts +42 -3
- package/dist/src/lib/workflow/metrics-schema.js +16 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +2 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +234 -13
- package/dist/src/lib/workflow/phase-executor.js +533 -56
- 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 +37 -0
- package/dist/src/lib/workflow/run-log-schema.js +92 -2
- package/dist/src/lib/workflow/run-orchestrator.d.ts +33 -0
- package/dist/src/lib/workflow/run-orchestrator.js +429 -47
- 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/status-derivation.d.ts +30 -0
- package/dist/src/lib/workflow/status-derivation.js +27 -0
- package/dist/src/lib/workflow/types.d.ts +149 -4
- package/dist/src/lib/workflow/types.js +1 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +70 -2
- package/dist/src/lib/workflow/worktree-manager.js +201 -52
- package/dist/src/mcp/tools/run.d.ts +11 -1
- package/dist/src/mcp/tools/run.js +22 -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 +9 -10
- package/templates/hooks/parallel-marker.sh +50 -0
- package/templates/hooks/post-tool.sh +38 -10
- package/templates/hooks/pre-tool.sh +405 -67
- package/templates/scripts/cleanup-worktree.sh +282 -30
- package/templates/scripts/new-feature.sh +264 -16
- package/templates/skills/_shared/references/trust-model.md +18 -0
- package/templates/skills/assess/SKILL.md +250 -344
- package/templates/skills/assess/references/predicted-collision-detection.md +10 -7
- package/templates/skills/docs/SKILL.md +4 -4
- package/templates/skills/exec/SKILL.md +26 -6
- package/templates/skills/fullsolve/SKILL.md +4 -2
- 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/reflect/SKILL.md +27 -13
- package/templates/skills/reflect/references/documentation-tiers.md +80 -68
- package/templates/skills/reflect/references/phase-reflection.md +31 -15
- package/templates/skills/release/SKILL.md +11 -3
- package/templates/skills/setup/SKILL.md +5 -5
- package/templates/skills/spec/SKILL.md +2 -0
- package/templates/skills/spec/references/verification-criteria.md +1 -1
- 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
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sequant assess-render <file>` — render an `AssessResult` JSON payload (#823).
|
|
3
|
+
*
|
|
4
|
+
* Internal surface. The `/assess` skill builds the JSON, calls this, and pastes
|
|
5
|
+
* stdout verbatim. Shipping it as a CLI subcommand rather than a `scripts/`
|
|
6
|
+
* shell-out is what makes the fix reach end users: `skills/spec/SKILL.md:78`
|
|
7
|
+
* establishes that skills may only run `npx tsx` blocks when the sequant source
|
|
8
|
+
* tree is present, so a script would work in this repo and nowhere else. A
|
|
9
|
+
* subcommand ships via `package.json` `bin` + `files: ["dist", ...]` and is
|
|
10
|
+
* present for every sequant install by definition.
|
|
11
|
+
*
|
|
12
|
+
* All output goes to stdout unmodified — no chalk, no boxes, no TTY branching —
|
|
13
|
+
* because the caller pastes it into a chat transcript and a GitHub comment.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Read, validate, and render an `AssessResult` file.
|
|
17
|
+
*
|
|
18
|
+
* Exits non-zero with a readable stderr message when the file is missing,
|
|
19
|
+
* unreadable, not JSON, or fails schema validation — the skill's Step 6 treats a
|
|
20
|
+
* non-zero exit as its signal to fall back to a hand-drawn dashboard rather than
|
|
21
|
+
* silently emitting nothing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function assessRenderCommand(file: string): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sequant assess-render <file>` — render an `AssessResult` JSON payload (#823).
|
|
3
|
+
*
|
|
4
|
+
* Internal surface. The `/assess` skill builds the JSON, calls this, and pastes
|
|
5
|
+
* stdout verbatim. Shipping it as a CLI subcommand rather than a `scripts/`
|
|
6
|
+
* shell-out is what makes the fix reach end users: `skills/spec/SKILL.md:78`
|
|
7
|
+
* establishes that skills may only run `npx tsx` blocks when the sequant source
|
|
8
|
+
* tree is present, so a script would work in this repo and nowhere else. A
|
|
9
|
+
* subcommand ships via `package.json` `bin` + `files: ["dist", ...]` and is
|
|
10
|
+
* present for every sequant install by definition.
|
|
11
|
+
*
|
|
12
|
+
* All output goes to stdout unmodified — no chalk, no boxes, no TTY branching —
|
|
13
|
+
* because the caller pastes it into a chat transcript and a GitHub comment.
|
|
14
|
+
*/
|
|
15
|
+
import { readFile } from "node:fs/promises";
|
|
16
|
+
import { render } from "../lib/assess/renderer.js";
|
|
17
|
+
import { AssessResultValidationError, parseAssessResult, } from "../lib/assess/types.js";
|
|
18
|
+
/**
|
|
19
|
+
* Read, validate, and render an `AssessResult` file.
|
|
20
|
+
*
|
|
21
|
+
* Exits non-zero with a readable stderr message when the file is missing,
|
|
22
|
+
* unreadable, not JSON, or fails schema validation — the skill's Step 6 treats a
|
|
23
|
+
* non-zero exit as its signal to fall back to a hand-drawn dashboard rather than
|
|
24
|
+
* silently emitting nothing.
|
|
25
|
+
*/
|
|
26
|
+
export async function assessRenderCommand(file) {
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = await readFile(file, "utf-8");
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
33
|
+
console.error(`assess-render: cannot read ${file}\n ${reason}`);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let payload;
|
|
38
|
+
try {
|
|
39
|
+
payload = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
43
|
+
console.error(`assess-render: ${file} is not valid JSON\n ${reason}`);
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
console.log(render(parseAssessResult(payload)));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof AssessResultValidationError) {
|
|
52
|
+
console.error(`assess-render: ${file} — ${error.message}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
56
|
+
console.error(`assess-render: failed to render ${file}\n ${reason}`);
|
|
57
|
+
}
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -6,10 +6,11 @@ import { execSync } from "child_process";
|
|
|
6
6
|
import { ui, colors } from "../lib/cli-ui.js";
|
|
7
7
|
import { GitHubProvider } from "../lib/workflow/platforms/github.js";
|
|
8
8
|
import { fileExists, isExecutable } from "../lib/fs.js";
|
|
9
|
+
import { checkSkillsInstalled } from "../lib/skills-check.js";
|
|
9
10
|
import { getManifest } from "../lib/manifest.js";
|
|
10
11
|
import { commandExists, isGhAuthenticated, isNativeWindows, isWSL, checkOptionalMcpServers, getMcpServersConfig, OPTIONAL_MCP_SERVERS, } from "../lib/system.js";
|
|
11
12
|
import { getSettings, DEFAULT_AGENT_SETTINGS } from "../lib/settings.js";
|
|
12
|
-
import { checkVersionThorough, getVersionWarning, } from "../lib/version-check.js";
|
|
13
|
+
import { checkVersionThorough, getVersionWarning, resolveCliInvocation, } from "../lib/version-check.js";
|
|
13
14
|
import { areSkillsOutdated } from "./sync.js";
|
|
14
15
|
import { readAgentsMd, checkAgentsMdConsistency, AGENTS_MD_PATH, } from "../lib/agents-md.js";
|
|
15
16
|
import { readFile } from "../lib/fs.js";
|
|
@@ -146,9 +147,11 @@ export async function doctorCommand(options = {}) {
|
|
|
146
147
|
message: "Missing .sequant-manifest.json - run `sequant init`",
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
// Checks 2+3: Skills directory + core skills. Shared with the `run`
|
|
151
|
+
// pre-flight via checkSkillsInstalled (#813) so the layout cannot drift.
|
|
152
|
+
const coreSkills = ["spec", "exec", "qa"];
|
|
153
|
+
const { skillsDirExists, missingSkills } = await checkSkillsInstalled(coreSkills);
|
|
154
|
+
if (skillsDirExists) {
|
|
152
155
|
checks.push({
|
|
153
156
|
name: "Skills",
|
|
154
157
|
status: "pass",
|
|
@@ -162,14 +165,6 @@ export async function doctorCommand(options = {}) {
|
|
|
162
165
|
message: "Missing .claude/skills/ directory",
|
|
163
166
|
});
|
|
164
167
|
}
|
|
165
|
-
// Check 3: Core skills present
|
|
166
|
-
const coreSkills = ["spec", "exec", "qa"];
|
|
167
|
-
const missingSkills = [];
|
|
168
|
-
for (const skill of coreSkills) {
|
|
169
|
-
if (!(await fileExists(`.claude/skills/${skill}/SKILL.md`))) {
|
|
170
|
-
missingSkills.push(skill);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
168
|
if (missingSkills.length === 0) {
|
|
174
169
|
checks.push({
|
|
175
170
|
name: "Core Skills",
|
|
@@ -190,7 +185,7 @@ export async function doctorCommand(options = {}) {
|
|
|
190
185
|
checks.push({
|
|
191
186
|
name: "Skills Version",
|
|
192
187
|
status: "warn",
|
|
193
|
-
message: `Outdated (${skillsStatus.currentVersion || "unknown"} → ${skillsStatus.packageVersion}) - run:
|
|
188
|
+
message: `Outdated (${skillsStatus.currentVersion || "unknown"} → ${skillsStatus.packageVersion}) - run: ${resolveCliInvocation()} sync`,
|
|
194
189
|
});
|
|
195
190
|
}
|
|
196
191
|
else {
|
|
@@ -8,7 +8,7 @@ import { join, dirname } from "path";
|
|
|
8
8
|
import { readdir } from "fs/promises";
|
|
9
9
|
import { ui, colors } from "../lib/cli-ui.js";
|
|
10
10
|
import { detectStack, detectAllStacks, getStackConfig, detectPackageManager, getPackageManagerCommands, STACKS, } from "../lib/stacks.js";
|
|
11
|
-
import { copyTemplates } from "../lib/templates.js";
|
|
11
|
+
import { copyTemplates, assertTemplatesDirExists } from "../lib/templates.js";
|
|
12
12
|
import { createManifest } from "../lib/manifest.js";
|
|
13
13
|
import { saveConfig } from "../lib/config.js";
|
|
14
14
|
import { createDefaultSettings, generateSettingsReference, } from "../lib/settings.js";
|
|
@@ -73,6 +73,17 @@ function logDefault(label, value) {
|
|
|
73
73
|
console.log(chalk.blue(`${label}: ${value} (default)`));
|
|
74
74
|
}
|
|
75
75
|
export async function initCommand(options) {
|
|
76
|
+
// Fail loudly on a missing templates root before any prompt or write, so a
|
|
77
|
+
// broken install can never half-provision a project. Placed ahead of the
|
|
78
|
+
// --upgrade-skills branch, which reads the same directory (#822).
|
|
79
|
+
try {
|
|
80
|
+
await assertTemplatesDirExists();
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.log(chalk.red(`❌ ${error.message}`));
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
76
87
|
// Handle --upgrade-skills: update skill files from installed package templates
|
|
77
88
|
if (options.upgradeSkills) {
|
|
78
89
|
await upgradeSkills();
|
|
@@ -520,6 +531,7 @@ async function upgradeSkills() {
|
|
|
520
531
|
const installedDir = ".claude/skills";
|
|
521
532
|
if (!(await fileExists(installedDir))) {
|
|
522
533
|
console.log(chalk.red("No skills directory found. Run `sequant init` first."));
|
|
534
|
+
process.exitCode = 1;
|
|
523
535
|
return;
|
|
524
536
|
}
|
|
525
537
|
// Resolve the package's templates/skills directory
|
|
@@ -14,6 +14,8 @@ function formatSignalLine(issue, pid, result) {
|
|
|
14
14
|
return `Refused to signal PID ${pid} for #${issue} (matches this process or its parent)`;
|
|
15
15
|
case "pid-dead":
|
|
16
16
|
return `Could not signal PID ${pid} for #${issue} (already exited)`;
|
|
17
|
+
case "stale-pid-untrusted":
|
|
18
|
+
return `Refused to signal PID ${pid} for #${issue} (lock is past the age ceiling; that PID has likely been recycled onto an unrelated process)`;
|
|
17
19
|
case "kill-failed":
|
|
18
20
|
return `Could not signal PID ${pid} for #${issue} (kill syscall failed)`;
|
|
19
21
|
case "orchestrator":
|
|
@@ -81,18 +81,32 @@ function formatTime(isoString) {
|
|
|
81
81
|
function displayLogSummary(log, filename, options) {
|
|
82
82
|
const passed = log.summary.passed;
|
|
83
83
|
const failed = log.summary.failed;
|
|
84
|
+
// #766: `partial` gets its own bucket so an all-partial run isn't rendered as
|
|
85
|
+
// `0 passed, 0 failed` (older logs predate the field → default to 0).
|
|
86
|
+
const partial = log.summary.partial ?? 0;
|
|
84
87
|
const total = log.summary.totalIssues;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// #856: an aborted run is a failure, but saying only "FAILED" sends the
|
|
89
|
+
// reader hunting for a bug in their own code. The run never got to fail on
|
|
90
|
+
// its own terms — something killed it.
|
|
91
|
+
const aborted = log.summary.aborted ?? 0;
|
|
92
|
+
const status = aborted > 0
|
|
93
|
+
? chalk.red("ABORTED")
|
|
94
|
+
: failed > 0
|
|
95
|
+
? chalk.red("FAILED")
|
|
96
|
+
: passed === total
|
|
97
|
+
? chalk.green("PASSED")
|
|
98
|
+
: chalk.yellow("PARTIAL");
|
|
90
99
|
console.log(chalk.blue(`\n Run: ${log.runId.slice(0, 8)}...`));
|
|
91
100
|
console.log(chalk.gray(` File: ${filename}`));
|
|
92
101
|
console.log(chalk.gray(` Time: ${formatTime(log.startTime)}`));
|
|
93
102
|
console.log(chalk.gray(` Duration: ${formatDuration(log.summary.totalDurationSeconds)}`));
|
|
94
|
-
console.log(chalk.gray(` Status: ${status} (${passed}/${total} passed, ${failed} failed
|
|
103
|
+
console.log(chalk.gray(` Status: ${status} (${passed}/${total} passed, ${failed} failed` +
|
|
104
|
+
(partial > 0 ? `, ${partial} partial` : "") +
|
|
105
|
+
`)`));
|
|
95
106
|
console.log(chalk.gray(` Phases: ${log.config.phases.join(" → ")}`));
|
|
107
|
+
if (log.abortedBy) {
|
|
108
|
+
console.log(chalk.red(` Aborted: run terminated by ${log.abortedBy} — results below are incomplete`));
|
|
109
|
+
}
|
|
96
110
|
// Show issues
|
|
97
111
|
for (const issue of log.issues) {
|
|
98
112
|
const issueStatus = issue.status === "success"
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { spawnSync } from "child_process";
|
|
15
15
|
import { ui, colors } from "../lib/cli-ui.js";
|
|
16
|
-
import { runMergeChecks, formatReportMarkdown, } from "../lib/merge-check/index.js";
|
|
16
|
+
import { runMergeChecks, formatReportMarkdown, resolveBranches, findMostRecentLog, resolveLogDir, } from "../lib/merge-check/index.js";
|
|
17
17
|
import { LockManager, formatLockedMessage } from "../lib/locks/index.js";
|
|
18
|
+
import { waitForChecks, resolveWatchTiming } from "../lib/merge-check/watch.js";
|
|
19
|
+
import { GitHubProvider } from "../lib/workflow/platforms/github.js";
|
|
20
|
+
/** Exit code for a watch that timed out — distinct from the 0/1/2 verdicts. */
|
|
21
|
+
const EXIT_WATCH_TIMEOUT = 3;
|
|
18
22
|
/**
|
|
19
23
|
* Determine exit code from batch verdict
|
|
20
24
|
*/
|
|
@@ -43,6 +47,126 @@ function getRepoRoot() {
|
|
|
43
47
|
}
|
|
44
48
|
return result.stdout.trim();
|
|
45
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the PR numbers to watch for a set of issues.
|
|
52
|
+
*
|
|
53
|
+
* Reuses `resolveBranches` (git remote + worktree scan) for the issue→branch
|
|
54
|
+
* map, then prefers the run-log `prNumber` and falls back to
|
|
55
|
+
* `viewPRByBranchSync`. Issues with no discoverable PR are returned in
|
|
56
|
+
* `unresolved` so the caller can report them rather than silently skipping.
|
|
57
|
+
*/
|
|
58
|
+
function resolveWatchTargets(issueNumbers, repoRoot, gh) {
|
|
59
|
+
const runLog = findMostRecentLog(resolveLogDir());
|
|
60
|
+
const branches = resolveBranches(issueNumbers, repoRoot, runLog);
|
|
61
|
+
const targets = [];
|
|
62
|
+
const unresolved = [];
|
|
63
|
+
for (const b of branches) {
|
|
64
|
+
const pr = b.prNumber ?? gh.viewPRByBranchSync(b.branch)?.number ?? null;
|
|
65
|
+
if (pr) {
|
|
66
|
+
targets.push({ issue: b.issueNumber, pr });
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
unresolved.push(b.issueNumber);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { targets, unresolved };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Watch gate (#818): before the normal merge-check report runs, poll each
|
|
76
|
+
* resolved PR's CI rollup until every check is terminal.
|
|
77
|
+
*
|
|
78
|
+
* Contract: this **never merges**. It only decides *when* the existing report
|
|
79
|
+
* runs. Returns `true` when it has handled the outcome and the caller must stop
|
|
80
|
+
* (a dispatch block → BLOCKED/exit 2, a timeout → distinct message/exit 3, or no
|
|
81
|
+
* PR could be resolved); returns `false` when every PR reached a terminal state
|
|
82
|
+
* and the caller should fall through to `runMergeChecks` unchanged.
|
|
83
|
+
*
|
|
84
|
+
* Multiple PRs share a single wall-clock deadline so `--timeout` bounds the whole
|
|
85
|
+
* gate, not each PR independently.
|
|
86
|
+
*/
|
|
87
|
+
async function runWatchGate(issueNumbers, options, repoRoot) {
|
|
88
|
+
const gh = new GitHubProvider();
|
|
89
|
+
// Auto-detect issues from the most recent run log when none were given,
|
|
90
|
+
// mirroring runMergeChecks so `--watch` composes with auto-detection.
|
|
91
|
+
let issues = issueNumbers;
|
|
92
|
+
if (issues.length === 0) {
|
|
93
|
+
const runLog = findMostRecentLog(resolveLogDir());
|
|
94
|
+
if (runLog) {
|
|
95
|
+
issues = runLog.issues.map((i) => i.issueNumber);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const { targets, unresolved } = resolveWatchTargets(issues, repoRoot, gh);
|
|
99
|
+
if (targets.length === 0) {
|
|
100
|
+
const message = "Watch: no open PR found for " +
|
|
101
|
+
(issues.length > 0
|
|
102
|
+
? `issue(s) ${issues.map((i) => `#${i}`).join(", ")}`
|
|
103
|
+
: "the most recent run") +
|
|
104
|
+
". Push the branch and open a PR before running `merge --watch`.";
|
|
105
|
+
if (options.json) {
|
|
106
|
+
console.log(JSON.stringify({ watch: "blocked", error: message }, null, 2));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.error(ui.errorBox("Watch Blocked", message));
|
|
110
|
+
}
|
|
111
|
+
process.exitCode = 2;
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
// Validated, never NaN — the shared deadline below depends on it (a NaN
|
|
115
|
+
// deadline would make every per-PR `remaining` NaN and defeat --timeout).
|
|
116
|
+
const { intervalMs, timeoutMs } = resolveWatchTiming(options);
|
|
117
|
+
const deadline = Date.now() + timeoutMs;
|
|
118
|
+
if (unresolved.length > 0 && !options.json) {
|
|
119
|
+
console.log(colors.muted(` ! No PR resolved for: ${unresolved.map((i) => `#${i}`).join(", ")} — skipping in watch.`));
|
|
120
|
+
}
|
|
121
|
+
for (const { issue, pr } of targets) {
|
|
122
|
+
if (!options.json) {
|
|
123
|
+
console.log(colors.muted(`Watching PR #${pr} (issue #${issue})…`));
|
|
124
|
+
}
|
|
125
|
+
const remaining = deadline - Date.now();
|
|
126
|
+
const result = await waitForChecks(pr, {
|
|
127
|
+
intervalMs,
|
|
128
|
+
// Share the deadline across PRs so the whole gate honors --timeout.
|
|
129
|
+
timeoutMs: Math.max(0, remaining),
|
|
130
|
+
onPoll: options.json
|
|
131
|
+
? undefined
|
|
132
|
+
: (msg) => console.log(colors.muted(` #${pr}: ${msg}`)),
|
|
133
|
+
}, gh);
|
|
134
|
+
if (result.status === "blocked") {
|
|
135
|
+
const reason = result.reason ?? "CI dispatch is blocked.";
|
|
136
|
+
if (options.json) {
|
|
137
|
+
console.log(JSON.stringify({
|
|
138
|
+
watch: "blocked",
|
|
139
|
+
pr,
|
|
140
|
+
issue,
|
|
141
|
+
reason,
|
|
142
|
+
checkName: result.checkName,
|
|
143
|
+
}, null, 2));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.log(ui.errorBox("BLOCKED", `PR #${pr} (issue #${issue}) — ${reason}` +
|
|
147
|
+
(result.checkName ? `\n\nCheck: ${result.checkName}` : "")));
|
|
148
|
+
}
|
|
149
|
+
process.exitCode = 2;
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
if (result.status === "timeout") {
|
|
153
|
+
const reason = result.reason ?? "Watch timed out.";
|
|
154
|
+
if (options.json) {
|
|
155
|
+
console.log(JSON.stringify({ watch: "timeout", pr, issue, reason }, null, 2));
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.error(ui.errorBox("Watch Timed Out", reason));
|
|
159
|
+
}
|
|
160
|
+
process.exitCode = EXIT_WATCH_TIMEOUT;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
// terminal → this PR is ready to be checked; continue to the next PR.
|
|
164
|
+
if (!options.json) {
|
|
165
|
+
console.log(colors.muted(` #${pr}: all checks terminal after ${result.polls} poll(s).`));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
46
170
|
/**
|
|
47
171
|
* Main merge command handler
|
|
48
172
|
*/
|
|
@@ -51,7 +175,14 @@ export async function mergeCommand(issues, options) {
|
|
|
51
175
|
if (!options.check && !options.scan && !options.review && !options.all) {
|
|
52
176
|
options.check = true;
|
|
53
177
|
}
|
|
54
|
-
|
|
178
|
+
let repoRoot;
|
|
179
|
+
try {
|
|
180
|
+
repoRoot = getRepoRoot();
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
console.error(colors.error("❌ Not in a git repository. Run merge from inside your project repo."));
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
55
186
|
const issueNumbers = issues
|
|
56
187
|
.map((i) => parseInt(i, 10))
|
|
57
188
|
.filter((n) => !isNaN(n));
|
|
@@ -81,6 +212,27 @@ export async function mergeCommand(issues, options) {
|
|
|
81
212
|
}
|
|
82
213
|
console.log("");
|
|
83
214
|
}
|
|
215
|
+
// Watch gate (#818): wait for CI to reach a terminal state before running the
|
|
216
|
+
// report. Returns true when it has already handled the outcome (dispatch
|
|
217
|
+
// block, timeout, or no PR to watch) and set the exit code — stop here.
|
|
218
|
+
if (options.watch) {
|
|
219
|
+
try {
|
|
220
|
+
const handled = await runWatchGate(issueNumbers, options, repoRoot);
|
|
221
|
+
if (handled)
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
226
|
+
if (options.json) {
|
|
227
|
+
console.log(JSON.stringify({ watch: "error", error: message }, null, 2));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.error(ui.errorBox("Watch Failed", message));
|
|
231
|
+
}
|
|
232
|
+
process.exitCode = 2;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
84
236
|
try {
|
|
85
237
|
const report = await runMergeChecks(issueNumbers, options, repoRoot);
|
|
86
238
|
if (options.json) {
|
|
@@ -77,7 +77,12 @@ export class ReadySnapshotAdapter {
|
|
|
77
77
|
this.status = "failed";
|
|
78
78
|
break;
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
// #804: an auto-wait surfaces here the same way activity does — as a
|
|
81
|
+
// refreshed nowLine on the still-running phase. Sharing one body is
|
|
82
|
+
// deliberate: a wait must NOT reach the complete/failed case, which
|
|
83
|
+
// would close out a phase that is merely paused.
|
|
84
|
+
case "activity":
|
|
85
|
+
case "waiting": {
|
|
81
86
|
if (this.currentPhase) {
|
|
82
87
|
this.currentPhase = {
|
|
83
88
|
...this.currentPhase,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* human / no implementation), persists that state, and emits a structured gap
|
|
15
15
|
* report. See `src/lib/workflow/ready-gate.ts` for the engine.
|
|
16
16
|
*/
|
|
17
|
-
import { type ReadyPolicy } from "../lib/settings.js";
|
|
17
|
+
import { type ReadyPolicy, type SequantSettings } from "../lib/settings.js";
|
|
18
18
|
import { type ReadyResult } from "../lib/workflow/ready-gate.js";
|
|
19
19
|
export interface ReadyCommandOptions {
|
|
20
20
|
policy?: string;
|
|
@@ -29,8 +29,11 @@ export interface ReadyCommandOptions {
|
|
|
29
29
|
/**
|
|
30
30
|
* Exit code from a ready result.
|
|
31
31
|
* - 0: ready (awaiting human merge)
|
|
32
|
-
* - 1: not ready — needs human intervention (budget/iterations/stagnation
|
|
33
|
-
*
|
|
32
|
+
* - 1: not ready — needs human intervention (budget/iterations/stagnation,
|
|
33
|
+
* or a NO_VERDICT QA turn (#853): the implementation may exist, so this
|
|
34
|
+
* is a re-run case, not the "nothing there" exit)
|
|
35
|
+
* - 2: not ready — no implementation (#534), uncommitted-only work (#879), or
|
|
36
|
+
* hard error. Same class: nothing certifiable exists on the branch.
|
|
34
37
|
*/
|
|
35
38
|
export declare function getReadyExitCode(result: ReadyResult): number;
|
|
36
39
|
/**
|
|
@@ -40,6 +43,25 @@ export declare function getReadyExitCode(result: ReadyResult): number;
|
|
|
40
43
|
* @internal Exported for testing only.
|
|
41
44
|
*/
|
|
42
45
|
export declare function resolvePolicy(flag: string | undefined, settingsPolicy: ReadyPolicy): ReadyPolicy;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the numeric limits for a ready-gate run: CLI → settings → default.
|
|
48
|
+
*
|
|
49
|
+
* #833: these previously guarded only the CLI value (`typeof x === "number" &&
|
|
50
|
+
* x > 0`) and fell straight through to `settings.run.*` unchecked. But
|
|
51
|
+
* `settings.run.timeout` is user-authored JSON, and `ready`'s value does NOT
|
|
52
|
+
* pass through `buildExecutionConfig` — `ready-gate.ts`'s `buildPhaseConfig`
|
|
53
|
+
* assembles its own `ExecutionConfig`. So a `"timeout": 0` in settings.json
|
|
54
|
+
* reached `setTimeout` as a 0 ms delay and aborted every ready-gate phase on
|
|
55
|
+
* its first tick, with no warning: the #833 defect, on the path the original
|
|
56
|
+
* fix did not cover. Chaining `positiveOr` leaves no layer unchecked.
|
|
57
|
+
*
|
|
58
|
+
* @internal Exported for testing only.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveReadyLimits(options: Pick<ReadyCommandOptions, "maxIterations" | "budget" | "timeout">, settings: Pick<SequantSettings, "run">): {
|
|
61
|
+
maxIterations: number;
|
|
62
|
+
tokenBudget: number | undefined;
|
|
63
|
+
phaseTimeout: number;
|
|
64
|
+
};
|
|
43
65
|
/**
|
|
44
66
|
* Locate the worktree path for an issue from `git worktree list`.
|
|
45
67
|
*
|
|
@@ -15,25 +15,32 @@
|
|
|
15
15
|
* report. See `src/lib/workflow/ready-gate.ts` for the engine.
|
|
16
16
|
*/
|
|
17
17
|
import { ui, colors } from "../lib/cli-ui.js";
|
|
18
|
-
import { getSettings } from "../lib/settings.js";
|
|
18
|
+
import { getSettings, } from "../lib/settings.js";
|
|
19
19
|
import { listWorktrees } from "../lib/workflow/worktree-manager.js";
|
|
20
20
|
import { GitHubProvider } from "../lib/workflow/platforms/github.js";
|
|
21
21
|
import { getStateManager } from "../lib/workflow/state-manager.js";
|
|
22
22
|
import { executePhaseWithRetry } from "../lib/workflow/phase-executor.js";
|
|
23
23
|
import { buildProgressWiring } from "./run-progress.js";
|
|
24
24
|
import { ReadySnapshotAdapter } from "./ready-tui-adapter.js";
|
|
25
|
+
import { DEFAULT_CONFIG } from "../lib/workflow/types.js";
|
|
26
|
+
import { positiveOr } from "../lib/workflow/config-resolver.js";
|
|
25
27
|
import { runReadyGate, parseNonGoals, } from "../lib/workflow/ready-gate.js";
|
|
26
28
|
/**
|
|
27
29
|
* Exit code from a ready result.
|
|
28
30
|
* - 0: ready (awaiting human merge)
|
|
29
|
-
* - 1: not ready — needs human intervention (budget/iterations/stagnation
|
|
30
|
-
*
|
|
31
|
+
* - 1: not ready — needs human intervention (budget/iterations/stagnation,
|
|
32
|
+
* or a NO_VERDICT QA turn (#853): the implementation may exist, so this
|
|
33
|
+
* is a re-run case, not the "nothing there" exit)
|
|
34
|
+
* - 2: not ready — no implementation (#534), uncommitted-only work (#879), or
|
|
35
|
+
* hard error. Same class: nothing certifiable exists on the branch.
|
|
31
36
|
*/
|
|
32
37
|
export function getReadyExitCode(result) {
|
|
33
38
|
if (result.ready)
|
|
34
39
|
return 0;
|
|
35
|
-
if (result.reason === "NO_IMPLEMENTATION"
|
|
40
|
+
if (result.reason === "NO_IMPLEMENTATION" ||
|
|
41
|
+
result.reason === "UNCOMMITTED_ONLY") {
|
|
36
42
|
return 2;
|
|
43
|
+
}
|
|
37
44
|
return 1;
|
|
38
45
|
}
|
|
39
46
|
/**
|
|
@@ -47,6 +54,33 @@ export function resolvePolicy(flag, settingsPolicy) {
|
|
|
47
54
|
return flag;
|
|
48
55
|
return settingsPolicy;
|
|
49
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the numeric limits for a ready-gate run: CLI → settings → default.
|
|
59
|
+
*
|
|
60
|
+
* #833: these previously guarded only the CLI value (`typeof x === "number" &&
|
|
61
|
+
* x > 0`) and fell straight through to `settings.run.*` unchecked. But
|
|
62
|
+
* `settings.run.timeout` is user-authored JSON, and `ready`'s value does NOT
|
|
63
|
+
* pass through `buildExecutionConfig` — `ready-gate.ts`'s `buildPhaseConfig`
|
|
64
|
+
* assembles its own `ExecutionConfig`. So a `"timeout": 0` in settings.json
|
|
65
|
+
* reached `setTimeout` as a 0 ms delay and aborted every ready-gate phase on
|
|
66
|
+
* its first tick, with no warning: the #833 defect, on the path the original
|
|
67
|
+
* fix did not cover. Chaining `positiveOr` leaves no layer unchecked.
|
|
68
|
+
*
|
|
69
|
+
* @internal Exported for testing only.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveReadyLimits(options, settings) {
|
|
72
|
+
return {
|
|
73
|
+
maxIterations: positiveOr(options.maxIterations, positiveOr(settings.run.maxIterations, DEFAULT_CONFIG.maxIterations)),
|
|
74
|
+
// Budget is genuinely optional — `undefined` means "no budget", not "use a
|
|
75
|
+
// default" — so it keeps the two-state form rather than chaining.
|
|
76
|
+
tokenBudget: typeof options.budget === "number" &&
|
|
77
|
+
Number.isFinite(options.budget) &&
|
|
78
|
+
options.budget > 0
|
|
79
|
+
? options.budget
|
|
80
|
+
: undefined,
|
|
81
|
+
phaseTimeout: positiveOr(options.timeout, positiveOr(settings.run.timeout, DEFAULT_CONFIG.phaseTimeout)),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
50
84
|
/**
|
|
51
85
|
* Locate the worktree path for an issue from `git worktree list`.
|
|
52
86
|
*
|
|
@@ -70,15 +104,7 @@ export async function readyCommand(issueArg, options) {
|
|
|
70
104
|
}
|
|
71
105
|
const settings = await getSettings();
|
|
72
106
|
const policy = resolvePolicy(options.policy, settings.ready.policy);
|
|
73
|
-
const maxIterations
|
|
74
|
-
? options.maxIterations
|
|
75
|
-
: settings.run.maxIterations;
|
|
76
|
-
const tokenBudget = typeof options.budget === "number" && options.budget > 0
|
|
77
|
-
? options.budget
|
|
78
|
-
: undefined;
|
|
79
|
-
const phaseTimeout = typeof options.timeout === "number" && options.timeout > 0
|
|
80
|
-
? options.timeout
|
|
81
|
-
: settings.run.timeout;
|
|
107
|
+
const { maxIterations, tokenBudget, phaseTimeout } = resolveReadyLimits(options, settings);
|
|
82
108
|
const mcp = options.mcp !== false;
|
|
83
109
|
// Resolve the issue's existing worktree (reuses run/state worktree infra).
|
|
84
110
|
const worktreePath = resolveWorktreePath(issueNumber);
|