sequant 2.9.0 → 2.11.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 +20 -5
- package/dist/bin/cli.js +143 -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 +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.d.ts +20 -1
- package/dist/src/commands/locks.js +208 -4
- 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 +31 -3
- package/dist/src/commands/ready.js +53 -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 +38 -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/commands/worktree.d.ts +31 -0
- package/dist/src/commands/worktree.js +95 -0
- 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 +56 -0
- package/dist/src/lib/cli-flags.js +89 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +23 -7
- package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
- package/dist/src/lib/cli-ui/run-renderer.js +69 -1
- package/dist/src/lib/errors.d.ts +78 -6
- package/dist/src/lib/errors.js +133 -15
- package/dist/src/lib/locks/checkout-lock.d.ts +193 -0
- package/dist/src/lib/locks/checkout-lock.js +389 -0
- package/dist/src/lib/locks/index.d.ts +7 -4
- package/dist/src/lib/locks/index.js +5 -3
- package/dist/src/lib/locks/lock-manager.d.ts +101 -3
- package/dist/src/lib/locks/lock-manager.js +277 -8
- package/dist/src/lib/locks/types.d.ts +106 -3
- package/dist/src/lib/locks/types.js +44 -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 +87 -0
- package/dist/src/lib/settings.js +106 -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 +10 -2
- package/dist/src/lib/test-tautology-detector.js +213 -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 +552 -60
- 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 +51 -0
- package/dist/src/lib/workflow/config-resolver.js +143 -2
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +22 -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 +25 -2
- package/dist/src/lib/workflow/effort-escalation.d.ts +73 -0
- package/dist/src/lib/workflow/effort-escalation.js +82 -0
- package/dist/src/lib/workflow/error-classifier.d.ts +4 -1
- package/dist/src/lib/workflow/error-classifier.js +4 -0
- 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 +54 -2
- package/dist/src/lib/workflow/log-writer.js +95 -6
- package/dist/src/lib/workflow/metrics-schema.d.ts +52 -9
- package/dist/src/lib/workflow/metrics-schema.js +33 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +11 -0
- package/dist/src/lib/workflow/phase-detection.d.ts +12 -0
- package/dist/src/lib/workflow/phase-detection.js +5 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +203 -14
- package/dist/src/lib/workflow/phase-executor.js +467 -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 +45 -4
- package/dist/src/lib/workflow/ready-gate.js +49 -12
- package/dist/src/lib/workflow/run-log-schema.d.ts +69 -0
- package/dist/src/lib/workflow/run-log-schema.js +78 -2
- package/dist/src/lib/workflow/run-orchestrator.d.ts +19 -0
- package/dist/src/lib/workflow/run-orchestrator.js +168 -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/spec-recommendation.d.ts +71 -0
- package/dist/src/lib/workflow/spec-recommendation.js +142 -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 +190 -4
- package/dist/src/lib/workflow/types.js +1 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +36 -3
- package/dist/src/lib/workflow/worktree-manager.js +111 -24
- package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
- package/dist/src/lib/workflow/worktree-resolver.js +126 -0
- 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 +10 -8
- package/templates/hooks/parallel-marker.sh +50 -0
- package/templates/hooks/post-tool.sh +20 -7
- package/templates/hooks/pre-tool.sh +303 -10
- package/templates/scripts/cleanup-worktree.sh +217 -33
- package/templates/scripts/new-feature.sh +289 -35
- package/templates/skills/_shared/references/subagent-types.md +7 -18
- package/templates/skills/_shared/references/trust-model.md +18 -0
- package/templates/skills/assess/SKILL.md +220 -391
- 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 +87 -13
- package/templates/skills/fullsolve/SKILL.md +130 -22
- package/templates/skills/loop/SKILL.md +94 -13
- package/templates/skills/merger/SKILL.md +100 -12
- package/templates/skills/qa/SKILL.md +396 -63
- 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 +80 -1
- package/templates/skills/setup/SKILL.md +5 -5
- package/templates/skills/spec/SKILL.md +33 -15
- package/templates/skills/spec/references/recommended-workflow.md +14 -1
- package/templates/skills/test/SKILL.md +1 -1
- package/templates/skills/testgen/SKILL.md +23 -6
- 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
- package/templates/agents/sequant-explorer.md +0 -24
|
@@ -9,6 +9,7 @@ import type { ShutdownManager } from "../shutdown.js";
|
|
|
9
9
|
import type { WorktreeInfo } from "./worktree-manager.js";
|
|
10
10
|
import type { SequantError } from "../errors.js";
|
|
11
11
|
import type { ErrorCategory } from "./error-classifier.js";
|
|
12
|
+
import type { ReadyResult } from "./ready-gate.js";
|
|
12
13
|
export type { WorkflowEventEmitter, WorkflowEvents, WorkflowEventListener, IssueEventStatus, BaseEventPayload, RunEventPayload, PhaseStartedPayload, PhaseCompletedPayload, PhaseFailedPayload, IssueStatusChangedPayload, QaVerdictPayload, ProgressPayload, } from "./event-emitter.js";
|
|
13
14
|
/**
|
|
14
15
|
* Canonical Zod schema for all workflow phases.
|
|
@@ -131,6 +132,14 @@ export interface ExecutionConfig {
|
|
|
131
132
|
* poll budget is preserved.
|
|
132
133
|
*/
|
|
133
134
|
onActivity?: (text: string) => void;
|
|
135
|
+
/**
|
|
136
|
+
* Runtime callback invoked while an auto-wait (#804) is in progress, once
|
|
137
|
+
* per tick plus once when it ends. Used to keep the renderer and the `-q`
|
|
138
|
+
* heartbeat showing the wait and its wake time, so a multi-hour pause reads
|
|
139
|
+
* as deliberate rather than as the silent stall of #574. Not serialized —
|
|
140
|
+
* set per-call by the orchestrator, like {@link onActivity}.
|
|
141
|
+
*/
|
|
142
|
+
onAutoWait?: (notice: AutoWaitNotice) => void;
|
|
134
143
|
/**
|
|
135
144
|
* Enable interactive relay (#383). When true, phase-executor sets
|
|
136
145
|
* `SEQUANT_RELAY=true` in the agent environment so the PostToolUse hook
|
|
@@ -147,6 +156,75 @@ export interface ExecutionConfig {
|
|
|
147
156
|
* QA pass does NOT skip the checks that catch the #318/#529/#570 class.
|
|
148
157
|
*/
|
|
149
158
|
fullQa?: boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Run the post-QA ready gate after the standard phases succeed (#817).
|
|
161
|
+
*
|
|
162
|
+
* When true, `runIssueWithLogging` invokes the existing `sequant ready`
|
|
163
|
+
* engine (`runReadyGate`) at the PR seam — before rebase/PR so the gate's
|
|
164
|
+
* auto-fix commits land in the PR — driving the issue to the configured
|
|
165
|
+
* `settings.ready.policy` threshold. It NEVER merges: the run terminates with
|
|
166
|
+
* the issue in `waiting_for_human_merge` (ready) or `blocked` (guard halt).
|
|
167
|
+
* Default off; when unset the run path is byte-identical to pre-#817.
|
|
168
|
+
*/
|
|
169
|
+
readyGate?: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Total wait budget, in minutes, for auto-waiting out an exhausted
|
|
172
|
+
* rate-limit window (#804). `0` (the default) disables auto-wait entirely,
|
|
173
|
+
* preserving the #761/#799 halt behavior byte-for-byte.
|
|
174
|
+
*
|
|
175
|
+
* When a phase fails with a window-exhausted `RateLimitError` whose reset
|
|
176
|
+
* lies within the remaining budget, the executor sleeps until the reset and
|
|
177
|
+
* retries the phase instead of returning the failure. Named for the
|
|
178
|
+
* *behavior* (waiting), not the mechanism, so it survives a future move from
|
|
179
|
+
* in-process sleep to out-of-process rescheduling.
|
|
180
|
+
*
|
|
181
|
+
* This is a TOTAL budget across the issue, not a per-occurrence allowance —
|
|
182
|
+
* see {@link AutoWaitLedger}.
|
|
183
|
+
*/
|
|
184
|
+
autoWaitMinutes?: number;
|
|
185
|
+
/**
|
|
186
|
+
* Resolved per-phase `model`/`effort` overrides (#914), keyed by phase
|
|
187
|
+
* name. Merged from `settings.run.phases` and the CLI's `--models`/
|
|
188
|
+
* `--efforts` flags via `resolvePhasePolicies` (CLI > settings > absent) —
|
|
189
|
+
* see `config-resolver.ts`. Absent/empty by default: `phase-executor.ts`
|
|
190
|
+
* only sets `AgentExecutionConfig.model`/`.effort` when a phase has an
|
|
191
|
+
* entry here, so an unconfigured run reaches the SDK unchanged.
|
|
192
|
+
*/
|
|
193
|
+
phasePolicies?: Record<string, {
|
|
194
|
+
model?: string;
|
|
195
|
+
effort?: string;
|
|
196
|
+
}>;
|
|
197
|
+
/**
|
|
198
|
+
* Evidence-based effort escalation on quality-loop retries (#915). CLI >
|
|
199
|
+
* settings > absent (`false`), resolved by `buildExecutionConfig`
|
|
200
|
+
* (`config-resolver.ts`) and `ready-gate.ts`'s `buildPhaseConfig` — the same
|
|
201
|
+
* two producers `phasePolicies` uses, so this cannot drift from that one
|
|
202
|
+
* (#833 class). Consumed only at dispatch time by
|
|
203
|
+
* `effort-escalation.ts`'s `withEscalatedEffort`, never baked statically
|
|
204
|
+
* into `phasePolicies` here — escalation is per-execution, not per-run.
|
|
205
|
+
*/
|
|
206
|
+
effortEscalation?: boolean;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* A single liveness notice emitted during an auto-wait (#804 AC-7).
|
|
210
|
+
*/
|
|
211
|
+
export interface AutoWaitNotice {
|
|
212
|
+
/** Issue the wait belongs to. */
|
|
213
|
+
issueNumber: number;
|
|
214
|
+
/** Phase whose retry is being waited for. */
|
|
215
|
+
phase: string;
|
|
216
|
+
/** Epoch ms at which the wait ends (reset + buffer). */
|
|
217
|
+
wakeAtMs: number;
|
|
218
|
+
/** Ms remaining at the time of this notice. `0` on the final notice. */
|
|
219
|
+
remainingMs: number;
|
|
220
|
+
/**
|
|
221
|
+
* Display string, already formatted. Built from `formatRateLimitMessage`
|
|
222
|
+
* plus the wake time — never by re-appending `resetsAt` to an
|
|
223
|
+
* already-formatted driver message (the #799 doubled-string trap).
|
|
224
|
+
*/
|
|
225
|
+
message: string;
|
|
226
|
+
/** True on the terminal notice, once the wait has ended or been aborted. */
|
|
227
|
+
done: boolean;
|
|
150
228
|
}
|
|
151
229
|
/**
|
|
152
230
|
* Default execution configuration
|
|
@@ -190,6 +268,16 @@ export interface PhaseResult {
|
|
|
190
268
|
stdoutTail?: string[];
|
|
191
269
|
/** Process exit code from the agent driver (#447) */
|
|
192
270
|
exitCode?: number;
|
|
271
|
+
/**
|
|
272
|
+
* Set when this execution's effort was escalated one tier above its
|
|
273
|
+
* resolved base (#915) — a quality-loop retry with `effortEscalation`
|
|
274
|
+
* enabled. Additive/optional, same shape as `capped?`/`structuredError?`;
|
|
275
|
+
* absent on every non-escalated execution.
|
|
276
|
+
*/
|
|
277
|
+
escalatedEffort?: {
|
|
278
|
+
base: string;
|
|
279
|
+
escalated: string;
|
|
280
|
+
};
|
|
193
281
|
}
|
|
194
282
|
/**
|
|
195
283
|
* Result of executing all phases for an issue
|
|
@@ -205,6 +293,23 @@ export interface IssueResult {
|
|
|
205
293
|
prNumber?: number;
|
|
206
294
|
/** PR URL if created after successful QA */
|
|
207
295
|
prUrl?: string;
|
|
296
|
+
/**
|
|
297
|
+
* Set when PR creation was attempted after passing QA but failed (#879).
|
|
298
|
+
* A failed `createPR` used to be a non-fatal yellow warning while the issue
|
|
299
|
+
* still reported success — so a run could "pass" while producing no PR. When
|
|
300
|
+
* present, `success` is false and the run summary counts the issue as failed
|
|
301
|
+
* with this string as the reason.
|
|
302
|
+
*/
|
|
303
|
+
prCreationError?: string;
|
|
304
|
+
/**
|
|
305
|
+
* Set when PR creation was skipped because the branch carried zero commits
|
|
306
|
+
* ahead of its base (#920) — a phase-restricted run (e.g. `--phases spec`)
|
|
307
|
+
* that implemented nothing has no deliverable to open a PR for, and
|
|
308
|
+
* attempting one would only fail with GitHub's "No commits between main and
|
|
309
|
+
* …" error. Distinct from {@link IssueResult.prCreationError}: this is not
|
|
310
|
+
* a failure — `success` stays whatever the phases produced.
|
|
311
|
+
*/
|
|
312
|
+
prSkippedReason?: string;
|
|
208
313
|
/**
|
|
209
314
|
* Set when the issue was skipped because another sequant session holds
|
|
210
315
|
* the per-issue lock (#625). Surfaced in the summary as
|
|
@@ -232,6 +337,25 @@ export interface IssueResult {
|
|
|
232
337
|
* privacy contract). Absent on success.
|
|
233
338
|
*/
|
|
234
339
|
failureCategory?: ErrorCategory;
|
|
340
|
+
/**
|
|
341
|
+
* Outcome of the post-QA ready gate (#817), present only when the run was
|
|
342
|
+
* invoked with `--ready-gate` and the gate actually ran (i.e. the standard
|
|
343
|
+
* phases succeeded). Carries the terminal reason, the persisted issue status
|
|
344
|
+
* (`waiting_for_human_merge` / `blocked` — never merged), and the rendered
|
|
345
|
+
* report the summary and PR body surface. Absent on runs without the flag.
|
|
346
|
+
*/
|
|
347
|
+
readyGate?: ReadyResult;
|
|
348
|
+
/**
|
|
349
|
+
* Why the ready gate did NOT run, when `--ready-gate` was requested but the
|
|
350
|
+
* gate threw (#817). Mutually exclusive with {@link IssueResult.readyGate}.
|
|
351
|
+
*
|
|
352
|
+
* A gate crash is deliberately non-fatal — the phase work is already
|
|
353
|
+
* committed and the PR still opens — but it must not be silent. Without this
|
|
354
|
+
* field a run whose gate died is byte-identical in the summary to one that
|
|
355
|
+
* never asked for a gate, so the user believes a second look happened when it
|
|
356
|
+
* did not. The summary renders this as an explicit "gate did not run" line.
|
|
357
|
+
*/
|
|
358
|
+
readyGateError?: string;
|
|
235
359
|
}
|
|
236
360
|
/**
|
|
237
361
|
* CLI options for the run command, merged with settings and env config.
|
|
@@ -263,8 +387,6 @@ export interface RunOptions {
|
|
|
263
387
|
autoDetectPhases?: boolean;
|
|
264
388
|
/** Enable automatic worktree creation for issue isolation */
|
|
265
389
|
worktreeIsolation?: boolean;
|
|
266
|
-
/** Reuse existing worktrees instead of creating new ones */
|
|
267
|
-
reuseWorktrees?: boolean;
|
|
268
390
|
/** Suppress version warnings and non-essential output */
|
|
269
391
|
quiet?: boolean;
|
|
270
392
|
/** Chain issues: each branches from previous (requires --sequential) */
|
|
@@ -299,6 +421,13 @@ export interface RunOptions {
|
|
|
299
421
|
* Resolution priority: this CLI flag → settings.run.mcp → default (true)
|
|
300
422
|
*/
|
|
301
423
|
noMcp?: boolean;
|
|
424
|
+
/**
|
|
425
|
+
* Total minutes willing to wait for an exhausted rate-limit window to reopen
|
|
426
|
+
* before halting (#804). `0` (default) keeps today's immediate halt.
|
|
427
|
+
* Resolution priority: this CLI flag → SEQUANT_AUTO_WAIT_MINUTES →
|
|
428
|
+
* settings.run.autoWaitMinutes → default (0).
|
|
429
|
+
*/
|
|
430
|
+
autoWaitMinutes?: number;
|
|
302
431
|
/**
|
|
303
432
|
* Resume from last completed phase.
|
|
304
433
|
* Reads phase markers from GitHub issue comments and skips completed phases.
|
|
@@ -324,7 +453,9 @@ export interface RunOptions {
|
|
|
324
453
|
noPr?: boolean;
|
|
325
454
|
/**
|
|
326
455
|
* Force re-execution of issues even if they have completed status.
|
|
327
|
-
* Bypasses the pre-flight state guard that skips
|
|
456
|
+
* Bypasses the pre-flight state guard that skips completed issues — see
|
|
457
|
+
* `isCompletedIssueStatus` (completed-status.ts) for exactly which statuses
|
|
458
|
+
* that covers, and why `blocked` is not one of them.
|
|
328
459
|
*/
|
|
329
460
|
force?: boolean;
|
|
330
461
|
/**
|
|
@@ -361,6 +492,17 @@ export interface RunOptions {
|
|
|
361
492
|
* #705: now a hidden no-op alias — the boxed Ink TUI is the default, so
|
|
362
493
|
* `--experimental-tui` only parses for backward compatibility and no longer
|
|
363
494
|
* gates rendering. Kept so existing scripts/muscle-memory don't break.
|
|
495
|
+
*
|
|
496
|
+
* INTENTIONALLY INERT — do not delete (#810). A dead-surface sweep of
|
|
497
|
+
* `RunOptions` will correctly observe that nothing branches on this field.
|
|
498
|
+
* That is the design, not a defect: the flag's whole job is to parse and do
|
|
499
|
+
* nothing, so scripts written against #705 keep working. Its inertness is
|
|
500
|
+
* asserted, not incidental — `run-flags.test.ts` ("--experimental-tui is a
|
|
501
|
+
* no-op"), `cli.integration.test.ts` (hidden from `--help`, still parses),
|
|
502
|
+
* and `run-tui.integration.test.ts` all pin it. Contrast `reuseWorktrees`,
|
|
503
|
+
* removed in #810: that field had no flag, no consumer, and no test, so it
|
|
504
|
+
* promised behavior nothing delivered. The distinction is a flag deliberately
|
|
505
|
+
* kept parseable versus a type field nobody could ever reach.
|
|
364
506
|
*/
|
|
365
507
|
experimentalTui?: boolean;
|
|
366
508
|
/**
|
|
@@ -375,6 +517,39 @@ export interface RunOptions {
|
|
|
375
517
|
* Resolution priority: this CLI flag → settings.run.relay → default (true).
|
|
376
518
|
*/
|
|
377
519
|
relay?: boolean;
|
|
520
|
+
/**
|
|
521
|
+
* Run the post-QA ready gate after the standard phases succeed (#817). Set via
|
|
522
|
+
* `--ready-gate`. When true, the run drives the issue to the configured
|
|
523
|
+
* `settings.ready.policy` threshold through the existing `sequant ready`
|
|
524
|
+
* engine before creating the PR, then STOPS at the human merge gate — it never
|
|
525
|
+
* merges. Off by default; opt-in only, so an unset flag leaves the run path
|
|
526
|
+
* (including the #749 break-to-PR behavior) unchanged. Reuses `ready`'s policy
|
|
527
|
+
* and bounds wholesale — no new settings accompany this flag.
|
|
528
|
+
*/
|
|
529
|
+
readyGate?: boolean;
|
|
530
|
+
/**
|
|
531
|
+
* Per-phase model override (#914). Either a bare value applied to every
|
|
532
|
+
* phase (`--models sonnet`) or a comma list of `phase=model` pairs
|
|
533
|
+
* (`--models spec=fable,exec=sonnet`). Parsed and merged with
|
|
534
|
+
* `settings.run.phases` by `resolvePhasePolicies` (CLI > settings >
|
|
535
|
+
* absent) into `ExecutionConfig.phasePolicies`. Malformed specs fail fast
|
|
536
|
+
* at the Commander option boundary via `cli-flags.ts`.
|
|
537
|
+
*/
|
|
538
|
+
models?: string;
|
|
539
|
+
/**
|
|
540
|
+
* Per-phase reasoning-effort override (#914). Same grammar as {@link
|
|
541
|
+
* models} (bare value or comma list of `phase=effort` pairs); each value
|
|
542
|
+
* validates against the SDK's closed `low|medium|high|xhigh|max` enum.
|
|
543
|
+
*/
|
|
544
|
+
efforts?: string;
|
|
545
|
+
/**
|
|
546
|
+
* Evidence-based effort escalation on quality-loop retries (#915). Set via
|
|
547
|
+
* `--escalate-effort`. When true, a retried phase execution resolves one
|
|
548
|
+
* effort tier above its configured/inherited base for that execution only
|
|
549
|
+
* — see `effort-escalation.ts`. Default `false`: escalation raises token
|
|
550
|
+
* spend, so an unset flag leaves every run byte-identical to pre-#915.
|
|
551
|
+
*/
|
|
552
|
+
escalateEffort?: boolean;
|
|
378
553
|
}
|
|
379
554
|
/**
|
|
380
555
|
* CLI arguments for run command
|
|
@@ -415,12 +590,23 @@ export interface BatchResult {
|
|
|
415
590
|
* `"activity"` (#543): sub-phase activity ping. `extra.text` carries a short
|
|
416
591
|
* one-line snippet (e.g. last line of agent output) for the dashboard's
|
|
417
592
|
* `nowLine`. Fires at most ~10 Hz from the phase executor.
|
|
593
|
+
*
|
|
594
|
+
* `"waiting"` (#804): an auto-wait for a rate-limit window is in progress.
|
|
595
|
+
* `extra.text` is the display message and `extra.wakeAtMs` the wake time;
|
|
596
|
+
* a `"waiting"` event WITHOUT `wakeAtMs` is the terminal notice that clears
|
|
597
|
+
* the waiting state. Unlike `"activity"`, this must reach both display paths
|
|
598
|
+
* — a multi-hour pause with no signal is the #574 complaint at 60x scale.
|
|
418
599
|
*/
|
|
419
|
-
export type ProgressCallback = (issue: number, phase: string, event: "start" | "complete" | "failed" | "activity", extra?: {
|
|
600
|
+
export type ProgressCallback = (issue: number, phase: string, event: "start" | "complete" | "failed" | "activity" | "waiting", extra?: {
|
|
420
601
|
durationSeconds?: number;
|
|
421
602
|
error?: string;
|
|
422
603
|
iteration?: number;
|
|
423
604
|
text?: string;
|
|
605
|
+
/**
|
|
606
|
+
* `"waiting"` (#804): epoch ms at which an auto-wait ends. Absent on the
|
|
607
|
+
* terminal notice, which clears the waiting state.
|
|
608
|
+
*/
|
|
609
|
+
wakeAtMs?: number;
|
|
424
610
|
}) => void;
|
|
425
611
|
/**
|
|
426
612
|
* #672 AC-2: fired once per issue after the executor has resolved the final
|
|
@@ -104,8 +104,15 @@ export declare function checkWorktreeFreshness(worktreePath: string, verbose: bo
|
|
|
104
104
|
export declare function removeStaleWorktree(existingPath: string, branch: string, verbose: boolean): boolean;
|
|
105
105
|
/**
|
|
106
106
|
* List all active worktrees with their branches
|
|
107
|
+
*
|
|
108
|
+
* `git worktree list` only ever reports worktrees belonging to the repository
|
|
109
|
+
* containing `cwd`, which is what makes this a repo-scoped lookup: a sibling
|
|
110
|
+
* project's worktree can never appear here, even though `../worktrees/` is a
|
|
111
|
+
* single directory shared by every repo under the same parent (#899).
|
|
112
|
+
*
|
|
113
|
+
* @param cwd - Directory to run git in. Defaults to the current process cwd.
|
|
107
114
|
*/
|
|
108
|
-
export declare function listWorktrees(): Array<{
|
|
115
|
+
export declare function listWorktrees(cwd?: string): Array<{
|
|
109
116
|
path: string;
|
|
110
117
|
branch: string;
|
|
111
118
|
issue: number | null;
|
|
@@ -145,6 +152,21 @@ export declare function filterResumedPhases(issueNumber: number, phases: Phase[]
|
|
|
145
152
|
phases: Phase[];
|
|
146
153
|
skipped: Phase[];
|
|
147
154
|
};
|
|
155
|
+
/**
|
|
156
|
+
* Run the frozen dependency install for a freshly provisioned worktree and
|
|
157
|
+
* surface (rather than swallow) a failed install.
|
|
158
|
+
*
|
|
159
|
+
* Mirrors the status check in `reinstallIfLockfileChanged` (#846): a plain
|
|
160
|
+
* `npm install` self-heals a stale/absent lockfile, but the frozen `npm ci`
|
|
161
|
+
* hard-fails (exit 1, no node_modules). Without this check a failing install
|
|
162
|
+
* left a silently dependency-less worktree whose breakage only surfaced later
|
|
163
|
+
* as a confusing phase error. The warning names the resolved command so the
|
|
164
|
+
* user can rerun it by hand.
|
|
165
|
+
*
|
|
166
|
+
* @returns true if the install succeeded, false if it failed (warn-and-continue)
|
|
167
|
+
* @internal Exported for testing
|
|
168
|
+
*/
|
|
169
|
+
export declare function installWorktreeDeps(worktreePath: string, packageManager: string | undefined, verbose: boolean): boolean;
|
|
148
170
|
/**
|
|
149
171
|
* Create or reuse a worktree for an issue
|
|
150
172
|
* @param baseBranch - Optional branch to use as base instead of origin/main (for chain mode)
|
|
@@ -225,17 +247,25 @@ export declare function rebaseBeforePR(worktreePath: string, issueNumber: number
|
|
|
225
247
|
* @param opts.qaVerdict QA verdict for the run; a non-A+ stopping state
|
|
226
248
|
* (anything other than `READY_FOR_MERGE`) surfaces a note so a human reviewer
|
|
227
249
|
* sees why the run broke to PR without reaching A+ (#749).
|
|
250
|
+
* @param opts.readyGateReport Rendered `sequant ready` gap report (#817). Set
|
|
251
|
+
* only when the run used `--ready-gate`; surfaces the gate outcome (threshold
|
|
252
|
+
* reached vs guard halt) in the PR body the same way `sequant ready` does.
|
|
228
253
|
* @internal Exported for testing
|
|
229
254
|
*/
|
|
230
255
|
export declare function buildAutomatedPRBody(issueNumber: number, opts?: {
|
|
231
256
|
stackManifest?: string;
|
|
232
257
|
qaVerdict?: string;
|
|
258
|
+
readyGateReport?: string;
|
|
233
259
|
}): string;
|
|
234
260
|
/**
|
|
235
261
|
* Push branch and create a PR after successful QA.
|
|
236
262
|
*
|
|
237
263
|
* Handles both fresh PR creation and detection of existing PRs.
|
|
238
|
-
*
|
|
264
|
+
*
|
|
265
|
+
* Returns `{ success: false, error }` on failure. As of #879 the caller
|
|
266
|
+
* (`runIssueWithLogging`) treats an attempted-but-failed PR as a run failure:
|
|
267
|
+
* a passing run that produced no PR is not a success. This function itself
|
|
268
|
+
* still only reports — it never throws — so relay teardown runs regardless.
|
|
239
269
|
*
|
|
240
270
|
* @param worktreePath Path to the worktree
|
|
241
271
|
* @param issueNumber Issue number
|
|
@@ -246,10 +276,13 @@ export declare function buildAutomatedPRBody(issueNumber: number, opts?: {
|
|
|
246
276
|
* @param stackOptions When set under --stacked, `prBase` overrides the default
|
|
247
277
|
* PR target (otherwise gh defaults to the repo's default branch) and
|
|
248
278
|
* `stackManifest` is appended to the PR body. (#605)
|
|
279
|
+
* @param qaVerdict QA verdict surfaced in the PR body when non-A+ (#749).
|
|
280
|
+
* @param readyGateReport Rendered `sequant ready` gap report, set only when the
|
|
281
|
+
* run used `--ready-gate` (#817).
|
|
249
282
|
* @returns PRCreationResult with PR info or error
|
|
250
283
|
* @internal Exported for testing
|
|
251
284
|
*/
|
|
252
285
|
export declare function createPR(worktreePath: string, issueNumber: number, issueTitle: string, branch: string, verbose: boolean, labels?: string[], stackOptions?: {
|
|
253
286
|
prBase?: string;
|
|
254
287
|
stackManifest?: string;
|
|
255
|
-
}, qaVerdict?: string): PRCreationResult;
|
|
288
|
+
}, qaVerdict?: string, readyGateReport?: string): PRCreationResult;
|
|
@@ -8,7 +8,8 @@ import chalk from "chalk";
|
|
|
8
8
|
import { spawnSync } from "child_process";
|
|
9
9
|
import { existsSync, readFileSync } from "fs";
|
|
10
10
|
import path from "path";
|
|
11
|
-
import {
|
|
11
|
+
import { resolvePackageManager, resolvePackageManagerConfig, } from "../stacks.js";
|
|
12
|
+
import { resolveDiffBase } from "./git-diff-utils.js";
|
|
12
13
|
import { getResumablePhasesForIssue } from "./phase-detection.js";
|
|
13
14
|
import { GitHubProvider } from "./platforms/github.js";
|
|
14
15
|
/**
|
|
@@ -155,7 +156,11 @@ export function checkWorktreeFreshness(worktreePath, verbose, baseBranch = "main
|
|
|
155
156
|
const countResult = spawnSync("git", ["-C", worktreePath, "rev-list", "--count", `${mergeBase}..${baseHead}`], { stdio: "pipe" });
|
|
156
157
|
if (countResult.status === 0) {
|
|
157
158
|
result.commitsBehind = parseInt(countResult.stdout.toString().trim(), 10);
|
|
158
|
-
// Consider stale if more than 5 commits behind
|
|
159
|
+
// Consider stale if more than 5 commits behind. NOT configurable — the
|
|
160
|
+
// threshold is the literal below, and a stale-but-clean worktree is
|
|
161
|
+
// force-removed and rebuilt on the strength of it (see the recreate
|
|
162
|
+
// branch in `ensureWorktree`). Surfaced in #810; left as a literal here
|
|
163
|
+
// because making it configurable is a behavior change, not a comment fix.
|
|
159
164
|
result.isStale = result.commitsBehind > 5;
|
|
160
165
|
}
|
|
161
166
|
}
|
|
@@ -200,10 +205,18 @@ export function removeStaleWorktree(existingPath, branch, verbose) {
|
|
|
200
205
|
}
|
|
201
206
|
/**
|
|
202
207
|
* List all active worktrees with their branches
|
|
208
|
+
*
|
|
209
|
+
* `git worktree list` only ever reports worktrees belonging to the repository
|
|
210
|
+
* containing `cwd`, which is what makes this a repo-scoped lookup: a sibling
|
|
211
|
+
* project's worktree can never appear here, even though `../worktrees/` is a
|
|
212
|
+
* single directory shared by every repo under the same parent (#899).
|
|
213
|
+
*
|
|
214
|
+
* @param cwd - Directory to run git in. Defaults to the current process cwd.
|
|
203
215
|
*/
|
|
204
|
-
export function listWorktrees() {
|
|
216
|
+
export function listWorktrees(cwd) {
|
|
205
217
|
const result = spawnSync("git", ["worktree", "list", "--porcelain"], {
|
|
206
218
|
stdio: "pipe",
|
|
219
|
+
...(cwd ? { cwd } : {}),
|
|
207
220
|
});
|
|
208
221
|
if (result.status !== 0)
|
|
209
222
|
return [];
|
|
@@ -231,7 +244,8 @@ export function listWorktrees() {
|
|
|
231
244
|
* Get changed files in a worktree compared to main
|
|
232
245
|
*/
|
|
233
246
|
export function getWorktreeChangedFiles(worktreePath) {
|
|
234
|
-
const
|
|
247
|
+
const diffBase = resolveDiffBase(worktreePath, "main");
|
|
248
|
+
const result = spawnSync("git", ["-C", worktreePath, "diff", "--name-only", `${diffBase}...HEAD`], { stdio: "pipe" });
|
|
235
249
|
if (result.status !== 0)
|
|
236
250
|
return [];
|
|
237
251
|
return result.stdout
|
|
@@ -245,7 +259,8 @@ export function getWorktreeChangedFiles(worktreePath) {
|
|
|
245
259
|
* Returns aggregate metrics only - no file paths to preserve privacy
|
|
246
260
|
*/
|
|
247
261
|
export function getWorktreeDiffStats(worktreePath) {
|
|
248
|
-
const
|
|
262
|
+
const diffBase = resolveDiffBase(worktreePath, "main");
|
|
263
|
+
const result = spawnSync("git", ["-C", worktreePath, "diff", "--stat", `${diffBase}...HEAD`], { stdio: "pipe" });
|
|
249
264
|
if (result.status !== 0) {
|
|
250
265
|
return { filesChanged: 0, linesAdded: 0 };
|
|
251
266
|
}
|
|
@@ -314,6 +329,63 @@ export function filterResumedPhases(issueNumber, phases, resume) {
|
|
|
314
329
|
const skipped = phases.filter((p) => !resumable.includes(p));
|
|
315
330
|
return { phases: resumable, skipped };
|
|
316
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Run the frozen dependency install for a freshly provisioned worktree and
|
|
334
|
+
* surface (rather than swallow) a failed install.
|
|
335
|
+
*
|
|
336
|
+
* Mirrors the status check in `reinstallIfLockfileChanged` (#846): a plain
|
|
337
|
+
* `npm install` self-heals a stale/absent lockfile, but the frozen `npm ci`
|
|
338
|
+
* hard-fails (exit 1, no node_modules). Without this check a failing install
|
|
339
|
+
* left a silently dependency-less worktree whose breakage only surfaced later
|
|
340
|
+
* as a confusing phase error. The warning names the resolved command so the
|
|
341
|
+
* user can rerun it by hand.
|
|
342
|
+
*
|
|
343
|
+
* @returns true if the install succeeded, false if it failed (warn-and-continue)
|
|
344
|
+
* @internal Exported for testing
|
|
345
|
+
*/
|
|
346
|
+
export function installWorktreeDeps(worktreePath, packageManager, verbose) {
|
|
347
|
+
if (verbose) {
|
|
348
|
+
console.log(chalk.gray(` Installing dependencies...`));
|
|
349
|
+
}
|
|
350
|
+
// Two independent questions, resolved in order against the worktree itself.
|
|
351
|
+
//
|
|
352
|
+
// WHICH manager: the manifest's packageManager is a snapshot and may be
|
|
353
|
+
// absent, so fall back to the worktree's own lockfile — that keeps this in
|
|
354
|
+
// agreement with what new-feature.sh would run for the same project (#870).
|
|
355
|
+
//
|
|
356
|
+
// WHICH commands for it: yarn's frozen install differs between classic
|
|
357
|
+
// (`--frozen-lockfile`) and berry (`--immutable`), and both majors use
|
|
358
|
+
// `yarn.lock`, so the manager's identity alone cannot say which (#871).
|
|
359
|
+
//
|
|
360
|
+
// The worktree is checked out by now, so its package.json / .yarnrc.yml /
|
|
361
|
+
// yarn.lock are all readable here.
|
|
362
|
+
const pmConfig = resolvePackageManagerConfig(resolvePackageManager(packageManager, worktreePath), worktreePath);
|
|
363
|
+
// ciInstall, not installSilent: a plain `npm install` normalizes and
|
|
364
|
+
// rewrites package-lock.json (observed: npm 10 strips the `libc` fields a
|
|
365
|
+
// newer npm committed), so every provisioned worktree started dirty. That
|
|
366
|
+
// one unstaged file cascaded: rebaseBeforePR refused to run, stale
|
|
367
|
+
// worktrees read as having "uncommitted changes" and were never recreated,
|
|
368
|
+
// and chain checkpoints skipped on an out-of-scope dirty file. A frozen
|
|
369
|
+
// install never touches the lockfile. Same substitution #803 made for
|
|
370
|
+
// merge-check's combined-branch test. The `!existsSync(node_modules)`
|
|
371
|
+
// guard at the call site means `npm ci`'s wipe-and-reinstall has nothing
|
|
372
|
+
// to wipe.
|
|
373
|
+
const [cmd, ...args] = pmConfig.ciInstall.split(" ");
|
|
374
|
+
const command = [cmd, ...args].join(" ");
|
|
375
|
+
const installResult = spawnSync(cmd, args, {
|
|
376
|
+
cwd: worktreePath,
|
|
377
|
+
stdio: "pipe",
|
|
378
|
+
});
|
|
379
|
+
if (installResult.status !== 0) {
|
|
380
|
+
// stderr may be empty on some failures — the resolved command alone is
|
|
381
|
+
// enough to act on, so always name it (AC-2).
|
|
382
|
+
const error = installResult.stderr?.toString().trim() ?? "";
|
|
383
|
+
const detail = error ? `: ${error}` : "";
|
|
384
|
+
console.log(chalk.yellow(` ! Dependency install failed (${command})${detail}`));
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
317
389
|
/**
|
|
318
390
|
* Create or reuse a worktree for an issue
|
|
319
391
|
* @param baseBranch - Optional branch to use as base instead of origin/main (for chain mode)
|
|
@@ -513,17 +585,7 @@ export async function ensureWorktree(issueNumber, title, verbose, packageManager
|
|
|
513
585
|
// Install dependencies if needed
|
|
514
586
|
const nodeModulesPath = path.join(worktreePath, "node_modules");
|
|
515
587
|
if (!existsSync(nodeModulesPath)) {
|
|
516
|
-
|
|
517
|
-
console.log(chalk.gray(` Installing dependencies...`));
|
|
518
|
-
}
|
|
519
|
-
// Use detected package manager or default to npm
|
|
520
|
-
const pm = packageManager || "npm";
|
|
521
|
-
const pmConfig = PM_CONFIG[pm];
|
|
522
|
-
const [cmd, ...args] = pmConfig.installSilent.split(" ");
|
|
523
|
-
spawnSync(cmd, args, {
|
|
524
|
-
cwd: worktreePath,
|
|
525
|
-
stdio: "pipe",
|
|
526
|
-
});
|
|
588
|
+
installWorktreeDeps(worktreePath, packageManager, verbose);
|
|
527
589
|
}
|
|
528
590
|
if (verbose) {
|
|
529
591
|
console.log(chalk.green(` ✔ Worktree ready: ${worktreePath}`));
|
|
@@ -742,9 +804,14 @@ export function reinstallIfLockfileChanged(worktreePath, packageManager, verbose
|
|
|
742
804
|
}
|
|
743
805
|
// Re-run install to sync node_modules with updated lockfile
|
|
744
806
|
console.log(chalk.blue(` Reinstalling dependencies (lockfile changed)...`));
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const
|
|
807
|
+
// Same two-step resolution as installWorktreeDeps: manifest-or-lockfile for
|
|
808
|
+
// the manager (#870), then classic-vs-berry for its commands (#871).
|
|
809
|
+
const pmConfig = resolvePackageManagerConfig(resolvePackageManager(packageManager, worktreePath), worktreePath);
|
|
810
|
+
// ciInstall for the same reason as provisioning (see ensureWorktree): this
|
|
811
|
+
// reinstall exists because a rebase pulled in a NEW lockfile, so installing
|
|
812
|
+
// exactly what that lockfile says — never rewriting it — is the semantic
|
|
813
|
+
// the function's own name promises.
|
|
814
|
+
const [cmd, ...args] = pmConfig.ciInstall.split(" ");
|
|
748
815
|
const installResult = spawnSync(cmd, args, {
|
|
749
816
|
cwd: worktreePath,
|
|
750
817
|
stdio: "pipe",
|
|
@@ -888,6 +955,9 @@ function qaVerdictNote(verdict) {
|
|
|
888
955
|
* @param opts.qaVerdict QA verdict for the run; a non-A+ stopping state
|
|
889
956
|
* (anything other than `READY_FOR_MERGE`) surfaces a note so a human reviewer
|
|
890
957
|
* sees why the run broke to PR without reaching A+ (#749).
|
|
958
|
+
* @param opts.readyGateReport Rendered `sequant ready` gap report (#817). Set
|
|
959
|
+
* only when the run used `--ready-gate`; surfaces the gate outcome (threshold
|
|
960
|
+
* reached vs guard halt) in the PR body the same way `sequant ready` does.
|
|
891
961
|
* @internal Exported for testing
|
|
892
962
|
*/
|
|
893
963
|
export function buildAutomatedPRBody(issueNumber, opts) {
|
|
@@ -901,9 +971,18 @@ export function buildAutomatedPRBody(issueNumber, opts) {
|
|
|
901
971
|
];
|
|
902
972
|
// #749: surface a non-A+ QA verdict in the PR body (not just the run log) so
|
|
903
973
|
// a reviewer sees why the run broke to PR rather than reaching A+.
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
974
|
+
//
|
|
975
|
+
// #817: when the ready gate ran, its report already carries the terminal
|
|
976
|
+
// verdict and stop reason (a superset of the #749 note), so prefer it and
|
|
977
|
+
// suppress the standalone note to avoid a contradictory double-headline.
|
|
978
|
+
if (opts?.readyGateReport) {
|
|
979
|
+
bodyLines.push(opts.readyGateReport, ``);
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
const note = qaVerdictNote(opts?.qaVerdict);
|
|
983
|
+
if (note) {
|
|
984
|
+
bodyLines.push(note, ``);
|
|
985
|
+
}
|
|
907
986
|
}
|
|
908
987
|
// #605 AC-4: emit stack manifest before the trailer so reviewers see the
|
|
909
988
|
// chain at the top of the body. Manifest is only set under --stacked.
|
|
@@ -917,7 +996,11 @@ export function buildAutomatedPRBody(issueNumber, opts) {
|
|
|
917
996
|
* Push branch and create a PR after successful QA.
|
|
918
997
|
*
|
|
919
998
|
* Handles both fresh PR creation and detection of existing PRs.
|
|
920
|
-
*
|
|
999
|
+
*
|
|
1000
|
+
* Returns `{ success: false, error }` on failure. As of #879 the caller
|
|
1001
|
+
* (`runIssueWithLogging`) treats an attempted-but-failed PR as a run failure:
|
|
1002
|
+
* a passing run that produced no PR is not a success. This function itself
|
|
1003
|
+
* still only reports — it never throws — so relay teardown runs regardless.
|
|
921
1004
|
*
|
|
922
1005
|
* @param worktreePath Path to the worktree
|
|
923
1006
|
* @param issueNumber Issue number
|
|
@@ -928,10 +1011,13 @@ export function buildAutomatedPRBody(issueNumber, opts) {
|
|
|
928
1011
|
* @param stackOptions When set under --stacked, `prBase` overrides the default
|
|
929
1012
|
* PR target (otherwise gh defaults to the repo's default branch) and
|
|
930
1013
|
* `stackManifest` is appended to the PR body. (#605)
|
|
1014
|
+
* @param qaVerdict QA verdict surfaced in the PR body when non-A+ (#749).
|
|
1015
|
+
* @param readyGateReport Rendered `sequant ready` gap report, set only when the
|
|
1016
|
+
* run used `--ready-gate` (#817).
|
|
931
1017
|
* @returns PRCreationResult with PR info or error
|
|
932
1018
|
* @internal Exported for testing
|
|
933
1019
|
*/
|
|
934
|
-
export function createPR(worktreePath, issueNumber, issueTitle, branch, verbose, labels, stackOptions, qaVerdict) {
|
|
1020
|
+
export function createPR(worktreePath, issueNumber, issueTitle, branch, verbose, labels, stackOptions, qaVerdict, readyGateReport) {
|
|
935
1021
|
const github = new GitHubProvider();
|
|
936
1022
|
// Step 1: Check for existing PR on this branch
|
|
937
1023
|
const existingPRInfo = github.viewPRByBranchSync(branch, worktreePath);
|
|
@@ -970,6 +1056,7 @@ export function createPR(worktreePath, issueNumber, issueTitle, branch, verbose,
|
|
|
970
1056
|
const prBody = buildAutomatedPRBody(issueNumber, {
|
|
971
1057
|
stackManifest: stackOptions?.stackManifest,
|
|
972
1058
|
qaVerdict,
|
|
1059
|
+
readyGateReport,
|
|
973
1060
|
});
|
|
974
1061
|
const prResult = github.createPRCliSync(prTitle, prBody, branch, worktreePath, stackOptions?.prBase);
|
|
975
1062
|
if (prResult.exitCode !== 0) {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo-scoped worktree resolution and verification (#899).
|
|
3
|
+
*
|
|
4
|
+
* Skill-driven `/fullsolve` used to hand `/exec` an unexpanded glob
|
|
5
|
+
* (`../worktrees/feature/<issue>-` followed by a star and a slash) and `/exec`
|
|
6
|
+
* was instructed to trust it without checking. Two things went wrong:
|
|
7
|
+
*
|
|
8
|
+
* 1. When nothing had created the worktree, the `cd` failed and the agent
|
|
9
|
+
* silently kept working in the main checkout.
|
|
10
|
+
* 2. `../worktrees/` is one flat namespace shared by every repo under the same
|
|
11
|
+
* parent directory, and issue numbers are per-repo — so where the glob *did*
|
|
12
|
+
* match, it could match a sibling project's worktree.
|
|
13
|
+
*
|
|
14
|
+
* Both are fixed the same way: never resolve worktrees through the filesystem.
|
|
15
|
+
* `git worktree list` reports only the worktrees of the repository containing
|
|
16
|
+
* `cwd`, so scoping is structural rather than a filter that can be forgotten,
|
|
17
|
+
* and it reports the *branch*, which is the real identity — a worktree
|
|
18
|
+
* directory slug can drift from its branch after a rename.
|
|
19
|
+
*/
|
|
20
|
+
/** Named failure modes for `resolveIssueWorktree`. */
|
|
21
|
+
export type ResolveErrorCode = "WORKTREE_NOT_FOUND" | "WORKTREE_AMBIGUOUS";
|
|
22
|
+
/** Named failure modes for `verifyWorktreePath`. */
|
|
23
|
+
export type VerifyErrorCode = "SEQUANT_WORKTREE_NOT_FOUND" | "SEQUANT_WORKTREE_FOREIGN" | "SEQUANT_WORKTREE_ISSUE_MISMATCH";
|
|
24
|
+
/** A worktree of the current repository, as reported by git. */
|
|
25
|
+
export interface ResolvedWorktree {
|
|
26
|
+
/** Absolute path, exactly as git reports it. */
|
|
27
|
+
path: string;
|
|
28
|
+
/** Branch checked out there, e.g. `feature/899-some-slug`. */
|
|
29
|
+
branch: string;
|
|
30
|
+
}
|
|
31
|
+
export type ResolveResult = ({
|
|
32
|
+
ok: true;
|
|
33
|
+
} & ResolvedWorktree) | {
|
|
34
|
+
ok: false;
|
|
35
|
+
error: ResolveErrorCode;
|
|
36
|
+
message: string;
|
|
37
|
+
/** Populated for WORKTREE_AMBIGUOUS so the caller can report candidates. */
|
|
38
|
+
candidates: ResolvedWorktree[];
|
|
39
|
+
};
|
|
40
|
+
export type VerifyResult = ({
|
|
41
|
+
ok: true;
|
|
42
|
+
} & ResolvedWorktree) | {
|
|
43
|
+
ok: false;
|
|
44
|
+
error: VerifyErrorCode;
|
|
45
|
+
message: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Find the worktree of the *current* repository that holds issue `issue`.
|
|
49
|
+
*
|
|
50
|
+
* Selection keys on the branch git reports, never on the directory name, so a
|
|
51
|
+
* worktree whose slug has drifted from its branch still resolves — and a
|
|
52
|
+
* directory that merely looks like a match (a sibling repo's worktree, or a
|
|
53
|
+
* stray directory) never does.
|
|
54
|
+
*
|
|
55
|
+
* @param issue - Issue number to look up.
|
|
56
|
+
* @param cwd - Directory inside the repository to search. Defaults to process cwd.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveIssueWorktree(issue: number, cwd?: string): ResolveResult;
|
|
59
|
+
/**
|
|
60
|
+
* Verify that a caller-supplied worktree path is safe to work in.
|
|
61
|
+
*
|
|
62
|
+
* This is the guard `/exec` runs before trusting `SEQUANT_WORKTREE`. It fails
|
|
63
|
+
* closed on every uncertain case — a bad path must halt the run, never degrade
|
|
64
|
+
* into "keep going in whatever directory we happen to be in".
|
|
65
|
+
*
|
|
66
|
+
* @param worktreePath - The path to check (typically `$SEQUANT_WORKTREE`).
|
|
67
|
+
* @param options.issue - When set, the branch must belong to this issue.
|
|
68
|
+
* @param options.cwd - Directory inside the repository to check against.
|
|
69
|
+
*/
|
|
70
|
+
export declare function verifyWorktreePath(worktreePath: string, options?: {
|
|
71
|
+
issue?: number;
|
|
72
|
+
cwd?: string;
|
|
73
|
+
}): VerifyResult;
|