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
|
@@ -57,6 +57,23 @@ export interface AgentSettings {
|
|
|
57
57
|
*/
|
|
58
58
|
isolateParallel: boolean;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The Agent SDK's closed reasoning-effort enum (#914). Single source of
|
|
62
|
+
* truth — reused by `PhasePolicySchema`'s zod validation below and by
|
|
63
|
+
* `cli-flags.ts:parsePhaseSpecFlag` for the `--efforts` CLI boundary, so the
|
|
64
|
+
* two validation points cannot drift apart on which values are accepted.
|
|
65
|
+
*/
|
|
66
|
+
export declare const EFFORT_LEVELS: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
67
|
+
/**
|
|
68
|
+
* A single phase's `model`/`effort` override for the claude-code driver
|
|
69
|
+
* (#914). See `RunSettings.phases`.
|
|
70
|
+
*/
|
|
71
|
+
export interface PhasePolicy {
|
|
72
|
+
/** Model alias/ID, passed through unvalidated to the Agent SDK. */
|
|
73
|
+
model?: string;
|
|
74
|
+
/** Reasoning effort — validated against the SDK's closed enum. */
|
|
75
|
+
effort?: (typeof EFFORT_LEVELS)[number];
|
|
76
|
+
}
|
|
60
77
|
/**
|
|
61
78
|
* Aider-specific settings for the aider agent driver.
|
|
62
79
|
*/
|
|
@@ -112,6 +129,18 @@ export interface RunSettings {
|
|
|
112
129
|
* Default: true
|
|
113
130
|
*/
|
|
114
131
|
retry: boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Total minutes willing to wait for an exhausted rate-limit window to reopen
|
|
134
|
+
* before halting the run (#804).
|
|
135
|
+
*
|
|
136
|
+
* `0` (default) disables auto-wait: a window-exhausted rate limit halts
|
|
137
|
+
* immediately, exactly as it did before #804. Any positive value is a TOTAL
|
|
138
|
+
* budget per issue, not a per-occurrence allowance.
|
|
139
|
+
*
|
|
140
|
+
* The wait is in-process — it does not survive closing the terminal.
|
|
141
|
+
* Overridable per-invocation with `--auto-wait <minutes>`.
|
|
142
|
+
*/
|
|
143
|
+
autoWaitMinutes: number;
|
|
115
144
|
/**
|
|
116
145
|
* Threshold for stale branch detection in pre-flight checks.
|
|
117
146
|
* If feature branch is more than this many commits behind main,
|
|
@@ -154,6 +183,21 @@ export interface RunSettings {
|
|
|
154
183
|
* Default: true.
|
|
155
184
|
*/
|
|
156
185
|
relay?: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Per-phase `model`/`effort` overrides for the claude-code driver (#914),
|
|
188
|
+
* keyed by phase name. Absent by default — zero behavior change until
|
|
189
|
+
* opted in. Overridable per-invocation with `--models`/`--efforts`
|
|
190
|
+
* (CLI > settings > absent, resolved by `resolvePhasePolicies` in
|
|
191
|
+
* `config-resolver.ts`).
|
|
192
|
+
*/
|
|
193
|
+
phases?: Record<string, PhasePolicy>;
|
|
194
|
+
/**
|
|
195
|
+
* Evidence-based effort escalation on quality-loop retries (#915). Default
|
|
196
|
+
* `false` — raising effort raises token spend, which is the user's call.
|
|
197
|
+
* Overridable per-invocation with `--escalate-effort` (CLI > settings >
|
|
198
|
+
* default). See `effort-escalation.ts` for the resolver.
|
|
199
|
+
*/
|
|
200
|
+
effortEscalation: boolean;
|
|
157
201
|
}
|
|
158
202
|
/**
|
|
159
203
|
* Scope assessment threshold configuration
|
|
@@ -292,6 +336,25 @@ export declare const AgentSettingsSchema: z.ZodObject<{
|
|
|
292
336
|
}>>;
|
|
293
337
|
isolateParallel: z.ZodDefault<z.ZodBoolean>;
|
|
294
338
|
}, z.core.$strip>;
|
|
339
|
+
/**
|
|
340
|
+
* Zod schema for a single phase's model/effort override (#914).
|
|
341
|
+
*
|
|
342
|
+
* Model aliases/IDs pass through unvalidated — they churn independently of
|
|
343
|
+
* sequant releases, and the Agent SDK's `query()` call errors clearly on a
|
|
344
|
+
* bad one. Effort validates against the SDK's closed enum at settings-parse
|
|
345
|
+
* time since that set is stable and a typo here would otherwise silently
|
|
346
|
+
* fall through to the SDK default.
|
|
347
|
+
*/
|
|
348
|
+
export declare const PhasePolicySchema: z.ZodObject<{
|
|
349
|
+
model: z.ZodOptional<z.ZodString>;
|
|
350
|
+
effort: z.ZodOptional<z.ZodEnum<{
|
|
351
|
+
low: "low";
|
|
352
|
+
medium: "medium";
|
|
353
|
+
high: "high";
|
|
354
|
+
xhigh: "xhigh";
|
|
355
|
+
max: "max";
|
|
356
|
+
}>>;
|
|
357
|
+
}, z.core.$strip>;
|
|
295
358
|
/** Zod schema for RunSettings */
|
|
296
359
|
export declare const RunSettingsSchema: z.ZodObject<{
|
|
297
360
|
logJson: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -311,6 +374,7 @@ export declare const RunSettingsSchema: z.ZodObject<{
|
|
|
311
374
|
defaultBase: z.ZodOptional<z.ZodString>;
|
|
312
375
|
mcp: z.ZodDefault<z.ZodBoolean>;
|
|
313
376
|
retry: z.ZodDefault<z.ZodBoolean>;
|
|
377
|
+
autoWaitMinutes: z.ZodDefault<z.ZodNumber>;
|
|
314
378
|
staleBranchThreshold: z.ZodDefault<z.ZodNumber>;
|
|
315
379
|
resolvedIssueTTL: z.ZodDefault<z.ZodNumber>;
|
|
316
380
|
pmRun: z.ZodOptional<z.ZodString>;
|
|
@@ -322,6 +386,17 @@ export declare const RunSettingsSchema: z.ZodObject<{
|
|
|
322
386
|
extraArgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
323
387
|
}, z.core.$strip>>;
|
|
324
388
|
relay: z.ZodDefault<z.ZodBoolean>;
|
|
389
|
+
phases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
390
|
+
model: z.ZodOptional<z.ZodString>;
|
|
391
|
+
effort: z.ZodOptional<z.ZodEnum<{
|
|
392
|
+
low: "low";
|
|
393
|
+
medium: "medium";
|
|
394
|
+
high: "high";
|
|
395
|
+
xhigh: "xhigh";
|
|
396
|
+
max: "max";
|
|
397
|
+
}>>;
|
|
398
|
+
}, z.core.$strip>>>;
|
|
399
|
+
effortEscalation: z.ZodDefault<z.ZodBoolean>;
|
|
325
400
|
}, z.core.$strip>;
|
|
326
401
|
/** Zod schema for ScopeThreshold (base — fields required, no defaults) */
|
|
327
402
|
export declare const ScopeThresholdSchema: z.ZodObject<{
|
|
@@ -403,6 +478,7 @@ export declare const SettingsSchema: z.ZodObject<{
|
|
|
403
478
|
defaultBase: z.ZodOptional<z.ZodString>;
|
|
404
479
|
mcp: z.ZodDefault<z.ZodBoolean>;
|
|
405
480
|
retry: z.ZodDefault<z.ZodBoolean>;
|
|
481
|
+
autoWaitMinutes: z.ZodDefault<z.ZodNumber>;
|
|
406
482
|
staleBranchThreshold: z.ZodDefault<z.ZodNumber>;
|
|
407
483
|
resolvedIssueTTL: z.ZodDefault<z.ZodNumber>;
|
|
408
484
|
pmRun: z.ZodOptional<z.ZodString>;
|
|
@@ -414,6 +490,17 @@ export declare const SettingsSchema: z.ZodObject<{
|
|
|
414
490
|
extraArgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
415
491
|
}, z.core.$strip>>;
|
|
416
492
|
relay: z.ZodDefault<z.ZodBoolean>;
|
|
493
|
+
phases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
494
|
+
model: z.ZodOptional<z.ZodString>;
|
|
495
|
+
effort: z.ZodOptional<z.ZodEnum<{
|
|
496
|
+
low: "low";
|
|
497
|
+
medium: "medium";
|
|
498
|
+
high: "high";
|
|
499
|
+
xhigh: "xhigh";
|
|
500
|
+
max: "max";
|
|
501
|
+
}>>;
|
|
502
|
+
}, z.core.$strip>>>;
|
|
503
|
+
effortEscalation: z.ZodDefault<z.ZodBoolean>;
|
|
417
504
|
}, z.core.$strip>>;
|
|
418
505
|
agents: z.ZodDefault<z.ZodObject<{
|
|
419
506
|
parallel: z.ZodDefault<z.ZodBoolean>;
|
package/dist/src/lib/settings.js
CHANGED
|
@@ -13,10 +13,18 @@
|
|
|
13
13
|
import { readFile, writeFile, fileExists, ensureDir } from "./fs.js";
|
|
14
14
|
import { dirname } from "path";
|
|
15
15
|
import { z } from "zod";
|
|
16
|
+
import { getPhaseNames } from "./workflow/phase-registry.js";
|
|
16
17
|
/** Path to project-level settings file */
|
|
17
18
|
export const SETTINGS_PATH = ".sequant/settings.json";
|
|
18
19
|
/** Current settings schema version */
|
|
19
20
|
export const SETTINGS_VERSION = "1.0";
|
|
21
|
+
/**
|
|
22
|
+
* The Agent SDK's closed reasoning-effort enum (#914). Single source of
|
|
23
|
+
* truth — reused by `PhasePolicySchema`'s zod validation below and by
|
|
24
|
+
* `cli-flags.ts:parsePhaseSpecFlag` for the `--efforts` CLI boundary, so the
|
|
25
|
+
* two validation points cannot drift apart on which values are accepted.
|
|
26
|
+
*/
|
|
27
|
+
export const EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
20
28
|
// ─── Zod Schemas (AC-1, AC-5) ────────────────────────────────────────────────
|
|
21
29
|
/** Zod schema for RotationSettings */
|
|
22
30
|
export const RotationSettingsSchema = z.object({
|
|
@@ -36,21 +44,42 @@ export const AgentSettingsSchema = z.object({
|
|
|
36
44
|
model: z.enum(["haiku", "sonnet", "opus"]).default("haiku"),
|
|
37
45
|
isolateParallel: z.boolean().default(false),
|
|
38
46
|
});
|
|
47
|
+
/**
|
|
48
|
+
* Zod schema for a single phase's model/effort override (#914).
|
|
49
|
+
*
|
|
50
|
+
* Model aliases/IDs pass through unvalidated — they churn independently of
|
|
51
|
+
* sequant releases, and the Agent SDK's `query()` call errors clearly on a
|
|
52
|
+
* bad one. Effort validates against the SDK's closed enum at settings-parse
|
|
53
|
+
* time since that set is stable and a typo here would otherwise silently
|
|
54
|
+
* fall through to the SDK default.
|
|
55
|
+
*/
|
|
56
|
+
export const PhasePolicySchema = z.object({
|
|
57
|
+
model: z.string().optional(),
|
|
58
|
+
effort: z.enum(EFFORT_LEVELS).optional(),
|
|
59
|
+
});
|
|
39
60
|
/** Zod schema for RunSettings */
|
|
40
61
|
export const RunSettingsSchema = z.object({
|
|
41
62
|
logJson: z.boolean().default(true),
|
|
42
63
|
logPath: z.string().default(".sequant/logs"),
|
|
43
64
|
autoDetectPhases: z.boolean().default(true),
|
|
44
|
-
|
|
65
|
+
// #833: `.positive()` on the three that feed a timer or a loop bound. A `0`
|
|
66
|
+
// or negative here is not a weaker setting — it is an instant abort
|
|
67
|
+
// (`setTimeout` clamps a non-positive delay to 0) or a loop that never runs
|
|
68
|
+
// (`while (iteration < maxIterations)` is false on entry). The resolvers now
|
|
69
|
+
// guard these too; the schema is what gets the bad key *named* at load
|
|
70
|
+
// instead of silently swapped for a default. Left unconstrained elsewhere
|
|
71
|
+
// (`staleBranchThreshold`, `resolvedIssueTTL`) where 0 is meaningful.
|
|
72
|
+
timeout: z.number().positive().default(1800),
|
|
45
73
|
sequential: z.boolean().default(false),
|
|
46
|
-
concurrency: z.number().default(3),
|
|
74
|
+
concurrency: z.number().positive().default(3),
|
|
47
75
|
qualityLoop: z.boolean().default(false),
|
|
48
|
-
maxIterations: z.number().default(3),
|
|
76
|
+
maxIterations: z.number().positive().default(3),
|
|
49
77
|
smartTests: z.boolean().default(true),
|
|
50
78
|
rotation: RotationSettingsSchema.default(() => RotationSettingsSchema.parse({})),
|
|
51
79
|
defaultBase: z.string().optional(),
|
|
52
80
|
mcp: z.boolean().default(true),
|
|
53
81
|
retry: z.boolean().default(true),
|
|
82
|
+
autoWaitMinutes: z.number().min(0).default(0),
|
|
54
83
|
staleBranchThreshold: z.number().default(5),
|
|
55
84
|
resolvedIssueTTL: z.number().default(7),
|
|
56
85
|
pmRun: z.string().optional(),
|
|
@@ -58,6 +87,23 @@ export const RunSettingsSchema = z.object({
|
|
|
58
87
|
agent: z.string().optional(),
|
|
59
88
|
aider: AiderSettingsSchema.optional(),
|
|
60
89
|
relay: z.boolean().default(true),
|
|
90
|
+
/**
|
|
91
|
+
* Per-phase `model`/`effort` overrides for the claude-code driver (#914).
|
|
92
|
+
* Absent by default — zero behavior change until opted in. Keyed by phase
|
|
93
|
+
* name (validated against `getPhaseNames()` via `KNOWN_KEYS["run.phases"]`
|
|
94
|
+
* as a non-fatal warning, not a schema-level rejection — a typo'd phase
|
|
95
|
+
* name here should not crash a run the way an invalid `effort` enum does).
|
|
96
|
+
*/
|
|
97
|
+
phases: z.record(z.string(), PhasePolicySchema).optional(),
|
|
98
|
+
/**
|
|
99
|
+
* Evidence-based effort escalation on quality-loop retries (#915). Default
|
|
100
|
+
* `false` — raising effort raises token spend, which is the user's call.
|
|
101
|
+
* When enabled, a retried phase execution (loop iteration ≥ 2, or a
|
|
102
|
+
* `sequant ready` QA-pass loop re-run) resolves one effort tier above its
|
|
103
|
+
* configured/inherited base for that execution only. See
|
|
104
|
+
* `effort-escalation.ts` for the resolver.
|
|
105
|
+
*/
|
|
106
|
+
effortEscalation: z.boolean().default(false),
|
|
61
107
|
});
|
|
62
108
|
/** Zod schema for ScopeThreshold (base — fields required, no defaults) */
|
|
63
109
|
export const ScopeThresholdSchema = z.object({
|
|
@@ -178,7 +224,13 @@ const KNOWN_KEYS = {
|
|
|
178
224
|
"agent",
|
|
179
225
|
"aider",
|
|
180
226
|
"relay",
|
|
227
|
+
"phases",
|
|
181
228
|
]),
|
|
229
|
+
// #914: keyed by real phase name so a typo (`run.phases.exce`) warns
|
|
230
|
+
// instead of silently resolving to nothing. Computed from the registry
|
|
231
|
+
// rather than hardcoded so a new phase registration doesn't need a
|
|
232
|
+
// matching edit here.
|
|
233
|
+
"run.phases": new Set(getPhaseNames()),
|
|
182
234
|
agents: new Set(["parallel", "model", "isolateParallel"]),
|
|
183
235
|
scopeAssessment: new Set([
|
|
184
236
|
"enabled",
|
|
@@ -243,6 +295,43 @@ function formatZodErrors(error) {
|
|
|
243
295
|
return { path, message };
|
|
244
296
|
});
|
|
245
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Delete the values Zod rejected from a deep clone of `raw`, so a re-parse can
|
|
300
|
+
* fill just those with their defaults and keep every valid sibling (#833).
|
|
301
|
+
*
|
|
302
|
+
* An array index in the path means one element of an array failed. Deleting by
|
|
303
|
+
* index would leave a hole that re-parses as `undefined` and fails again, so
|
|
304
|
+
* the whole array is dropped and re-defaulted instead.
|
|
305
|
+
*/
|
|
306
|
+
function stripInvalidPaths(raw, issues) {
|
|
307
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
308
|
+
return {};
|
|
309
|
+
const clone = structuredClone(raw);
|
|
310
|
+
for (const issue of issues) {
|
|
311
|
+
// An empty path means the root object itself was rejected — nothing to
|
|
312
|
+
// salvage, so fall back to a bare defaults parse.
|
|
313
|
+
if (issue.path.length === 0)
|
|
314
|
+
return {};
|
|
315
|
+
// Truncate at the first array index: drop the array, not an element.
|
|
316
|
+
const firstIndex = issue.path.findIndex((seg) => typeof seg === "number");
|
|
317
|
+
const path = firstIndex === -1 ? issue.path : issue.path.slice(0, firstIndex);
|
|
318
|
+
if (path.length === 0)
|
|
319
|
+
return {};
|
|
320
|
+
let cursor = clone;
|
|
321
|
+
for (const key of path.slice(0, -1)) {
|
|
322
|
+
const next = cursor?.[key];
|
|
323
|
+
cursor =
|
|
324
|
+
next && typeof next === "object" && !Array.isArray(next)
|
|
325
|
+
? next
|
|
326
|
+
: undefined;
|
|
327
|
+
if (!cursor)
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
if (cursor)
|
|
331
|
+
delete cursor[path[path.length - 1]];
|
|
332
|
+
}
|
|
333
|
+
return clone;
|
|
334
|
+
}
|
|
246
335
|
/**
|
|
247
336
|
* Validate a raw settings object against the Zod schema (AC-2).
|
|
248
337
|
*
|
|
@@ -262,8 +351,15 @@ export function validateSettings(raw) {
|
|
|
262
351
|
}
|
|
263
352
|
// Zod validation failed — report errors as warnings and fall back to defaults
|
|
264
353
|
warnings.push(...formatZodErrors(result.error));
|
|
265
|
-
//
|
|
266
|
-
//
|
|
354
|
+
// Salvage the valid settings by dropping only the rejected keys and
|
|
355
|
+
// re-parsing. #833: this previously re-parsed `{}` despite a comment saying
|
|
356
|
+
// it stripped invalid fields, so a single bad value — one typo in
|
|
357
|
+
// `run.timeout` — silently discarded the user's entire settings.json and
|
|
358
|
+
// reverted every unrelated key to its default.
|
|
359
|
+
const salvaged = SettingsSchema.safeParse(stripInvalidPaths(raw, result.error.issues));
|
|
360
|
+
if (salvaged.success) {
|
|
361
|
+
return { settings: salvaged.data, warnings };
|
|
362
|
+
}
|
|
267
363
|
const fallback = SettingsSchema.safeParse({});
|
|
268
364
|
const settings = (fallback.success ? fallback.data : DEFAULT_SETTINGS);
|
|
269
365
|
return { settings, warnings };
|
|
@@ -350,9 +446,11 @@ export const DEFAULT_SETTINGS = {
|
|
|
350
446
|
rotation: DEFAULT_ROTATION_SETTINGS,
|
|
351
447
|
mcp: true, // Enable MCP servers by default in headless mode
|
|
352
448
|
retry: true, // Enable automatic retry with MCP fallback by default
|
|
449
|
+
autoWaitMinutes: 0, // #804: auto-wait off by default — window exhaustion halts
|
|
353
450
|
staleBranchThreshold: 5, // Block QA/test if feature is >5 commits behind main
|
|
354
451
|
resolvedIssueTTL: 7, // Auto-prune resolved issues after 7 days
|
|
355
452
|
relay: true, // Enable interactive relay (#383) by default
|
|
453
|
+
effortEscalation: false, // #915: off by default — raises token spend
|
|
356
454
|
},
|
|
357
455
|
agents: DEFAULT_AGENT_SETTINGS,
|
|
358
456
|
scopeAssessment: DEFAULT_SCOPE_ASSESSMENT_SETTINGS,
|
|
@@ -592,11 +690,11 @@ Generated by \`sequant init\`. See defaults below.
|
|
|
592
690
|
| \`logJson\` | boolean | \`true\` | Enable JSON logging |
|
|
593
691
|
| \`logPath\` | string | \`".sequant/logs"\` | Path to log directory |
|
|
594
692
|
| \`autoDetectPhases\` | boolean | \`true\` | Auto-detect phases from GitHub issue labels |
|
|
595
|
-
| \`timeout\` | number | \`1800\` | Default timeout per phase in seconds |
|
|
693
|
+
| \`timeout\` | number (> 0) | \`1800\` | Default timeout per phase in seconds. Must be positive — 0 or negative is rejected with a warning and the default used (#833) |
|
|
596
694
|
| \`sequential\` | boolean | \`false\` | Run issues sequentially by default |
|
|
597
|
-
| \`concurrency\` | number | \`3\` | Max concurrent issues in parallel mode |
|
|
695
|
+
| \`concurrency\` | number (> 0) | \`3\` | Max concurrent issues in parallel mode. Must be positive (#833) |
|
|
598
696
|
| \`qualityLoop\` | boolean | \`false\` | Enable quality loop by default |
|
|
599
|
-
| \`maxIterations\` | number | \`3\` | Max iterations for quality loop |
|
|
697
|
+
| \`maxIterations\` | number (> 0) | \`3\` | Max iterations for quality loop. Must be positive (#833) |
|
|
600
698
|
| \`smartTests\` | boolean | \`true\` | Enable smart test detection |
|
|
601
699
|
| \`defaultBase\` | string | — | Default base branch for worktree creation |
|
|
602
700
|
| \`mcp\` | boolean | \`true\` | Enable MCP servers in headless mode |
|
|
@@ -20,6 +20,36 @@
|
|
|
20
20
|
* shutdown.dispose();
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
+
/**
|
|
24
|
+
* Why the process is shutting down, handed to every cleanup task so they can
|
|
25
|
+
* record the cause rather than silently persisting a truncated record (#856).
|
|
26
|
+
*/
|
|
27
|
+
export interface AbortContext {
|
|
28
|
+
/** Signal that triggered the shutdown, e.g. `SIGTERM`. */
|
|
29
|
+
signal: string;
|
|
30
|
+
/** Human-readable cause, from `describeSignalCause`. */
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Conventional shell exit code for death-by-signal: `128 + signum`
|
|
35
|
+
* (SIGINT → 130, SIGTERM → 143). Falls back to 1 for anything unmapped.
|
|
36
|
+
*
|
|
37
|
+
* #856: this used to be a flat `0`. A run killed from outside therefore
|
|
38
|
+
* exited *successfully*, so nothing downstream — CI, a wrapping script, the
|
|
39
|
+
* user reading `$?` — could tell a completed run from a terminated one.
|
|
40
|
+
*/
|
|
41
|
+
export declare function exitCodeForSignal(signal: string): number;
|
|
42
|
+
/**
|
|
43
|
+
* Human-readable cause for a termination signal (#856, AC-4).
|
|
44
|
+
*
|
|
45
|
+
* SIGINT is the user pressing Ctrl+C — self-explanatory. SIGTERM is not:
|
|
46
|
+
* no interactive user sends it by hand, so it always means something else
|
|
47
|
+
* killed the run, and the user needs to be told that rather than left to
|
|
48
|
+
* infer it from truncated output. The known offender on macOS is Claude
|
|
49
|
+
* Code's `[bg-pty]` host-dead watchdog, which group-SIGKILLs a process tree
|
|
50
|
+
* containing the run; see `docs/incidents/856/`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function describeSignalCause(signal: string): string;
|
|
23
53
|
/**
|
|
24
54
|
* Options for ShutdownManager
|
|
25
55
|
*/
|
|
@@ -46,6 +76,8 @@ export interface ShutdownManagerOptions {
|
|
|
46
76
|
export declare class ShutdownManager {
|
|
47
77
|
private cleanupTasks;
|
|
48
78
|
private _isShuttingDown;
|
|
79
|
+
/** Cause of the in-progress shutdown; null until a signal arrives (#856). */
|
|
80
|
+
private _abortContext;
|
|
49
81
|
/** Active abort controllers — supports concurrent phase execution (#404) */
|
|
50
82
|
private abortControllers;
|
|
51
83
|
private forceExitTimeout;
|
|
@@ -63,6 +95,12 @@ export declare class ShutdownManager {
|
|
|
63
95
|
* Alias for isShuttingDown (matches proposed API)
|
|
64
96
|
*/
|
|
65
97
|
get shuttingDown(): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Why the process is shutting down, or `null` if it isn't (#856).
|
|
100
|
+
* Lets code outside a cleanup task — e.g. a `finally` block that also
|
|
101
|
+
* finalizes state — record the same cause the cleanup tasks saw.
|
|
102
|
+
*/
|
|
103
|
+
get abortContext(): AbortContext | null;
|
|
66
104
|
/**
|
|
67
105
|
* Register an abort controller for a running phase.
|
|
68
106
|
*
|
|
@@ -91,9 +129,11 @@ export declare class ShutdownManager {
|
|
|
91
129
|
* This allows dependent cleanup to happen in correct order.
|
|
92
130
|
*
|
|
93
131
|
* @param name - Human-readable name for user feedback
|
|
94
|
-
* @param task - Async function to execute during cleanup
|
|
132
|
+
* @param task - Async function to execute during cleanup. Receives the
|
|
133
|
+
* `AbortContext` when the shutdown was signal-triggered, or `null` on a
|
|
134
|
+
* programmatic teardown. Tasks that don't care may ignore the argument.
|
|
95
135
|
*/
|
|
96
|
-
registerCleanup(name: string, task: () => Promise<void>): void;
|
|
136
|
+
registerCleanup(name: string, task: (abort: AbortContext | null) => Promise<void>): void;
|
|
97
137
|
/**
|
|
98
138
|
* Unregister a cleanup task by name
|
|
99
139
|
*
|
package/dist/src/lib/shutdown.js
CHANGED
|
@@ -21,6 +21,43 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
import chalk from "chalk";
|
|
24
|
+
/** POSIX signal numbers for the signals we install handlers for. */
|
|
25
|
+
const SIGNAL_NUMBERS = {
|
|
26
|
+
SIGINT: 2,
|
|
27
|
+
SIGTERM: 15,
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Conventional shell exit code for death-by-signal: `128 + signum`
|
|
31
|
+
* (SIGINT → 130, SIGTERM → 143). Falls back to 1 for anything unmapped.
|
|
32
|
+
*
|
|
33
|
+
* #856: this used to be a flat `0`. A run killed from outside therefore
|
|
34
|
+
* exited *successfully*, so nothing downstream — CI, a wrapping script, the
|
|
35
|
+
* user reading `$?` — could tell a completed run from a terminated one.
|
|
36
|
+
*/
|
|
37
|
+
export function exitCodeForSignal(signal) {
|
|
38
|
+
const num = SIGNAL_NUMBERS[signal];
|
|
39
|
+
return num === undefined ? 1 : 128 + num;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Human-readable cause for a termination signal (#856, AC-4).
|
|
43
|
+
*
|
|
44
|
+
* SIGINT is the user pressing Ctrl+C — self-explanatory. SIGTERM is not:
|
|
45
|
+
* no interactive user sends it by hand, so it always means something else
|
|
46
|
+
* killed the run, and the user needs to be told that rather than left to
|
|
47
|
+
* infer it from truncated output. The known offender on macOS is Claude
|
|
48
|
+
* Code's `[bg-pty]` host-dead watchdog, which group-SIGKILLs a process tree
|
|
49
|
+
* containing the run; see `docs/incidents/856/`.
|
|
50
|
+
*/
|
|
51
|
+
export function describeSignalCause(signal) {
|
|
52
|
+
switch (signal) {
|
|
53
|
+
case "SIGINT":
|
|
54
|
+
return "interrupted by user (Ctrl+C)";
|
|
55
|
+
case "SIGTERM":
|
|
56
|
+
return "terminated by an external SIGTERM (not sent by sequant)";
|
|
57
|
+
default:
|
|
58
|
+
return `terminated by ${signal}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
24
61
|
/**
|
|
25
62
|
* Manages graceful shutdown for sequant run
|
|
26
63
|
*
|
|
@@ -34,6 +71,8 @@ import chalk from "chalk";
|
|
|
34
71
|
export class ShutdownManager {
|
|
35
72
|
cleanupTasks = [];
|
|
36
73
|
_isShuttingDown = false;
|
|
74
|
+
/** Cause of the in-progress shutdown; null until a signal arrives (#856). */
|
|
75
|
+
_abortContext = null;
|
|
37
76
|
/** Active abort controllers — supports concurrent phase execution (#404) */
|
|
38
77
|
abortControllers = new Set();
|
|
39
78
|
forceExitTimeout;
|
|
@@ -67,6 +106,14 @@ export class ShutdownManager {
|
|
|
67
106
|
get shuttingDown() {
|
|
68
107
|
return this._isShuttingDown;
|
|
69
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Why the process is shutting down, or `null` if it isn't (#856).
|
|
111
|
+
* Lets code outside a cleanup task — e.g. a `finally` block that also
|
|
112
|
+
* finalizes state — record the same cause the cleanup tasks saw.
|
|
113
|
+
*/
|
|
114
|
+
get abortContext() {
|
|
115
|
+
return this._abortContext;
|
|
116
|
+
}
|
|
70
117
|
/**
|
|
71
118
|
* Register an abort controller for a running phase.
|
|
72
119
|
*
|
|
@@ -104,7 +151,9 @@ export class ShutdownManager {
|
|
|
104
151
|
* This allows dependent cleanup to happen in correct order.
|
|
105
152
|
*
|
|
106
153
|
* @param name - Human-readable name for user feedback
|
|
107
|
-
* @param task - Async function to execute during cleanup
|
|
154
|
+
* @param task - Async function to execute during cleanup. Receives the
|
|
155
|
+
* `AbortContext` when the shutdown was signal-triggered, or `null` on a
|
|
156
|
+
* programmatic teardown. Tasks that don't care may ignore the argument.
|
|
108
157
|
*/
|
|
109
158
|
registerCleanup(name, task) {
|
|
110
159
|
this.cleanupTasks.push({ name, task });
|
|
@@ -138,6 +187,13 @@ export class ShutdownManager {
|
|
|
138
187
|
return;
|
|
139
188
|
}
|
|
140
189
|
this._isShuttingDown = true;
|
|
190
|
+
// #856 AC-4: name the cause up front. A truncated run whose last words
|
|
191
|
+
// were "shutting down gracefully" reads as an orderly stop; it isn't one.
|
|
192
|
+
const abort = {
|
|
193
|
+
signal,
|
|
194
|
+
reason: describeSignalCause(signal),
|
|
195
|
+
};
|
|
196
|
+
this._abortContext = abort;
|
|
141
197
|
this.output(chalk.yellow(`\n! Received ${signal}, shutting down gracefully...`));
|
|
142
198
|
// Abort all in-flight phases immediately
|
|
143
199
|
if (this.abortControllers.size > 0) {
|
|
@@ -157,7 +213,7 @@ export class ShutdownManager {
|
|
|
157
213
|
const tasksToRun = [...this.cleanupTasks].reverse();
|
|
158
214
|
for (const { name, task } of tasksToRun) {
|
|
159
215
|
try {
|
|
160
|
-
await task();
|
|
216
|
+
await task(abort);
|
|
161
217
|
this.output(chalk.green(`✓ ${name}`));
|
|
162
218
|
}
|
|
163
219
|
catch (err) {
|
|
@@ -166,9 +222,22 @@ export class ShutdownManager {
|
|
|
166
222
|
}
|
|
167
223
|
}
|
|
168
224
|
clearTimeout(forceExitTimer);
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
225
|
+
// #856 AC-4: report the abort AS an abort, with its cause and a non-zero
|
|
226
|
+
// exit code. The old banner ("Interrupted. Cleanup complete.") plus
|
|
227
|
+
// `exit(0)` made an externally-killed run indistinguishable from a
|
|
228
|
+
// successful one — and because this path exits the process, the normal
|
|
229
|
+
// `displaySummary` / `abortReason` rendering in run-display.ts is never
|
|
230
|
+
// reached, so this banner is the only thing the user sees.
|
|
231
|
+
const code = exitCodeForSignal(signal);
|
|
232
|
+
this.errorOutput(chalk.red(`\n✗ Run aborted — ${abort.reason}.`));
|
|
233
|
+
this.output(chalk.yellow(` Cleanup completed. No phase results beyond this point were recorded.`));
|
|
234
|
+
if (signal === "SIGTERM") {
|
|
235
|
+
this.output(chalk.gray(` sequant does not send itself SIGTERM. If this run was launched as a\n` +
|
|
236
|
+
` backgrounded task inside an interactive Claude Code session, see\n` +
|
|
237
|
+
` docs/incidents/856/ — relaunch from a plain terminal instead.`));
|
|
238
|
+
}
|
|
239
|
+
this.output(chalk.gray(` Exit code: ${code}`));
|
|
240
|
+
this.exit(code);
|
|
172
241
|
}
|
|
173
242
|
/**
|
|
174
243
|
* Remove signal handlers and clean up
|
|
@@ -181,6 +250,7 @@ export class ShutdownManager {
|
|
|
181
250
|
process.removeListener("SIGTERM", this.sigtermHandler);
|
|
182
251
|
this.cleanupTasks = [];
|
|
183
252
|
this.abortControllers.clear();
|
|
253
|
+
this._abortContext = null;
|
|
184
254
|
}
|
|
185
255
|
}
|
|
186
256
|
/**
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills installation check — single source of truth for the
|
|
3
|
+
* `.claude/skills/<skill>/SKILL.md` layout.
|
|
4
|
+
*
|
|
5
|
+
* Shared by `doctor` (Checks 2+3) and the `run` pre-flight (#813) so the two
|
|
6
|
+
* cannot drift. The claude-code driver loads skills from project scope only
|
|
7
|
+
* (`settingSources: ["project"]`, see #19 AC-3 / #711), which makes
|
|
8
|
+
* `.claude/skills/` a hard runtime dependency: a phase prompt like
|
|
9
|
+
* "Run the /spec workflow" can never resolve without it.
|
|
10
|
+
*/
|
|
11
|
+
/** Directory (relative to a project root) that skills are resolved from. */
|
|
12
|
+
export declare const SKILLS_DIR = ".claude/skills";
|
|
13
|
+
export interface SkillsCheckResult {
|
|
14
|
+
/** True when the `.claude/skills/` directory exists. */
|
|
15
|
+
skillsDirExists: boolean;
|
|
16
|
+
/** Requested skills whose `SKILL.md` is missing, in input order. */
|
|
17
|
+
missingSkills: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check that each requested skill is installed at
|
|
21
|
+
* `<cwd>/.claude/skills/<skill>/SKILL.md`.
|
|
22
|
+
*
|
|
23
|
+
* @param skills - Skill directory names to require (e.g. `["spec", "exec"]`).
|
|
24
|
+
* @param cwd - Project root to check under (default: `process.cwd()`).
|
|
25
|
+
*/
|
|
26
|
+
export declare function checkSkillsInstalled(skills: string[], cwd?: string): Promise<SkillsCheckResult>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills installation check — single source of truth for the
|
|
3
|
+
* `.claude/skills/<skill>/SKILL.md` layout.
|
|
4
|
+
*
|
|
5
|
+
* Shared by `doctor` (Checks 2+3) and the `run` pre-flight (#813) so the two
|
|
6
|
+
* cannot drift. The claude-code driver loads skills from project scope only
|
|
7
|
+
* (`settingSources: ["project"]`, see #19 AC-3 / #711), which makes
|
|
8
|
+
* `.claude/skills/` a hard runtime dependency: a phase prompt like
|
|
9
|
+
* "Run the /spec workflow" can never resolve without it.
|
|
10
|
+
*/
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { fileExists } from "./fs.js";
|
|
13
|
+
/** Directory (relative to a project root) that skills are resolved from. */
|
|
14
|
+
export const SKILLS_DIR = ".claude/skills";
|
|
15
|
+
/**
|
|
16
|
+
* Check that each requested skill is installed at
|
|
17
|
+
* `<cwd>/.claude/skills/<skill>/SKILL.md`.
|
|
18
|
+
*
|
|
19
|
+
* @param skills - Skill directory names to require (e.g. `["spec", "exec"]`).
|
|
20
|
+
* @param cwd - Project root to check under (default: `process.cwd()`).
|
|
21
|
+
*/
|
|
22
|
+
export async function checkSkillsInstalled(skills, cwd = process.cwd()) {
|
|
23
|
+
const skillsDirExists = await fileExists(join(cwd, SKILLS_DIR));
|
|
24
|
+
const missingSkills = [];
|
|
25
|
+
for (const skill of skills) {
|
|
26
|
+
if (!(await fileExists(join(cwd, SKILLS_DIR, skill, "SKILL.md")))) {
|
|
27
|
+
missingSkills.push(skill);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { skillsDirExists, missingSkills };
|
|
31
|
+
}
|