sequant 2.10.0 → 2.12.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 +19 -2
- package/dist/bin/cli.js +47 -2
- 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/hooks/pre-tool.sh +331 -12
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +7 -18
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +5 -1
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +62 -8
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +187 -28
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +127 -23
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +130 -13
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +306 -8
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +79 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +40 -20
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +14 -1
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +23 -6
- package/dist/src/commands/doctor.js +20 -18
- package/dist/src/commands/locks.d.ts +20 -1
- package/dist/src/commands/locks.js +206 -4
- package/dist/src/commands/ready.d.ts +6 -0
- package/dist/src/commands/ready.js +19 -1
- package/dist/src/commands/run-display.js +1 -0
- package/dist/src/commands/worktree.d.ts +31 -0
- package/dist/src/commands/worktree.js +95 -0
- package/dist/src/lib/ac-linter.js +26 -0
- package/dist/src/lib/ac-parser.d.ts +40 -0
- package/dist/src/lib/ac-parser.js +202 -16
- package/dist/src/lib/cli-flags.d.ts +23 -0
- package/dist/src/lib/cli-flags.js +43 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +2 -0
- package/dist/src/lib/cli-ui/run-renderer.js +7 -1
- 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 +6 -3
- package/dist/src/lib/locks/index.js +4 -2
- package/dist/src/lib/locks/lock-manager.d.ts +81 -1
- package/dist/src/lib/locks/lock-manager.js +230 -5
- package/dist/src/lib/locks/types.d.ts +72 -0
- package/dist/src/lib/locks/types.js +28 -0
- package/dist/src/lib/markdown-fence.d.ts +24 -0
- package/dist/src/lib/markdown-fence.js +51 -0
- package/dist/src/lib/mcp-config.d.ts +24 -0
- package/dist/src/lib/mcp-config.js +51 -0
- package/dist/src/lib/scope/analyzer.d.ts +4 -0
- package/dist/src/lib/scope/analyzer.js +7 -1
- package/dist/src/lib/settings.d.ts +111 -1
- package/dist/src/lib/settings.js +59 -0
- package/dist/src/lib/system.d.ts +7 -3
- package/dist/src/lib/system.js +7 -3
- package/dist/src/lib/test-tautology-detector.d.ts +4 -3
- package/dist/src/lib/test-tautology-detector.js +147 -40
- package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
- package/dist/src/lib/workflow/batch-executor.js +154 -23
- package/dist/src/lib/workflow/config-resolver.d.ts +25 -0
- package/dist/src/lib/workflow/config-resolver.js +90 -0
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +22 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +14 -3
- 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/log-writer.d.ts +10 -1
- package/dist/src/lib/workflow/log-writer.js +20 -0
- package/dist/src/lib/workflow/metrics-schema.d.ts +49 -6
- 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/mutation-marker.d.ts +86 -0
- package/dist/src/lib/workflow/mutation-marker.js +97 -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 +17 -0
- package/dist/src/lib/workflow/phase-executor.js +60 -4
- package/dist/src/lib/workflow/qa-gaps-marker.d.ts +38 -0
- package/dist/src/lib/workflow/qa-gaps-marker.js +66 -0
- package/dist/src/lib/workflow/ready-gate.d.ts +53 -1
- package/dist/src/lib/workflow/ready-gate.js +105 -14
- package/dist/src/lib/workflow/run-log-schema.d.ts +175 -0
- package/dist/src/lib/workflow/run-log-schema.js +71 -1
- package/dist/src/lib/workflow/run-orchestrator.js +27 -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-schema.d.ts +5 -1
- package/dist/src/lib/workflow/state-schema.js +8 -1
- package/dist/src/lib/workflow/types.d.ts +78 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +8 -1
- package/dist/src/lib/workflow/worktree-manager.js +9 -1
- package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
- package/dist/src/lib/workflow/worktree-resolver.js +126 -0
- package/package.json +4 -3
- package/templates/hooks/pre-tool.sh +331 -12
- package/templates/scripts/cleanup-worktree.sh +36 -15
- package/templates/scripts/new-feature.sh +25 -19
- package/templates/skills/_shared/references/subagent-types.md +7 -18
- package/templates/skills/assess/SKILL.md +5 -1
- package/templates/skills/exec/SKILL.md +62 -8
- package/templates/skills/fullsolve/SKILL.md +187 -28
- package/templates/skills/loop/SKILL.md +127 -23
- package/templates/skills/merger/SKILL.md +130 -13
- package/templates/skills/qa/SKILL.md +306 -8
- package/templates/skills/release/SKILL.md +79 -0
- package/templates/skills/spec/SKILL.md +40 -20
- 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/templates/agents/sequant-explorer.md +0 -24
|
@@ -42,6 +42,33 @@ export const QaVerdictSchema = z.enum([
|
|
|
42
42
|
"AC_NOT_MET",
|
|
43
43
|
"NEEDS_VERIFICATION",
|
|
44
44
|
]);
|
|
45
|
+
/**
|
|
46
|
+
* Source that produced the resolved spec→run phase recommendation (#921).
|
|
47
|
+
*
|
|
48
|
+
* Ordered by resolution priority: a durable structured marker in the spec's
|
|
49
|
+
* GitHub comment beats the same comment's prose section, which beats the
|
|
50
|
+
* spec agent's ephemeral chat text, which beats label-based guessing.
|
|
51
|
+
*/
|
|
52
|
+
export const SpecRecommendationSourceSchema = z.enum([
|
|
53
|
+
"marker",
|
|
54
|
+
"comment-prose",
|
|
55
|
+
"chat",
|
|
56
|
+
"label-fallback",
|
|
57
|
+
]);
|
|
58
|
+
/**
|
|
59
|
+
* Resolved spec→run phase recommendation, recorded on the issue log so
|
|
60
|
+
* fallback frequency is auditable (#921 AC-4). Additive/optional — absent on
|
|
61
|
+
* runs that never reached spec resolution (e.g. spec failed) or predate this
|
|
62
|
+
* field, keeping the persisted-log schema stable at `version: 1`.
|
|
63
|
+
*/
|
|
64
|
+
export const SpecRecommendationSchema = z.object({
|
|
65
|
+
/** Which step in the resolution chain produced this result */
|
|
66
|
+
source: SpecRecommendationSourceSchema,
|
|
67
|
+
/** Resolved phases, spec excluded (spec already ran) */
|
|
68
|
+
phases: z.array(PhaseSchema),
|
|
69
|
+
/** Whether the quality loop should be enabled */
|
|
70
|
+
qualityLoop: z.boolean(),
|
|
71
|
+
});
|
|
45
72
|
/**
|
|
46
73
|
* File diff statistics for a single file (AC-3)
|
|
47
74
|
*/
|
|
@@ -82,7 +109,7 @@ export const ErrorContextSchema = z.object({
|
|
|
82
109
|
/**
|
|
83
110
|
* Classified error category (legacy, kept for backwards compatibility).
|
|
84
111
|
* Keep in sync with `ERROR_CATEGORIES` in `error-classifier.ts` —
|
|
85
|
-
* `rate_limit` / `billing` added by #761 AC-6.
|
|
112
|
+
* `rate_limit` / `billing` added by #761 AC-6, `pr_creation` by #920.
|
|
86
113
|
*/
|
|
87
114
|
category: z.enum([
|
|
88
115
|
"context_overflow",
|
|
@@ -92,6 +119,7 @@ export const ErrorContextSchema = z.object({
|
|
|
92
119
|
"timeout",
|
|
93
120
|
"rate_limit",
|
|
94
121
|
"billing",
|
|
122
|
+
"pr_creation",
|
|
95
123
|
"unknown",
|
|
96
124
|
]),
|
|
97
125
|
/** Typed error class name (AC-8), e.g. "ApiError", "BuildError" */
|
|
@@ -101,6 +129,39 @@ export const ErrorContextSchema = z.object({
|
|
|
101
129
|
/** Whether this error type is retryable (AC-9) */
|
|
102
130
|
isRetryable: z.boolean().optional(),
|
|
103
131
|
});
|
|
132
|
+
/**
|
|
133
|
+
* Finite taxonomy for a single gap finding surfaced by `/qa` (#937).
|
|
134
|
+
*
|
|
135
|
+
* Replaces the lossy prose-scrape gap channel (`parseListSection` against
|
|
136
|
+
* `**Issues:**`/`**Gaps**` headers, which misses AC-table NOT_MET rows, §6d
|
|
137
|
+
* Adversarial Re-Read findings, and §5 Risk Assessment). `evidence` is
|
|
138
|
+
* required so a finding can't be speculative — see #608's 0%-action-rate
|
|
139
|
+
* result for open-ended "what might we be missing" findings.
|
|
140
|
+
*/
|
|
141
|
+
export const GapCategorySchema = z.enum([
|
|
142
|
+
"requirement_gap",
|
|
143
|
+
"dependency_gap",
|
|
144
|
+
"test_gap",
|
|
145
|
+
"repository_gap",
|
|
146
|
+
"risk_gap",
|
|
147
|
+
"execution_gap",
|
|
148
|
+
]);
|
|
149
|
+
export const GapActionSchema = z.enum([
|
|
150
|
+
"fix_now",
|
|
151
|
+
"document",
|
|
152
|
+
"pause_for_human",
|
|
153
|
+
]);
|
|
154
|
+
export const GapFindingSchema = z.object({
|
|
155
|
+
category: GapCategorySchema,
|
|
156
|
+
/** Concrete observation grounding the finding — never speculation. */
|
|
157
|
+
evidence: z.string().min(1),
|
|
158
|
+
description: z.string().min(1),
|
|
159
|
+
recommendedAction: GapActionSchema,
|
|
160
|
+
/** ACs this finding relates to, e.g. ["AC-3"]. */
|
|
161
|
+
affectedAcs: z.array(z.string()).optional(),
|
|
162
|
+
/** True when the finding overlaps one of the issue's Non-Goals. */
|
|
163
|
+
nonGoal: z.boolean().optional(),
|
|
164
|
+
});
|
|
104
165
|
/**
|
|
105
166
|
* Condensed QA verdict summary for structured log output (#434).
|
|
106
167
|
*
|
|
@@ -117,6 +178,13 @@ export const QaSummarySchema = z.object({
|
|
|
117
178
|
gaps: z.array(z.string()),
|
|
118
179
|
/** List of improvement suggestions from QA */
|
|
119
180
|
suggestions: z.array(z.string()),
|
|
181
|
+
/**
|
|
182
|
+
* Structured gap findings parsed from the `SEQUANT_QA_GAPS` marker (#937).
|
|
183
|
+
* Present only when the marker was found and validated; `gaps` above
|
|
184
|
+
* always carries the union of marker + prose descriptions (dedupe'd) so
|
|
185
|
+
* marker-unaware consumers never regress.
|
|
186
|
+
*/
|
|
187
|
+
findings: z.array(GapFindingSchema).optional(),
|
|
120
188
|
});
|
|
121
189
|
/**
|
|
122
190
|
* Log entry for a single phase execution
|
|
@@ -198,6 +266,8 @@ export const IssueLogSchema = z.object({
|
|
|
198
266
|
prNumber: z.number().int().positive().optional(),
|
|
199
267
|
/** PR URL if created after successful QA */
|
|
200
268
|
prUrl: z.string().optional(),
|
|
269
|
+
/** How the spec→run phase recommendation was resolved (#921 AC-4) */
|
|
270
|
+
specRecommendation: SpecRecommendationSchema.optional(),
|
|
201
271
|
});
|
|
202
272
|
/**
|
|
203
273
|
* Run configuration
|
|
@@ -1207,6 +1207,29 @@ export class RunOrchestrator {
|
|
|
1207
1207
|
// issue is the halting one; in parallel runs it is the earliest-listed
|
|
1208
1208
|
// failure. Undefined on success — the field is omitted from the record.
|
|
1209
1209
|
const failureCategory = results.find((r) => !r.success && r.failureCategory)?.failureCategory;
|
|
1210
|
+
// #915: one entry per escalated phase execution, from both retry sites —
|
|
1211
|
+
// the outer quality loop (`phaseResults[].escalatedEffort`) and the
|
|
1212
|
+
// `--ready-gate` QA-pass loop (`readyGate.effortEscalations`).
|
|
1213
|
+
const effortEscalations = [
|
|
1214
|
+
...results.flatMap((r) => r.phaseResults
|
|
1215
|
+
.filter((p) => p.escalatedEffort)
|
|
1216
|
+
.map((p) => ({
|
|
1217
|
+
phase: p.phase,
|
|
1218
|
+
base: p.escalatedEffort.base,
|
|
1219
|
+
escalated: p.escalatedEffort.escalated,
|
|
1220
|
+
}))),
|
|
1221
|
+
...results.flatMap((r) => r.readyGate?.effortEscalations ?? []),
|
|
1222
|
+
];
|
|
1223
|
+
// #915: the outer quality loop already logs its own escalations live at
|
|
1224
|
+
// the batch-executor dispatch site — only the `--ready-gate` QA-pass loop
|
|
1225
|
+
// has no equivalent live print, so surface those here.
|
|
1226
|
+
if (config.verbose) {
|
|
1227
|
+
for (const r of results) {
|
|
1228
|
+
for (const e of r.readyGate?.effortEscalations ?? []) {
|
|
1229
|
+
console.log(chalk.gray(` effort: ${e.base} → ${e.escalated} (ready-gate retry, #${r.issueNumber})`));
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1210
1233
|
await metricsWriter.recordRun({
|
|
1211
1234
|
issues: issueNumbers,
|
|
1212
1235
|
phases: Array.from(allPhases),
|
|
@@ -1215,6 +1238,10 @@ export class RunOrchestrator {
|
|
|
1215
1238
|
model: process.env.ANTHROPIC_MODEL ?? "opus",
|
|
1216
1239
|
flags: cliFlags,
|
|
1217
1240
|
failureCategory,
|
|
1241
|
+
// #914: resolved per-phase model/effort, when any phase had one.
|
|
1242
|
+
phasePolicies: config.phasePolicies,
|
|
1243
|
+
// #915: escalated tiers, when any phase execution escalated.
|
|
1244
|
+
effortEscalations,
|
|
1218
1245
|
metrics: {
|
|
1219
1246
|
tokensUsed: tokenUsage.tokensUsed,
|
|
1220
1247
|
filesChanged: totalFilesChanged,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured resolution of the spec→run phase recommendation (#921).
|
|
3
|
+
*
|
|
4
|
+
* `parseRecommendedWorkflow` (phase-mapper.ts) regexes the spec agent's
|
|
5
|
+
* *ephemeral chat text* for a `## Recommended Workflow` section. When the
|
|
6
|
+
* spec agent posts its plan via a body file instead of restating it in
|
|
7
|
+
* chat, that regex has nothing to match and the run silently falls back to
|
|
8
|
+
* label-based phase detection — dropping any recommended phase the label
|
|
9
|
+
* fallback can never produce (e.g. `testgen`). See #814.
|
|
10
|
+
*
|
|
11
|
+
* This module resolves the recommendation through an ordered chain, each
|
|
12
|
+
* step falling through loudly to the next on failure:
|
|
13
|
+
*
|
|
14
|
+
* 1. `marker` — the durable `<!-- SEQUANT_SPEC: {json} --> `
|
|
15
|
+
* comment marker (this issue's fix)
|
|
16
|
+
* 2. `comment-prose` — the same regex as `chat`, applied to the spec
|
|
17
|
+
* plan's GitHub comment body instead of chat text
|
|
18
|
+
* 3. `chat` — `parseRecommendedWorkflow` over the agent's
|
|
19
|
+
* captured chat output (existing behavior)
|
|
20
|
+
* 4. `label-fallback` — `detectPhasesFromLabels` (existing behavior)
|
|
21
|
+
*
|
|
22
|
+
* The durable comment marker is the system's existing idiom — see
|
|
23
|
+
* `SEQUANT_PHASE` in `phase-detection.ts` and the `/assess` HTML markers in
|
|
24
|
+
* `assess-comment-parser.ts`.
|
|
25
|
+
*/
|
|
26
|
+
import type { Phase } from "./types.js";
|
|
27
|
+
import { GitHubProvider } from "./platforms/github.js";
|
|
28
|
+
import type { SpecRecommendationSource } from "./run-log-schema.js";
|
|
29
|
+
export interface ResolvedSpecRecommendation {
|
|
30
|
+
phases: Phase[];
|
|
31
|
+
qualityLoop: boolean;
|
|
32
|
+
source: SpecRecommendationSource;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract and validate the latest `SEQUANT_SPEC` marker across a set of
|
|
36
|
+
* comment bodies (oldest-to-newest order, matching `gh`'s natural order).
|
|
37
|
+
*
|
|
38
|
+
* Returns `null` when no marker is present at all. Returns `null` and logs a
|
|
39
|
+
* visible warning when the latest marker is malformed JSON, fails schema
|
|
40
|
+
* validation, or names a phase the registry doesn't recognize — per AC-2,
|
|
41
|
+
* an invalid marker falls through to the next resolution step rather than
|
|
42
|
+
* silently dropping the recommendation or silently ignoring the bad phase.
|
|
43
|
+
*
|
|
44
|
+
* Only the *latest* marker is considered (last comment, last match within
|
|
45
|
+
* it) — a re-spec's marker supersedes an earlier one, matching how a human
|
|
46
|
+
* reader would interpret the thread.
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseSpecMarker(commentBodies: string[]): {
|
|
49
|
+
phases: Phase[];
|
|
50
|
+
qualityLoop: boolean;
|
|
51
|
+
} | null;
|
|
52
|
+
export interface ResolveSpecRecommendationInput {
|
|
53
|
+
/** The spec agent's captured chat output (existing `chat` fallback). */
|
|
54
|
+
chatOutput: string;
|
|
55
|
+
/** GitHub issue number, used to fetch its comments for the marker/prose steps. */
|
|
56
|
+
issueNumber: number;
|
|
57
|
+
/** Issue labels, used for the final label-based fallback. */
|
|
58
|
+
labels: string[];
|
|
59
|
+
/**
|
|
60
|
+
* Injectable for tests — defaults to a real `GitHubProvider`. Comment
|
|
61
|
+
* fetch failures are already handled by `fetchIssueCommentBodiesSync`
|
|
62
|
+
* (returns `[]`), so the chain degrades to `chat`/`label-fallback`
|
|
63
|
+
* gracefully when GitHub is unreachable.
|
|
64
|
+
*/
|
|
65
|
+
githubProvider?: Pick<GitHubProvider, "fetchIssueCommentBodiesSync">;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the spec→run phase recommendation through the ordered chain:
|
|
69
|
+
* comment-marker → comment-prose → chat-text → label-fallback.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveSpecRecommendation(input: ResolveSpecRecommendationInput): ResolvedSpecRecommendation;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured resolution of the spec→run phase recommendation (#921).
|
|
3
|
+
*
|
|
4
|
+
* `parseRecommendedWorkflow` (phase-mapper.ts) regexes the spec agent's
|
|
5
|
+
* *ephemeral chat text* for a `## Recommended Workflow` section. When the
|
|
6
|
+
* spec agent posts its plan via a body file instead of restating it in
|
|
7
|
+
* chat, that regex has nothing to match and the run silently falls back to
|
|
8
|
+
* label-based phase detection — dropping any recommended phase the label
|
|
9
|
+
* fallback can never produce (e.g. `testgen`). See #814.
|
|
10
|
+
*
|
|
11
|
+
* This module resolves the recommendation through an ordered chain, each
|
|
12
|
+
* step falling through loudly to the next on failure:
|
|
13
|
+
*
|
|
14
|
+
* 1. `marker` — the durable `<!-- SEQUANT_SPEC: {json} --> `
|
|
15
|
+
* comment marker (this issue's fix)
|
|
16
|
+
* 2. `comment-prose` — the same regex as `chat`, applied to the spec
|
|
17
|
+
* plan's GitHub comment body instead of chat text
|
|
18
|
+
* 3. `chat` — `parseRecommendedWorkflow` over the agent's
|
|
19
|
+
* captured chat output (existing behavior)
|
|
20
|
+
* 4. `label-fallback` — `detectPhasesFromLabels` (existing behavior)
|
|
21
|
+
*
|
|
22
|
+
* The durable comment marker is the system's existing idiom — see
|
|
23
|
+
* `SEQUANT_PHASE` in `phase-detection.ts` and the `/assess` HTML markers in
|
|
24
|
+
* `assess-comment-parser.ts`.
|
|
25
|
+
*/
|
|
26
|
+
import chalk from "chalk";
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
import { phaseRegistry } from "./phase-registry.js";
|
|
29
|
+
import { GitHubProvider } from "./platforms/github.js";
|
|
30
|
+
import { stripMarkdownCode } from "./phase-detection.js";
|
|
31
|
+
import { parseRecommendedWorkflow, detectPhasesFromLabels, } from "./phase-mapper.js";
|
|
32
|
+
/** Regex to extract the SEQUANT_SPEC marker JSON from an HTML comment. */
|
|
33
|
+
const SPEC_MARKER_REGEX = /<!-- SEQUANT_SPEC: (\{[^}]+\}) -->/g;
|
|
34
|
+
/**
|
|
35
|
+
* Structural shape of the marker JSON, before phase names are checked
|
|
36
|
+
* against the phase registry. `qualityLoop` defaults to `false` when
|
|
37
|
+
* omitted, matching `parseRecommendedWorkflow`'s prose-parsing default.
|
|
38
|
+
*/
|
|
39
|
+
const SpecMarkerJsonSchema = z.object({
|
|
40
|
+
phases: z.array(z.string()).min(1),
|
|
41
|
+
qualityLoop: z.boolean().optional().default(false),
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Extract and validate the latest `SEQUANT_SPEC` marker across a set of
|
|
45
|
+
* comment bodies (oldest-to-newest order, matching `gh`'s natural order).
|
|
46
|
+
*
|
|
47
|
+
* Returns `null` when no marker is present at all. Returns `null` and logs a
|
|
48
|
+
* visible warning when the latest marker is malformed JSON, fails schema
|
|
49
|
+
* validation, or names a phase the registry doesn't recognize — per AC-2,
|
|
50
|
+
* an invalid marker falls through to the next resolution step rather than
|
|
51
|
+
* silently dropping the recommendation or silently ignoring the bad phase.
|
|
52
|
+
*
|
|
53
|
+
* Only the *latest* marker is considered (last comment, last match within
|
|
54
|
+
* it) — a re-spec's marker supersedes an earlier one, matching how a human
|
|
55
|
+
* reader would interpret the thread.
|
|
56
|
+
*/
|
|
57
|
+
export function parseSpecMarker(commentBodies) {
|
|
58
|
+
let latestRaw = null;
|
|
59
|
+
for (const body of commentBodies) {
|
|
60
|
+
const stripped = stripMarkdownCode(body);
|
|
61
|
+
SPEC_MARKER_REGEX.lastIndex = 0;
|
|
62
|
+
let match;
|
|
63
|
+
while ((match = SPEC_MARKER_REGEX.exec(stripped)) !== null) {
|
|
64
|
+
latestRaw = match[1];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (latestRaw === null) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
let parsedJson;
|
|
71
|
+
try {
|
|
72
|
+
parsedJson = JSON.parse(latestRaw);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
console.log(chalk.yellow(` ⚠ SEQUANT_SPEC marker is not valid JSON — falling through to comment prose`));
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const result = SpecMarkerJsonSchema.safeParse(parsedJson);
|
|
79
|
+
if (!result.success) {
|
|
80
|
+
console.log(chalk.yellow(` ⚠ SEQUANT_SPEC marker failed schema validation — falling through to comment prose`));
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const unknownPhases = result.data.phases.filter((name) => !phaseRegistry.has(name));
|
|
84
|
+
if (unknownPhases.length > 0) {
|
|
85
|
+
console.log(chalk.yellow(` ⚠ SEQUANT_SPEC marker names unknown phase(s): ${unknownPhases.join(", ")} — falling through to comment prose`));
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return { phases: result.data.phases, qualityLoop: result.data.qualityLoop };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Find the most recently posted comment containing a `## Recommended
|
|
92
|
+
* Workflow` section and parse it with the existing prose regex.
|
|
93
|
+
*
|
|
94
|
+
* Scans newest-to-oldest so a later spec re-run's comment takes precedence
|
|
95
|
+
* over an earlier one — same intuition as the marker step.
|
|
96
|
+
*/
|
|
97
|
+
function parseCommentProse(commentBodies) {
|
|
98
|
+
for (let i = commentBodies.length - 1; i >= 0; i--) {
|
|
99
|
+
const parsed = parseRecommendedWorkflow(commentBodies[i]);
|
|
100
|
+
if (parsed) {
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolve the spec→run phase recommendation through the ordered chain:
|
|
108
|
+
* comment-marker → comment-prose → chat-text → label-fallback.
|
|
109
|
+
*/
|
|
110
|
+
export function resolveSpecRecommendation(input) {
|
|
111
|
+
const github = input.githubProvider ?? new GitHubProvider();
|
|
112
|
+
const commentBodies = github.fetchIssueCommentBodiesSync(String(input.issueNumber));
|
|
113
|
+
let phases;
|
|
114
|
+
let qualityLoop;
|
|
115
|
+
let source;
|
|
116
|
+
const marker = parseSpecMarker(commentBodies);
|
|
117
|
+
const prose = marker ? null : parseCommentProse(commentBodies);
|
|
118
|
+
const chat = marker || prose || !input.chatOutput
|
|
119
|
+
? null
|
|
120
|
+
: parseRecommendedWorkflow(input.chatOutput);
|
|
121
|
+
if (marker) {
|
|
122
|
+
({ phases, qualityLoop } = marker);
|
|
123
|
+
source = "marker";
|
|
124
|
+
}
|
|
125
|
+
else if (prose) {
|
|
126
|
+
({ phases, qualityLoop } = prose);
|
|
127
|
+
source = "comment-prose";
|
|
128
|
+
}
|
|
129
|
+
else if (chat) {
|
|
130
|
+
({ phases, qualityLoop } = chat);
|
|
131
|
+
source = "chat";
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const fallback = detectPhasesFromLabels(input.labels);
|
|
135
|
+
phases = fallback.phases;
|
|
136
|
+
qualityLoop = fallback.qualityLoop;
|
|
137
|
+
source = "label-fallback";
|
|
138
|
+
}
|
|
139
|
+
// Spec already ran by the time this resolves — never re-include it,
|
|
140
|
+
// regardless of which step in the chain produced the result.
|
|
141
|
+
return { phases: phases.filter((p) => p !== "spec"), qualityLoop, source };
|
|
142
|
+
}
|
|
@@ -149,6 +149,7 @@ export declare const AcceptanceCriterionSchema: z.ZodObject<{
|
|
|
149
149
|
}>;
|
|
150
150
|
verifiedAt: z.ZodOptional<z.ZodString>;
|
|
151
151
|
notes: z.ZodOptional<z.ZodString>;
|
|
152
|
+
evidence: z.ZodOptional<z.ZodString>;
|
|
152
153
|
}, z.core.$strip>;
|
|
153
154
|
export type AcceptanceCriterion = z.infer<typeof AcceptanceCriterionSchema>;
|
|
154
155
|
/**
|
|
@@ -172,6 +173,7 @@ export declare const AcceptanceCriteriaSchema: z.ZodObject<{
|
|
|
172
173
|
}>;
|
|
173
174
|
verifiedAt: z.ZodOptional<z.ZodString>;
|
|
174
175
|
notes: z.ZodOptional<z.ZodString>;
|
|
176
|
+
evidence: z.ZodOptional<z.ZodString>;
|
|
175
177
|
}, z.core.$strip>>;
|
|
176
178
|
extractedAt: z.ZodString;
|
|
177
179
|
summary: z.ZodObject<{
|
|
@@ -281,6 +283,7 @@ export declare const IssueStateSchema: z.ZodObject<{
|
|
|
281
283
|
}>;
|
|
282
284
|
verifiedAt: z.ZodOptional<z.ZodString>;
|
|
283
285
|
notes: z.ZodOptional<z.ZodString>;
|
|
286
|
+
evidence: z.ZodOptional<z.ZodString>;
|
|
284
287
|
}, z.core.$strip>>;
|
|
285
288
|
extractedAt: z.ZodString;
|
|
286
289
|
summary: z.ZodObject<{
|
|
@@ -430,6 +433,7 @@ export declare const WorkflowStateSchema: z.ZodObject<{
|
|
|
430
433
|
}>;
|
|
431
434
|
verifiedAt: z.ZodOptional<z.ZodString>;
|
|
432
435
|
notes: z.ZodOptional<z.ZodString>;
|
|
436
|
+
evidence: z.ZodOptional<z.ZodString>;
|
|
433
437
|
}, z.core.$strip>>;
|
|
434
438
|
extractedAt: z.ZodString;
|
|
435
439
|
summary: z.ZodObject<{
|
|
@@ -538,7 +542,7 @@ export declare function createPhaseState(status?: PhaseStatus): PhaseState;
|
|
|
538
542
|
/**
|
|
539
543
|
* Create acceptance criterion
|
|
540
544
|
*/
|
|
541
|
-
export declare function createAcceptanceCriterion(id: string, description: string, verificationMethod?: ACVerificationMethod): AcceptanceCriterion;
|
|
545
|
+
export declare function createAcceptanceCriterion(id: string, description: string, verificationMethod?: ACVerificationMethod, evidence?: string): AcceptanceCriterion;
|
|
542
546
|
/**
|
|
543
547
|
* Create acceptance criteria tracking structure
|
|
544
548
|
*/
|
|
@@ -149,6 +149,12 @@ export const AcceptanceCriterionSchema = z.object({
|
|
|
149
149
|
verifiedAt: z.string().datetime().optional(),
|
|
150
150
|
/** Additional notes about verification */
|
|
151
151
|
notes: z.string().optional(),
|
|
152
|
+
/**
|
|
153
|
+
* Explicit `Evidence:` clause declared on the AC line (#938). When present,
|
|
154
|
+
* this is what verificationMethod was resolved from instead of keyword
|
|
155
|
+
* inference — see `resolveVerificationMethod` in ac-parser.ts.
|
|
156
|
+
*/
|
|
157
|
+
evidence: z.string().optional(),
|
|
152
158
|
});
|
|
153
159
|
/**
|
|
154
160
|
* Acceptance criteria tracking for an issue
|
|
@@ -369,12 +375,13 @@ export function createPhaseState(status = "pending") {
|
|
|
369
375
|
/**
|
|
370
376
|
* Create acceptance criterion
|
|
371
377
|
*/
|
|
372
|
-
export function createAcceptanceCriterion(id, description, verificationMethod = "manual") {
|
|
378
|
+
export function createAcceptanceCriterion(id, description, verificationMethod = "manual", evidence) {
|
|
373
379
|
return {
|
|
374
380
|
id,
|
|
375
381
|
description,
|
|
376
382
|
verificationMethod,
|
|
377
383
|
status: "pending",
|
|
384
|
+
...(evidence !== undefined ? { evidence } : {}),
|
|
378
385
|
};
|
|
379
386
|
}
|
|
380
387
|
/**
|
|
@@ -83,6 +83,15 @@ export interface ExecutionConfig {
|
|
|
83
83
|
dryRun: boolean;
|
|
84
84
|
/** Enable MCP servers in headless mode (true by default, false if --no-mcp flag used) */
|
|
85
85
|
mcp: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Claude Desktop MCP server names explicitly opted in via
|
|
88
|
+
* `settings.run.mcpAllowlist` (#936). Both `ExecutionConfig` producers
|
|
89
|
+
* (`buildExecutionConfig`, `ready-gate.ts`'s `buildPhaseConfig`) read
|
|
90
|
+
* this from settings the same way, so it cannot drift between them the
|
|
91
|
+
* way #833's `phaseTimeout` once did. `phase-executor.ts` forwards it
|
|
92
|
+
* verbatim onto `AgentExecutionConfig.mcpAllowlist`.
|
|
93
|
+
*/
|
|
94
|
+
mcpAllowlist?: string[];
|
|
86
95
|
/**
|
|
87
96
|
* Enable automatic retry with MCP fallback.
|
|
88
97
|
* When true (default), failed phases are retried with MCP disabled.
|
|
@@ -182,6 +191,28 @@ export interface ExecutionConfig {
|
|
|
182
191
|
* see {@link AutoWaitLedger}.
|
|
183
192
|
*/
|
|
184
193
|
autoWaitMinutes?: number;
|
|
194
|
+
/**
|
|
195
|
+
* Resolved per-phase `model`/`effort` overrides (#914), keyed by phase
|
|
196
|
+
* name. Merged from `settings.run.phases` and the CLI's `--models`/
|
|
197
|
+
* `--efforts` flags via `resolvePhasePolicies` (CLI > settings > absent) —
|
|
198
|
+
* see `config-resolver.ts`. Absent/empty by default: `phase-executor.ts`
|
|
199
|
+
* only sets `AgentExecutionConfig.model`/`.effort` when a phase has an
|
|
200
|
+
* entry here, so an unconfigured run reaches the SDK unchanged.
|
|
201
|
+
*/
|
|
202
|
+
phasePolicies?: Record<string, {
|
|
203
|
+
model?: string;
|
|
204
|
+
effort?: string;
|
|
205
|
+
}>;
|
|
206
|
+
/**
|
|
207
|
+
* Evidence-based effort escalation on quality-loop retries (#915). CLI >
|
|
208
|
+
* settings > absent (`false`), resolved by `buildExecutionConfig`
|
|
209
|
+
* (`config-resolver.ts`) and `ready-gate.ts`'s `buildPhaseConfig` — the same
|
|
210
|
+
* two producers `phasePolicies` uses, so this cannot drift from that one
|
|
211
|
+
* (#833 class). Consumed only at dispatch time by
|
|
212
|
+
* `effort-escalation.ts`'s `withEscalatedEffort`, never baked statically
|
|
213
|
+
* into `phasePolicies` here — escalation is per-execution, not per-run.
|
|
214
|
+
*/
|
|
215
|
+
effortEscalation?: boolean;
|
|
185
216
|
}
|
|
186
217
|
/**
|
|
187
218
|
* A single liveness notice emitted during an auto-wait (#804 AC-7).
|
|
@@ -246,6 +277,16 @@ export interface PhaseResult {
|
|
|
246
277
|
stdoutTail?: string[];
|
|
247
278
|
/** Process exit code from the agent driver (#447) */
|
|
248
279
|
exitCode?: number;
|
|
280
|
+
/**
|
|
281
|
+
* Set when this execution's effort was escalated one tier above its
|
|
282
|
+
* resolved base (#915) — a quality-loop retry with `effortEscalation`
|
|
283
|
+
* enabled. Additive/optional, same shape as `capped?`/`structuredError?`;
|
|
284
|
+
* absent on every non-escalated execution.
|
|
285
|
+
*/
|
|
286
|
+
escalatedEffort?: {
|
|
287
|
+
base: string;
|
|
288
|
+
escalated: string;
|
|
289
|
+
};
|
|
249
290
|
}
|
|
250
291
|
/**
|
|
251
292
|
* Result of executing all phases for an issue
|
|
@@ -269,6 +310,15 @@ export interface IssueResult {
|
|
|
269
310
|
* with this string as the reason.
|
|
270
311
|
*/
|
|
271
312
|
prCreationError?: string;
|
|
313
|
+
/**
|
|
314
|
+
* Set when PR creation was skipped because the branch carried zero commits
|
|
315
|
+
* ahead of its base (#920) — a phase-restricted run (e.g. `--phases spec`)
|
|
316
|
+
* that implemented nothing has no deliverable to open a PR for, and
|
|
317
|
+
* attempting one would only fail with GitHub's "No commits between main and
|
|
318
|
+
* …" error. Distinct from {@link IssueResult.prCreationError}: this is not
|
|
319
|
+
* a failure — `success` stays whatever the phases produced.
|
|
320
|
+
*/
|
|
321
|
+
prSkippedReason?: string;
|
|
272
322
|
/**
|
|
273
323
|
* Set when the issue was skipped because another sequant session holds
|
|
274
324
|
* the per-issue lock (#625). Surfaced in the summary as
|
|
@@ -486,6 +536,29 @@ export interface RunOptions {
|
|
|
486
536
|
* and bounds wholesale — no new settings accompany this flag.
|
|
487
537
|
*/
|
|
488
538
|
readyGate?: boolean;
|
|
539
|
+
/**
|
|
540
|
+
* Per-phase model override (#914). Either a bare value applied to every
|
|
541
|
+
* phase (`--models sonnet`) or a comma list of `phase=model` pairs
|
|
542
|
+
* (`--models spec=fable,exec=sonnet`). Parsed and merged with
|
|
543
|
+
* `settings.run.phases` by `resolvePhasePolicies` (CLI > settings >
|
|
544
|
+
* absent) into `ExecutionConfig.phasePolicies`. Malformed specs fail fast
|
|
545
|
+
* at the Commander option boundary via `cli-flags.ts`.
|
|
546
|
+
*/
|
|
547
|
+
models?: string;
|
|
548
|
+
/**
|
|
549
|
+
* Per-phase reasoning-effort override (#914). Same grammar as {@link
|
|
550
|
+
* models} (bare value or comma list of `phase=effort` pairs); each value
|
|
551
|
+
* validates against the SDK's closed `low|medium|high|xhigh|max` enum.
|
|
552
|
+
*/
|
|
553
|
+
efforts?: string;
|
|
554
|
+
/**
|
|
555
|
+
* Evidence-based effort escalation on quality-loop retries (#915). Set via
|
|
556
|
+
* `--escalate-effort`. When true, a retried phase execution resolves one
|
|
557
|
+
* effort tier above its configured/inherited base for that execution only
|
|
558
|
+
* — see `effort-escalation.ts`. Default `false`: escalation raises token
|
|
559
|
+
* spend, so an unset flag leaves every run byte-identical to pre-#915.
|
|
560
|
+
*/
|
|
561
|
+
escalateEffort?: boolean;
|
|
489
562
|
}
|
|
490
563
|
/**
|
|
491
564
|
* CLI arguments for run command
|
|
@@ -641,4 +714,9 @@ export interface IssueExecutionContext {
|
|
|
641
714
|
* around verbose Claude streaming.
|
|
642
715
|
*/
|
|
643
716
|
phasePauseHandle?: PhasePauseHandle;
|
|
717
|
+
/**
|
|
718
|
+
* @internal Test seam for the #964 qa-verdict comment post. Defaults to a
|
|
719
|
+
* real `GitHubProvider().postComment` when unset.
|
|
720
|
+
*/
|
|
721
|
+
postComment?: (issueNumber: number, body: string) => Promise<void>;
|
|
644
722
|
}
|
|
@@ -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;
|
|
@@ -205,10 +205,18 @@ export function removeStaleWorktree(existingPath, branch, verbose) {
|
|
|
205
205
|
}
|
|
206
206
|
/**
|
|
207
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.
|
|
208
215
|
*/
|
|
209
|
-
export function listWorktrees() {
|
|
216
|
+
export function listWorktrees(cwd) {
|
|
210
217
|
const result = spawnSync("git", ["worktree", "list", "--porcelain"], {
|
|
211
218
|
stdio: "pipe",
|
|
219
|
+
...(cwd ? { cwd } : {}),
|
|
212
220
|
});
|
|
213
221
|
if (result.status !== 0)
|
|
214
222
|
return [];
|
|
@@ -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;
|