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,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which persisted issue statuses mean "this issue is already done" (#837).
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for every pre-flight guard that skips finished work.
|
|
5
|
+
* It exists because the same predicate was previously spelled out inline at two
|
|
6
|
+
* sites with two different lists, and #817's `--ready-gate` extended the status
|
|
7
|
+
* vocabulary without either site following:
|
|
8
|
+
*
|
|
9
|
+
* - `chain-resume.ts` peels the completed prefix off a `--chain` run.
|
|
10
|
+
* - `run-orchestrator.ts` drops finished issues from a non-chain run.
|
|
11
|
+
*
|
|
12
|
+
* A gated issue deliberately terminates in `waiting_for_human_merge` (policy
|
|
13
|
+
* threshold reached) rather than `ready_for_merge`, since `ready_for_merge`
|
|
14
|
+
* would read as auto-merge-ready and defeat the human merge gate the feature
|
|
15
|
+
* exists to preserve. Both guards missed it, so re-running re-executed
|
|
16
|
+
* already-gated issues from phase 0 — a full spec/exec/qa pipeline plus another
|
|
17
|
+
* full-weight ready gate.
|
|
18
|
+
*
|
|
19
|
+
* `blocked` is deliberately NOT a completed status, for two reasons:
|
|
20
|
+
*
|
|
21
|
+
* 1. It is a *generic* member of `IssueStatusSchema` ("waiting on external input
|
|
22
|
+
* or dependency"), not a ready-gate-exclusive terminal. Admitting it here
|
|
23
|
+
* would silently apply to every other writer of that status, present and
|
|
24
|
+
* future.
|
|
25
|
+
* 2. A guard halt IS the human-attention signal. Skipping it as complete would
|
|
26
|
+
* report the issue as passed when it demonstrably did not.
|
|
27
|
+
*
|
|
28
|
+
* So a `blocked` issue is re-executed rather than skipped — the same
|
|
29
|
+
* conservative rule already applied to issues whose state lookup fails.
|
|
30
|
+
* Re-running wastes tokens; silently dropping an issue the user must look at is
|
|
31
|
+
* the worse failure. Once the user clears the blocker, the re-run is what lets
|
|
32
|
+
* work resume.
|
|
33
|
+
*
|
|
34
|
+
* NOTE: this is "done, do not re-run", which is NOT the same set as "might have
|
|
35
|
+
* a merged PR" — `state-cleanup.ts`'s merge-detection sweep deliberately keeps
|
|
36
|
+
* its own wider list (it also scans `in_progress`, #592, and
|
|
37
|
+
* `waiting_for_qa_gate`, #606). Do not unify the two.
|
|
38
|
+
*
|
|
39
|
+
* When adding a status to `IssueStatusSchema`, decide here whether it belongs.
|
|
40
|
+
*/
|
|
41
|
+
export const COMPLETED_ISSUE_STATUSES = [
|
|
42
|
+
"ready_for_merge",
|
|
43
|
+
"merged",
|
|
44
|
+
"waiting_for_human_merge",
|
|
45
|
+
];
|
|
46
|
+
const COMPLETED_ISSUE_STATUS_SET = new Set(COMPLETED_ISSUE_STATUSES);
|
|
47
|
+
/**
|
|
48
|
+
* True iff `status` marks the issue as already finished, so a re-run should
|
|
49
|
+
* skip it unless `--force` is passed.
|
|
50
|
+
*
|
|
51
|
+
* Accepts `string | undefined` because persisted state may predate the current
|
|
52
|
+
* schema or be absent entirely; an unknown or missing status is treated as
|
|
53
|
+
* incomplete (re-execute), never as complete.
|
|
54
|
+
*/
|
|
55
|
+
export function isCompletedIssueStatus(status) {
|
|
56
|
+
return status !== undefined && COMPLETED_ISSUE_STATUS_SET.has(status);
|
|
57
|
+
}
|
|
@@ -42,6 +42,32 @@ export declare function normalizeCommanderOptions(options: RunOptions): RunOptio
|
|
|
42
42
|
* This replaces the inline merging logic previously in run.ts.
|
|
43
43
|
*/
|
|
44
44
|
export declare function resolveRunOptions(cliOptions: RunOptions, settings: SequantSettings): RunOptions;
|
|
45
|
+
/**
|
|
46
|
+
* Fall back to `fallback` unless `value` is a usable positive number.
|
|
47
|
+
*
|
|
48
|
+
* `??` alone is not enough for these (#833). `NaN` is not nullish, so a
|
|
49
|
+
* malformed value survives `?? default` and flows on into `setTimeout`, which
|
|
50
|
+
* clamps a `NaN` delay to 0 and aborts the phase on its first tick, or into
|
|
51
|
+
* `while (iteration < maxIterations)`, which is false on entry and runs zero
|
|
52
|
+
* phases. Both read as a phase/agent fault rather than a bad input, which is
|
|
53
|
+
* what makes the silent version expensive. Non-finite and non-positive values
|
|
54
|
+
* therefore fall back *to* the default instead of through it.
|
|
55
|
+
*
|
|
56
|
+
* `bin/cli.ts` rejects these at the flag boundary with a message naming the
|
|
57
|
+
* flag — that is the user-facing fix. This is the structural backstop for
|
|
58
|
+
* programmatic callers, `settings.json`, and whatever calls this next.
|
|
59
|
+
*
|
|
60
|
+
* Exported because `phaseTimeout` has two producers, not one: this module and
|
|
61
|
+
* `commands/ready.ts`, whose value reaches the driver through
|
|
62
|
+
* `ready-gate.ts`'s own `buildPhaseConfig` and never passes through
|
|
63
|
+
* `buildExecutionConfig`. Guarding only here would have left that path open.
|
|
64
|
+
* Chain it to express the layering — CLI, then settings, then the default:
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* positiveOr(options.timeout, positiveOr(settings.run.timeout, DEFAULT))
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function positiveOr(value: number | undefined, fallback: number): number;
|
|
45
71
|
/**
|
|
46
72
|
* Build an ExecutionConfig from merged RunOptions and settings.
|
|
47
73
|
* Extracts the phase-timeout, MCP, retry, and mode resolution logic
|
|
@@ -97,6 +97,10 @@ export function normalizeCommanderOptions(options) {
|
|
|
97
97
|
...(raw.retry === false && { noRetry: true }),
|
|
98
98
|
...(raw.rebase === false && { noRebase: true }),
|
|
99
99
|
...(raw.pr === false && { noPr: true }),
|
|
100
|
+
// #804: map the flag-derived key onto the interface field. Guarded on
|
|
101
|
+
// `undefined` (not truthiness) so an explicit `--auto-wait 0` still
|
|
102
|
+
// overrides a non-zero setting.
|
|
103
|
+
...(raw.autoWait !== undefined && { autoWaitMinutes: raw.autoWait }),
|
|
100
104
|
};
|
|
101
105
|
}
|
|
102
106
|
/**
|
|
@@ -117,6 +121,7 @@ export function resolveRunOptions(cliOptions, settings) {
|
|
|
117
121
|
qualityLoop: defined.qualityLoop ?? settings.run.qualityLoop,
|
|
118
122
|
maxIterations: defined.maxIterations ?? settings.run.maxIterations,
|
|
119
123
|
noSmartTests: defined.noSmartTests ?? !settings.run.smartTests,
|
|
124
|
+
autoWaitMinutes: defined.autoWaitMinutes ?? settings.run.autoWaitMinutes,
|
|
120
125
|
// Agent settings
|
|
121
126
|
isolateParallel: defined.isolateParallel ?? settings.agents.isolateParallel,
|
|
122
127
|
// Env overrides
|
|
@@ -129,6 +134,36 @@ export function resolveRunOptions(cliOptions, settings) {
|
|
|
129
134
|
merged.autoDetectPhases = autoDetectPhases;
|
|
130
135
|
return merged;
|
|
131
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Fall back to `fallback` unless `value` is a usable positive number.
|
|
139
|
+
*
|
|
140
|
+
* `??` alone is not enough for these (#833). `NaN` is not nullish, so a
|
|
141
|
+
* malformed value survives `?? default` and flows on into `setTimeout`, which
|
|
142
|
+
* clamps a `NaN` delay to 0 and aborts the phase on its first tick, or into
|
|
143
|
+
* `while (iteration < maxIterations)`, which is false on entry and runs zero
|
|
144
|
+
* phases. Both read as a phase/agent fault rather than a bad input, which is
|
|
145
|
+
* what makes the silent version expensive. Non-finite and non-positive values
|
|
146
|
+
* therefore fall back *to* the default instead of through it.
|
|
147
|
+
*
|
|
148
|
+
* `bin/cli.ts` rejects these at the flag boundary with a message naming the
|
|
149
|
+
* flag — that is the user-facing fix. This is the structural backstop for
|
|
150
|
+
* programmatic callers, `settings.json`, and whatever calls this next.
|
|
151
|
+
*
|
|
152
|
+
* Exported because `phaseTimeout` has two producers, not one: this module and
|
|
153
|
+
* `commands/ready.ts`, whose value reaches the driver through
|
|
154
|
+
* `ready-gate.ts`'s own `buildPhaseConfig` and never passes through
|
|
155
|
+
* `buildExecutionConfig`. Guarding only here would have left that path open.
|
|
156
|
+
* Chain it to express the layering — CLI, then settings, then the default:
|
|
157
|
+
*
|
|
158
|
+
* ```ts
|
|
159
|
+
* positiveOr(options.timeout, positiveOr(settings.run.timeout, DEFAULT))
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export function positiveOr(value, fallback) {
|
|
163
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
164
|
+
? value
|
|
165
|
+
: fallback;
|
|
166
|
+
}
|
|
132
167
|
/**
|
|
133
168
|
* Build an ExecutionConfig from merged RunOptions and settings.
|
|
134
169
|
* Extracts the phase-timeout, MCP, retry, and mode resolution logic
|
|
@@ -157,15 +192,32 @@ export function buildExecutionConfig(mergedOptions, settings, issueCount) {
|
|
|
157
192
|
parallel: isParallel,
|
|
158
193
|
dryRun: mergedOptions.dryRun ?? false,
|
|
159
194
|
verbose: mergedOptions.verbose ?? false,
|
|
160
|
-
|
|
195
|
+
// #833: chained so the layering is CLI → settings → default, matching
|
|
196
|
+
// `commands/ready.ts`. A malformed CLI value falls back to the user's
|
|
197
|
+
// configured setting rather than skipping past it to the hardcoded
|
|
198
|
+
// default; a malformed setting falls back to the default. In the normal
|
|
199
|
+
// path `mergedOptions` already carries the settings value, so this only
|
|
200
|
+
// differs when one of the two layers is unusable — which is the case that
|
|
201
|
+
// matters.
|
|
202
|
+
phaseTimeout: positiveOr(mergedOptions.timeout, positiveOr(settings.run.timeout, DEFAULT_CONFIG.phaseTimeout)),
|
|
161
203
|
qualityLoop: mergedOptions.qualityLoop ?? false,
|
|
162
|
-
maxIterations: mergedOptions.maxIterations
|
|
204
|
+
maxIterations: positiveOr(mergedOptions.maxIterations, positiveOr(settings.run.maxIterations, DEFAULT_CONFIG.maxIterations)),
|
|
163
205
|
noSmartTests: mergedOptions.noSmartTests ?? false,
|
|
164
206
|
mcp: mcpEnabled,
|
|
165
207
|
retry: retryEnabled,
|
|
208
|
+
// #804: default 0 (off) — the whole regression contract for auto-wait is
|
|
209
|
+
// that an unset flag leaves the #761/#799 halt path untouched.
|
|
210
|
+
autoWaitMinutes: mergedOptions.autoWaitMinutes ??
|
|
211
|
+
settings.run.autoWaitMinutes ??
|
|
212
|
+
DEFAULT_CONFIG.autoWaitMinutes,
|
|
166
213
|
agent: mergedOptions.agent ?? settings.run.agent,
|
|
167
214
|
aiderSettings: settings.run.aider,
|
|
168
215
|
isolateParallel: mergedOptions.isolateParallel,
|
|
169
216
|
relayEnabled,
|
|
217
|
+
// #817: opt-in only, no settings backing (AC-4). A missing `--ready-gate`
|
|
218
|
+
// resolves to false, keeping the run path byte-identical (AC-5). This is the
|
|
219
|
+
// load-bearing wire the #795 inert-flag class guards against — the flag is
|
|
220
|
+
// useless if it stops reaching the executor here.
|
|
221
|
+
readyGate: mergedOptions.readyGate ?? false,
|
|
170
222
|
};
|
|
171
223
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parser for line-leading dependency markers in an issue body (#767).
|
|
3
|
+
*
|
|
4
|
+
* Two callers extract issue numbers from `depends on #N` / `blocked by #N`
|
|
5
|
+
* declarations:
|
|
6
|
+
*
|
|
7
|
+
* - `chain-preflight.ts:parseDeclaredBlockers` — warn-only pre-flight, honors
|
|
8
|
+
* BOTH `depends on` and `blocked by`.
|
|
9
|
+
* - `batch-executor.ts:parseDependencies` — feeds `sortByDependencies`, which
|
|
10
|
+
* *silently reorders the run*, so it honors ONLY `depends on`.
|
|
11
|
+
*
|
|
12
|
+
* The hardened mechanics (line anchoring, required `#`, code/comment stripping)
|
|
13
|
+
* were introduced for the pre-flight in #762/PR #764 and lived only in
|
|
14
|
+
* `chain-preflight.ts`. #767 promotes them here so the sorter can adopt the same
|
|
15
|
+
* hardening without the two regexes drifting — while keeping the **marker set
|
|
16
|
+
* per-caller** so sharing the parser does NOT make the sorter start reordering
|
|
17
|
+
* on `blocked by` (a new, unrequested silent-reorder class; #762 Open Q #3).
|
|
18
|
+
*/
|
|
19
|
+
/** A dependency-declaration marker a caller opts into honoring. */
|
|
20
|
+
export type DepMarker = "depends on" | "blocked by";
|
|
21
|
+
/**
|
|
22
|
+
* Parse the issue numbers a body declares itself dependent on / blocked by,
|
|
23
|
+
* honoring only the requested `markers`. Deduped, order-preserving.
|
|
24
|
+
*
|
|
25
|
+
* Only line-leading markers count as declarations — see `buildMarkerRegex` for
|
|
26
|
+
* why mid-sentence prose mentions are deliberately ignored. Code blocks, inline
|
|
27
|
+
* code spans, and HTML comments are stripped first.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseBodyDependencyMarkers(body: string, markers: DepMarker[]): number[];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parser for line-leading dependency markers in an issue body (#767).
|
|
3
|
+
*
|
|
4
|
+
* Two callers extract issue numbers from `depends on #N` / `blocked by #N`
|
|
5
|
+
* declarations:
|
|
6
|
+
*
|
|
7
|
+
* - `chain-preflight.ts:parseDeclaredBlockers` — warn-only pre-flight, honors
|
|
8
|
+
* BOTH `depends on` and `blocked by`.
|
|
9
|
+
* - `batch-executor.ts:parseDependencies` — feeds `sortByDependencies`, which
|
|
10
|
+
* *silently reorders the run*, so it honors ONLY `depends on`.
|
|
11
|
+
*
|
|
12
|
+
* The hardened mechanics (line anchoring, required `#`, code/comment stripping)
|
|
13
|
+
* were introduced for the pre-flight in #762/PR #764 and lived only in
|
|
14
|
+
* `chain-preflight.ts`. #767 promotes them here so the sorter can adopt the same
|
|
15
|
+
* hardening without the two regexes drifting — while keeping the **marker set
|
|
16
|
+
* per-caller** so sharing the parser does NOT make the sorter start reordering
|
|
17
|
+
* on `blocked by` (a new, unrequested silent-reorder class; #762 Open Q #3).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Build the anchored marker regex for the requested marker set.
|
|
21
|
+
*
|
|
22
|
+
* Matches a declared marker, optionally bold-wrapped, colon-separated, and/or
|
|
23
|
+
* written as a list item, e.g. `- **Depends on**: #123`.
|
|
24
|
+
*
|
|
25
|
+
* Anchored to line start because a *declaration* is a line about the issue's
|
|
26
|
+
* own dependencies, whereas prose that merely mentions the marker mid-sentence
|
|
27
|
+
* is not. #762's own body is the motivating case: it contains both `...when #39
|
|
28
|
+
* says blocked by #38` and `...real markers like "Blocked by #36"` as examples,
|
|
29
|
+
* and an unanchored match reported #762 as blocked by #38 and #36 — exactly the
|
|
30
|
+
* false inference #604 says is worse than none. That matters most under
|
|
31
|
+
* `--strict-preflight`, where a bogus warning hard-aborts a legitimate chain.
|
|
32
|
+
*
|
|
33
|
+
* The `#` is required (unlike the historic looser `#?` in `parseDependencies`)
|
|
34
|
+
* so a line such as `Blocked by 5 days of review` — or the prose
|
|
35
|
+
* `Issue 14 depends on 12+13` — cannot parse as an issue number.
|
|
36
|
+
*/
|
|
37
|
+
function buildMarkerRegex(markers) {
|
|
38
|
+
const alternation = markers.map((m) => m.split(/\s+/).join("\\s+")).join("|");
|
|
39
|
+
return new RegExp(`^\\s*(?:[-*]\\s*)?\\*?\\*?(?:${alternation})\\*?\\*?:?\\s*#(\\d+)`, "gim");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strip fenced code blocks, inline code spans, and HTML comments so markers
|
|
43
|
+
* inside quoted shell snippets, documentation examples, or commented-out drafts
|
|
44
|
+
* don't count as real declarations. Inline spans are matched within a single
|
|
45
|
+
* line so an unbalanced backtick cannot swallow the rest of the body.
|
|
46
|
+
*
|
|
47
|
+
* Deliberately diverges from `assess-collision-detect.ts:stripCodeBlocksAndComments`,
|
|
48
|
+
* which keeps inline spans: its PATH_REGEX only matches backtick-wrapped paths,
|
|
49
|
+
* so stripping them there would find nothing. Same syntax, opposite meaning —
|
|
50
|
+
* a backticked marker here is an example, a backticked path there is the target.
|
|
51
|
+
* Keep the two separate (see PR #770).
|
|
52
|
+
*/
|
|
53
|
+
function stripCodeAndComments(body) {
|
|
54
|
+
return body
|
|
55
|
+
.replace(/```[\s\S]*?```/g, "")
|
|
56
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
57
|
+
.replace(/`[^`\n]*`/g, "");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse the issue numbers a body declares itself dependent on / blocked by,
|
|
61
|
+
* honoring only the requested `markers`. Deduped, order-preserving.
|
|
62
|
+
*
|
|
63
|
+
* Only line-leading markers count as declarations — see `buildMarkerRegex` for
|
|
64
|
+
* why mid-sentence prose mentions are deliberately ignored. Code blocks, inline
|
|
65
|
+
* code spans, and HTML comments are stripped first.
|
|
66
|
+
*/
|
|
67
|
+
export function parseBodyDependencyMarkers(body, markers) {
|
|
68
|
+
if (markers.length === 0)
|
|
69
|
+
return [];
|
|
70
|
+
const cleaned = stripCodeAndComments(body);
|
|
71
|
+
const regex = buildMarkerRegex(markers);
|
|
72
|
+
const found = [];
|
|
73
|
+
for (const m of cleaned.matchAll(regex)) {
|
|
74
|
+
const n = parseInt(m[1], 10);
|
|
75
|
+
if (!isNaN(n) && !found.includes(n))
|
|
76
|
+
found.push(n);
|
|
77
|
+
}
|
|
78
|
+
return found;
|
|
79
|
+
}
|
|
@@ -98,6 +98,13 @@ export interface AgentPhaseResult {
|
|
|
98
98
|
export interface AgentDriver {
|
|
99
99
|
/** Human-readable name for logging */
|
|
100
100
|
name: string;
|
|
101
|
+
/**
|
|
102
|
+
* True when this driver executes phases by resolving slash-command skills
|
|
103
|
+
* from the project's `.claude/skills/` directory (claude-code). Drivers
|
|
104
|
+
* whose prompts do the work inline (aider's `driverOverrides` templates)
|
|
105
|
+
* return false, and the `run` skills pre-flight (#813) is skipped for them.
|
|
106
|
+
*/
|
|
107
|
+
resolvesSkills: boolean;
|
|
101
108
|
/** Execute a phase prompt and return structured result */
|
|
102
109
|
executePhase(prompt: string, config: AgentExecutionConfig): Promise<AgentPhaseResult>;
|
|
103
110
|
/** Check if this driver is available/configured */
|
|
@@ -9,6 +9,8 @@ import type { AgentDriver, AgentExecutionConfig, AgentPhaseResult, ResumeHandle
|
|
|
9
9
|
import type { AiderSettings } from "../../settings.js";
|
|
10
10
|
export declare class AiderDriver implements AgentDriver {
|
|
11
11
|
name: string;
|
|
12
|
+
/** Aider phases use inline `driverOverrides` prompts — no skills (#813). */
|
|
13
|
+
resolvesSkills: boolean;
|
|
12
14
|
private settings?;
|
|
13
15
|
constructor(settings?: AiderSettings);
|
|
14
16
|
/**
|
|
@@ -10,6 +10,8 @@ import { execSync } from "child_process";
|
|
|
10
10
|
import { RingBuffer } from "../ring-buffer.js";
|
|
11
11
|
export class AiderDriver {
|
|
12
12
|
name = "aider";
|
|
13
|
+
/** Aider phases use inline `driverOverrides` prompts — no skills (#813). */
|
|
14
|
+
resolvesSkills = false;
|
|
13
15
|
settings;
|
|
14
16
|
constructor(settings) {
|
|
15
17
|
this.settings = settings;
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import type { AgentDriver, AgentExecutionConfig, AgentPhaseResult, ResumeHandle } from "./agent-driver.js";
|
|
8
8
|
export declare class ClaudeCodeDriver implements AgentDriver {
|
|
9
9
|
name: string;
|
|
10
|
+
/** Phases run slash-command skills from `.claude/skills/` (#813). */
|
|
11
|
+
resolvesSkills: boolean;
|
|
10
12
|
/**
|
|
11
13
|
* Track session ID across calls so callers can implement resume.
|
|
12
14
|
* Set after each executePhase() call.
|
|
@@ -42,12 +44,31 @@ export declare class ClaudeCodeDriver implements AgentDriver {
|
|
|
42
44
|
* RateLimitError would wrongly re-enable the retry / MCP-fallback path. When
|
|
43
45
|
* the `rate_limit_event` is itself a billing failure its richer metadata
|
|
44
46
|
* (`canUserPurchaseCredits`, etc.) is preserved.
|
|
47
|
+
*
|
|
48
|
+
* Counter-exception (#860): when the `rate_limit_event` carries the FULL
|
|
49
|
+
* captured subscription-window shape — waitable window evidence (recognized
|
|
50
|
+
* window type + future `resetsAt`) *plus* the `out_of_credits` marker — the
|
|
51
|
+
* richer structured signal wins over the bare `billing_error` enum. A
|
|
52
|
+
* subscription plan's five-hour exhaustion surfaces assistant-side as a
|
|
53
|
+
* billing error, and overriding on that enum here would strip the metadata
|
|
54
|
+
* `--auto-wait` needs and re-inert the feature this classification exists
|
|
55
|
+
* to enable. Deliberately no wider than the evidenced shape: a pure
|
|
56
|
+
* throttle event (window, no billing markers) beside a `billing_error`
|
|
57
|
+
* enum keeps the #732 rule — billing wins — because there the two signals
|
|
58
|
+
* genuinely conflict and no capture justifies trusting the throttle.
|
|
45
59
|
*/
|
|
46
60
|
private buildStructuredError;
|
|
47
61
|
/**
|
|
48
62
|
* Map the SDK's assistant/api-retry error enum to a typed error. Only
|
|
49
63
|
* rate-limit / billing variants are mapped; other variants (auth, etc.)
|
|
50
64
|
* return undefined and defer to the existing classification path.
|
|
65
|
+
*
|
|
66
|
+
* The assistant-error channel carries no `resetsAt`/`rateLimitType` (the
|
|
67
|
+
* enum is the whole signal — `SDKAssistantMessageError` is a bare string),
|
|
68
|
+
* so the originating variant is recorded in metadata to keep `rate_limit`
|
|
69
|
+
* and `overloaded` distinguishable downstream (#761 AC-9). With no
|
|
70
|
+
* `resetsAt`, these errors are treated as transient by the executor's
|
|
71
|
+
* window-exhaustion check — the mandatory metadata-absent fallback.
|
|
51
72
|
*/
|
|
52
73
|
private errorFromAssistantError;
|
|
53
74
|
private buildResumeHandle;
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
8
8
|
import { getMcpServersConfig } from "../../system.js";
|
|
9
|
-
import { RateLimitError, BillingError, createRateLimitError, isRateLimitFailureInfo, } from "../../errors.js";
|
|
9
|
+
import { RateLimitError, BillingError, createRateLimitError, isWaitableWindow, isRateLimitFailureInfo, } from "../../errors.js";
|
|
10
10
|
import { RingBuffer } from "../ring-buffer.js";
|
|
11
11
|
export class ClaudeCodeDriver {
|
|
12
12
|
name = "claude-code";
|
|
13
|
+
/** Phases run slash-command skills from `.claude/skills/` (#813). */
|
|
14
|
+
resolvesSkills = true;
|
|
13
15
|
/**
|
|
14
16
|
* Track session ID across calls so callers can implement resume.
|
|
15
17
|
* Set after each executePhase() call.
|
|
@@ -228,10 +230,22 @@ export class ClaudeCodeDriver {
|
|
|
228
230
|
clearTimeout(timeoutId);
|
|
229
231
|
const error = err instanceof Error ? err.message : String(err);
|
|
230
232
|
if (error.includes("abort") || error.includes("AbortError")) {
|
|
233
|
+
// A rate limit can manifest as a hang rather than a stream event
|
|
234
|
+
// reaching the result: the SDK stalls, the phase timeout fires, and
|
|
235
|
+
// the abort lands here. Only failure-grade signals are ever captured
|
|
236
|
+
// (see the stream loop), so attaching them cannot mask a genuine
|
|
237
|
+
// timeout — with no signal, `abortStructuredError` is undefined and
|
|
238
|
+
// the bare timeout message survives unchanged (#761 AC-1). Without
|
|
239
|
+
// this, the captured `rateLimitInfo` was discarded and downstream
|
|
240
|
+
// classification saw only `Timeout after Ns`, sending a closed
|
|
241
|
+
// rate-limit window into the full retry + MCP-fallback ladder.
|
|
242
|
+
const abortStructuredError = this.buildStructuredError(rateLimitInfo, assistantError, apiRetryError);
|
|
231
243
|
return {
|
|
232
244
|
success: false,
|
|
233
245
|
output: capturedOutput,
|
|
234
|
-
error:
|
|
246
|
+
error: abortStructuredError?.message ??
|
|
247
|
+
`Timeout after ${config.phaseTimeout}s`,
|
|
248
|
+
structuredError: abortStructuredError,
|
|
235
249
|
stderrTail: stderrBuffer.getLines(),
|
|
236
250
|
stdoutTail: stdoutBuffer.getLines(),
|
|
237
251
|
};
|
|
@@ -239,8 +253,7 @@ export class ClaudeCodeDriver {
|
|
|
239
253
|
// If the stream surfaced a failure-grade rate-limit/billing signal before
|
|
240
254
|
// throwing, prefer that typed cause (#732) over the raw thrown message — a
|
|
241
255
|
// mid-stream throw after a *rejected* rate_limit_event is very likely the
|
|
242
|
-
// proximate cause.
|
|
243
|
-
// timeout is never masked by a stale rate-limit signal.
|
|
256
|
+
// proximate cause.
|
|
244
257
|
const structuredError = this.buildStructuredError(rateLimitInfo, assistantError, apiRetryError);
|
|
245
258
|
const stderrSuffix = capturedStderr
|
|
246
259
|
? `\nStderr: ${capturedStderr.slice(0, 500)}`
|
|
@@ -271,11 +284,27 @@ export class ClaudeCodeDriver {
|
|
|
271
284
|
* RateLimitError would wrongly re-enable the retry / MCP-fallback path. When
|
|
272
285
|
* the `rate_limit_event` is itself a billing failure its richer metadata
|
|
273
286
|
* (`canUserPurchaseCredits`, etc.) is preserved.
|
|
287
|
+
*
|
|
288
|
+
* Counter-exception (#860): when the `rate_limit_event` carries the FULL
|
|
289
|
+
* captured subscription-window shape — waitable window evidence (recognized
|
|
290
|
+
* window type + future `resetsAt`) *plus* the `out_of_credits` marker — the
|
|
291
|
+
* richer structured signal wins over the bare `billing_error` enum. A
|
|
292
|
+
* subscription plan's five-hour exhaustion surfaces assistant-side as a
|
|
293
|
+
* billing error, and overriding on that enum here would strip the metadata
|
|
294
|
+
* `--auto-wait` needs and re-inert the feature this classification exists
|
|
295
|
+
* to enable. Deliberately no wider than the evidenced shape: a pure
|
|
296
|
+
* throttle event (window, no billing markers) beside a `billing_error`
|
|
297
|
+
* enum keeps the #732 rule — billing wins — because there the two signals
|
|
298
|
+
* genuinely conflict and no capture justifies trusting the throttle.
|
|
274
299
|
*/
|
|
275
300
|
buildStructuredError(rateLimitInfo, assistantError, apiRetryError) {
|
|
276
301
|
if (rateLimitInfo) {
|
|
277
302
|
const err = createRateLimitError(rateLimitInfo);
|
|
278
|
-
|
|
303
|
+
const isCapturedSubscriptionShape = isWaitableWindow(rateLimitInfo) &&
|
|
304
|
+
rateLimitInfo.overageDisabledReason === "out_of_credits";
|
|
305
|
+
if (err instanceof RateLimitError &&
|
|
306
|
+
assistantError === "billing_error" &&
|
|
307
|
+
!isCapturedSubscriptionShape) {
|
|
279
308
|
return new BillingError("Billing error");
|
|
280
309
|
}
|
|
281
310
|
return err;
|
|
@@ -287,15 +316,28 @@ export class ClaudeCodeDriver {
|
|
|
287
316
|
* Map the SDK's assistant/api-retry error enum to a typed error. Only
|
|
288
317
|
* rate-limit / billing variants are mapped; other variants (auth, etc.)
|
|
289
318
|
* return undefined and defer to the existing classification path.
|
|
319
|
+
*
|
|
320
|
+
* The assistant-error channel carries no `resetsAt`/`rateLimitType` (the
|
|
321
|
+
* enum is the whole signal — `SDKAssistantMessageError` is a bare string),
|
|
322
|
+
* so the originating variant is recorded in metadata to keep `rate_limit`
|
|
323
|
+
* and `overloaded` distinguishable downstream (#761 AC-9). With no
|
|
324
|
+
* `resetsAt`, these errors are treated as transient by the executor's
|
|
325
|
+
* window-exhaustion check — the mandatory metadata-absent fallback.
|
|
290
326
|
*/
|
|
291
327
|
errorFromAssistantError(error) {
|
|
292
328
|
switch (error) {
|
|
293
329
|
case "billing_error":
|
|
294
|
-
return new BillingError("Billing error"
|
|
330
|
+
return new BillingError("Billing error", {
|
|
331
|
+
assistantError: "billing_error",
|
|
332
|
+
});
|
|
295
333
|
case "rate_limit":
|
|
296
|
-
return new RateLimitError("Rate limited"
|
|
334
|
+
return new RateLimitError("Rate limited", {
|
|
335
|
+
assistantError: "rate_limit",
|
|
336
|
+
});
|
|
297
337
|
case "overloaded":
|
|
298
|
-
return new RateLimitError("API overloaded"
|
|
338
|
+
return new RateLimitError("API overloaded", {
|
|
339
|
+
assistantError: "overloaded",
|
|
340
|
+
});
|
|
299
341
|
default:
|
|
300
342
|
return undefined;
|
|
301
343
|
}
|
|
@@ -5,8 +5,15 @@
|
|
|
5
5
|
* categories. Exit codes are the primary signal; stderr patterns are secondary.
|
|
6
6
|
*/
|
|
7
7
|
import { SequantError } from "../errors.js";
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* All recognized error categories (kept for backwards compatibility).
|
|
10
|
+
*
|
|
11
|
+
* `rate_limit` / `billing` (#761 AC-6) only arise from the driver's structured
|
|
12
|
+
* errors — `classifyError` never produces them, since stderr text cannot
|
|
13
|
+
* distinguish a window-exhausted limit from a transient 429 (`api_error`).
|
|
14
|
+
* Keep in sync with the inline category enum in `run-log-schema.ts`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ERROR_CATEGORIES: readonly ["context_overflow", "api_error", "hook_failure", "build_error", "timeout", "rate_limit", "billing", "unknown"];
|
|
10
17
|
export type ErrorCategory = (typeof ERROR_CATEGORIES)[number];
|
|
11
18
|
/**
|
|
12
19
|
* Map from error type name to legacy category string.
|
|
@@ -5,13 +5,22 @@
|
|
|
5
5
|
* categories. Exit codes are the primary signal; stderr patterns are secondary.
|
|
6
6
|
*/
|
|
7
7
|
import { ContextOverflowError, ApiError, HookFailureError, BuildError, TimeoutError, SubprocessError, } from "../errors.js";
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* All recognized error categories (kept for backwards compatibility).
|
|
10
|
+
*
|
|
11
|
+
* `rate_limit` / `billing` (#761 AC-6) only arise from the driver's structured
|
|
12
|
+
* errors — `classifyError` never produces them, since stderr text cannot
|
|
13
|
+
* distinguish a window-exhausted limit from a transient 429 (`api_error`).
|
|
14
|
+
* Keep in sync with the inline category enum in `run-log-schema.ts`.
|
|
15
|
+
*/
|
|
9
16
|
export const ERROR_CATEGORIES = [
|
|
10
17
|
"context_overflow",
|
|
11
18
|
"api_error",
|
|
12
19
|
"hook_failure",
|
|
13
20
|
"build_error",
|
|
14
21
|
"timeout",
|
|
22
|
+
"rate_limit",
|
|
23
|
+
"billing",
|
|
15
24
|
"unknown",
|
|
16
25
|
];
|
|
17
26
|
/**
|
|
@@ -30,6 +39,10 @@ export function errorTypeToCategory(error) {
|
|
|
30
39
|
return "build_error";
|
|
31
40
|
case "TimeoutError":
|
|
32
41
|
return "timeout";
|
|
42
|
+
case "RateLimitError":
|
|
43
|
+
return "rate_limit";
|
|
44
|
+
case "BillingError":
|
|
45
|
+
return "billing";
|
|
33
46
|
default:
|
|
34
47
|
return "unknown";
|
|
35
48
|
}
|
|
@@ -18,18 +18,50 @@ export interface GitDiffStatsResult {
|
|
|
18
18
|
/** Total lines deleted across all files */
|
|
19
19
|
totalDeletions: number;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the ref diff stats should compare against (#878).
|
|
23
|
+
*
|
|
24
|
+
* Worktrees are created from `origin/<base>` (worktree-manager), but callers
|
|
25
|
+
* historically passed the bare branch name and the diff ran against the
|
|
26
|
+
* *local* ref. When local `<base>` lags the remote — routine, since nothing
|
|
27
|
+
* in the run path updates it — `<base>...HEAD` attributes commits the run
|
|
28
|
+
* never made (phantom filesModified).
|
|
29
|
+
*
|
|
30
|
+
* Candidates are the origin-qualified and bare forms of `baseBranch`
|
|
31
|
+
* (already-remote-qualified input keeps a single candidate). Among the
|
|
32
|
+
* candidates that resolve to a commit, pick the one nearest to HEAD
|
|
33
|
+
* (smallest `rev-list --count <cand>..HEAD`), preferring the
|
|
34
|
+
* origin-qualified form on a tie. Nearest-wins matches the worktree's
|
|
35
|
+
* actual creation point in both staleness directions: a stale local
|
|
36
|
+
* default branch (this issue) and a chain-mode worktree branched from a
|
|
37
|
+
* local base that is ahead of its pushed counterpart. Falls back to
|
|
38
|
+
* `baseBranch` verbatim when no candidate resolves (e.g. remote-less repo
|
|
39
|
+
* with a missing branch) — the diff then fails gracefully to empty, the
|
|
40
|
+
* pre-#878 behavior.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveDiffBase(worktreePath: string, baseBranch: string): string;
|
|
21
43
|
/**
|
|
22
44
|
* Get git commit SHA for a worktree (AC-2)
|
|
23
45
|
*
|
|
46
|
+
* When `baseRef` is provided, returns undefined if HEAD has no commits
|
|
47
|
+
* unique to it (#878) — a branch that never moved off its base would
|
|
48
|
+
* otherwise log the base tip as if it were the phase's commit. Callers
|
|
49
|
+
* recording plain "where is HEAD" markers (run start/end) omit `baseRef`.
|
|
50
|
+
*
|
|
24
51
|
* @param worktreePath - Path to the git worktree
|
|
25
|
-
* @
|
|
52
|
+
* @param baseRef - Optional resolved base ref (see resolveDiffBase); when
|
|
53
|
+
* given, a HEAD with zero commits past it yields undefined
|
|
54
|
+
* @returns The current HEAD commit SHA, or undefined on error / no unique
|
|
55
|
+
* commits
|
|
26
56
|
*/
|
|
27
|
-
export declare function getCommitHash(worktreePath: string): string | undefined;
|
|
57
|
+
export declare function getCommitHash(worktreePath: string, baseRef?: string): string | undefined;
|
|
28
58
|
/**
|
|
29
59
|
* Get git diff statistics for a worktree (AC-1, AC-3, AC-4)
|
|
30
60
|
*
|
|
31
61
|
* Efficiently captures both filesModified and fileDiffStats using
|
|
32
|
-
* minimal git commands.
|
|
62
|
+
* minimal git commands. The base is resolved via resolveDiffBase (#878) so
|
|
63
|
+
* the comparison targets the ref the worktree was actually created from
|
|
64
|
+
* (origin/<base> in the common case) rather than a possibly-stale local ref.
|
|
33
65
|
*
|
|
34
66
|
* @param worktreePath - Path to the git worktree
|
|
35
67
|
* @param baseBranch - Branch to compare against (default: "main")
|