@nathapp/nax 0.49.1 → 0.49.6
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/CHANGELOG.md +14 -0
- package/README.md +282 -10
- package/dist/nax.js +257 -136
- package/package.json +1 -1
- package/src/agents/acp/adapter.ts +53 -23
- package/src/agents/acp/spawn-client.ts +0 -2
- package/src/agents/claude/execution.ts +14 -0
- package/src/agents/types.ts +7 -0
- package/src/cli/prompts-main.ts +4 -59
- package/src/cli/prompts-shared.ts +70 -0
- package/src/cli/prompts-tdd.ts +1 -1
- package/src/config/merge.ts +18 -0
- package/src/config/test-strategy.ts +4 -4
- package/src/execution/iteration-runner.ts +1 -1
- package/src/execution/pipeline-result-handler.ts +4 -1
- package/src/execution/story-selector.ts +2 -1
- package/src/interaction/plugins/webhook.ts +44 -25
- package/src/pipeline/stages/autofix.ts +26 -7
- package/src/pipeline/stages/routing.ts +1 -1
- package/src/review/runner.ts +15 -0
- package/src/tdd/cleanup.ts +15 -6
- package/src/tdd/isolation.ts +9 -2
- package/src/tdd/rectification-gate.ts +41 -10
- package/src/tdd/session-runner.ts +71 -38
- package/src/verification/executor.ts +4 -1
- package/src/verification/strategies/acceptance.ts +4 -1
package/dist/nax.js
CHANGED
|
@@ -3267,10 +3267,10 @@ Security-critical functions (authentication, cryptography, tokens, sessions, cre
|
|
|
3267
3267
|
password hashing, access control) must be classified at MINIMUM "medium" complexity
|
|
3268
3268
|
regardless of LOC count. These require at minimum "tdd-simple" test strategy.`, TEST_STRATEGY_GUIDE = `## Test Strategy Guide
|
|
3269
3269
|
|
|
3270
|
-
- test-after: Simple changes with well-understood behavior. Write tests after implementation.
|
|
3271
|
-
- tdd-simple: Medium complexity. Write
|
|
3272
|
-
- three-session-tdd: Complex stories.
|
|
3273
|
-
- three-session-tdd-lite: Expert/high-risk stories.
|
|
3270
|
+
- test-after: Simple changes with well-understood behavior. Write tests after implementation in a single session.
|
|
3271
|
+
- tdd-simple: Medium complexity. Write failing tests first, then implement to pass them \u2014 all in one session.
|
|
3272
|
+
- three-session-tdd: Complex stories. 3 sessions: (1) test-writer writes failing tests \u2014 no src/ changes allowed, (2) implementer makes them pass without modifying test files, (3) verifier confirms correctness.
|
|
3273
|
+
- three-session-tdd-lite: Expert/high-risk stories. 3 sessions: (1) test-writer writes failing tests and may create minimal src/ stubs for imports, (2) implementer makes tests pass and may add missing coverage or replace stubs, (3) verifier confirms correctness.`, GROUPING_RULES = `## Grouping Rules
|
|
3274
3274
|
|
|
3275
3275
|
- Combine small, related tasks into a single "simple" or "medium" story.
|
|
3276
3276
|
- Do NOT create separate stories for every single file or function unless complex.
|
|
@@ -3667,6 +3667,16 @@ function buildAllowedEnv(options) {
|
|
|
3667
3667
|
async function executeOnce(binary, options, pidRegistry) {
|
|
3668
3668
|
const cmd = _runOnceDeps.buildCmd(binary, options);
|
|
3669
3669
|
const startTime = Date.now();
|
|
3670
|
+
if (options.sessionRole || options.acpSessionName || options.keepSessionOpen) {
|
|
3671
|
+
const logger2 = getLogger();
|
|
3672
|
+
logger2.debug("agent", "CLI mode: session options received (unused)", {
|
|
3673
|
+
sessionRole: options.sessionRole,
|
|
3674
|
+
acpSessionName: options.acpSessionName,
|
|
3675
|
+
keepSessionOpen: options.keepSessionOpen,
|
|
3676
|
+
featureName: options.featureName,
|
|
3677
|
+
storyId: options.storyId
|
|
3678
|
+
});
|
|
3679
|
+
}
|
|
3670
3680
|
const proc = Bun.spawn(cmd, {
|
|
3671
3681
|
cwd: options.workdir,
|
|
3672
3682
|
stdout: "pipe",
|
|
@@ -19276,7 +19286,6 @@ class SpawnAcpClient {
|
|
|
19276
19286
|
model;
|
|
19277
19287
|
cwd;
|
|
19278
19288
|
timeoutSeconds;
|
|
19279
|
-
permissionMode;
|
|
19280
19289
|
env;
|
|
19281
19290
|
pidRegistry;
|
|
19282
19291
|
constructor(cmdStr, cwd, timeoutSeconds, pidRegistry) {
|
|
@@ -19290,7 +19299,6 @@ class SpawnAcpClient {
|
|
|
19290
19299
|
this.agentName = lastToken;
|
|
19291
19300
|
this.cwd = cwd || process.cwd();
|
|
19292
19301
|
this.timeoutSeconds = timeoutSeconds || 1800;
|
|
19293
|
-
this.permissionMode = "approve-reads";
|
|
19294
19302
|
this.env = buildAllowedEnv2();
|
|
19295
19303
|
this.pidRegistry = pidRegistry;
|
|
19296
19304
|
}
|
|
@@ -19432,7 +19440,13 @@ async function closeAcpSession(session) {
|
|
|
19432
19440
|
function acpSessionsPath(workdir, featureName) {
|
|
19433
19441
|
return join3(workdir, "nax", "features", featureName, "acp-sessions.json");
|
|
19434
19442
|
}
|
|
19435
|
-
|
|
19443
|
+
function sidecarSessionName(entry) {
|
|
19444
|
+
return typeof entry === "string" ? entry : entry.sessionName;
|
|
19445
|
+
}
|
|
19446
|
+
function sidecarAgentName(entry) {
|
|
19447
|
+
return typeof entry === "string" ? "claude" : entry.agentName;
|
|
19448
|
+
}
|
|
19449
|
+
async function saveAcpSession(workdir, featureName, storyId, sessionName, agentName = "claude") {
|
|
19436
19450
|
try {
|
|
19437
19451
|
const path = acpSessionsPath(workdir, featureName);
|
|
19438
19452
|
let data = {};
|
|
@@ -19440,7 +19454,7 @@ async function saveAcpSession(workdir, featureName, storyId, sessionName) {
|
|
|
19440
19454
|
const existing = await Bun.file(path).text();
|
|
19441
19455
|
data = JSON.parse(existing);
|
|
19442
19456
|
} catch {}
|
|
19443
|
-
data[storyId] = sessionName;
|
|
19457
|
+
data[storyId] = { sessionName, agentName };
|
|
19444
19458
|
await Bun.write(path, JSON.stringify(data, null, 2));
|
|
19445
19459
|
} catch (err) {
|
|
19446
19460
|
getSafeLogger()?.warn("acp-adapter", "Failed to save session to sidecar", { error: String(err) });
|
|
@@ -19467,7 +19481,8 @@ async function readAcpSession(workdir, featureName, storyId) {
|
|
|
19467
19481
|
const path = acpSessionsPath(workdir, featureName);
|
|
19468
19482
|
const existing = await Bun.file(path).text();
|
|
19469
19483
|
const data = JSON.parse(existing);
|
|
19470
|
-
|
|
19484
|
+
const entry = data[storyId];
|
|
19485
|
+
return entry ? sidecarSessionName(entry) : null;
|
|
19471
19486
|
} catch {
|
|
19472
19487
|
return null;
|
|
19473
19488
|
}
|
|
@@ -19486,24 +19501,34 @@ async function sweepFeatureSessions(workdir, featureName) {
|
|
|
19486
19501
|
return;
|
|
19487
19502
|
const logger = getSafeLogger();
|
|
19488
19503
|
logger?.info("acp-adapter", `[sweep] Closing ${entries.length} open sessions for feature: ${featureName}`);
|
|
19489
|
-
const
|
|
19490
|
-
const
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
19498
|
-
|
|
19504
|
+
const byAgent = new Map;
|
|
19505
|
+
for (const [, entry] of entries) {
|
|
19506
|
+
const agent = sidecarAgentName(entry);
|
|
19507
|
+
const name = sidecarSessionName(entry);
|
|
19508
|
+
if (!byAgent.has(agent))
|
|
19509
|
+
byAgent.set(agent, []);
|
|
19510
|
+
byAgent.get(agent)?.push(name);
|
|
19511
|
+
}
|
|
19512
|
+
for (const [agentName, sessionNames] of byAgent) {
|
|
19513
|
+
const cmdStr = `acpx ${agentName}`;
|
|
19514
|
+
const client = _acpAdapterDeps.createClient(cmdStr, workdir);
|
|
19515
|
+
try {
|
|
19516
|
+
await client.start();
|
|
19517
|
+
for (const sessionName of sessionNames) {
|
|
19518
|
+
try {
|
|
19519
|
+
if (client.loadSession) {
|
|
19520
|
+
const session = await client.loadSession(sessionName, agentName, "approve-reads");
|
|
19521
|
+
if (session) {
|
|
19522
|
+
await session.close().catch(() => {});
|
|
19523
|
+
}
|
|
19499
19524
|
}
|
|
19525
|
+
} catch (err) {
|
|
19526
|
+
logger?.warn("acp-adapter", `[sweep] Failed to close session ${sessionName}`, { error: String(err) });
|
|
19500
19527
|
}
|
|
19501
|
-
} catch (err) {
|
|
19502
|
-
logger?.warn("acp-adapter", `[sweep] Failed to close session ${sessionName}`, { error: String(err) });
|
|
19503
19528
|
}
|
|
19529
|
+
} finally {
|
|
19530
|
+
await client.close().catch(() => {});
|
|
19504
19531
|
}
|
|
19505
|
-
} finally {
|
|
19506
|
-
await client.close().catch(() => {});
|
|
19507
19532
|
}
|
|
19508
19533
|
try {
|
|
19509
19534
|
await Bun.write(path, JSON.stringify({}, null, 2));
|
|
@@ -19644,7 +19669,7 @@ class AcpAgentAdapter {
|
|
|
19644
19669
|
});
|
|
19645
19670
|
const session = await ensureAcpSession(client, sessionName, this.name, permissionMode);
|
|
19646
19671
|
if (options.featureName && options.storyId) {
|
|
19647
|
-
await saveAcpSession(options.workdir, options.featureName, options.storyId, sessionName);
|
|
19672
|
+
await saveAcpSession(options.workdir, options.featureName, options.storyId, sessionName, this.name);
|
|
19648
19673
|
}
|
|
19649
19674
|
let lastResponse = null;
|
|
19650
19675
|
let timedOut = false;
|
|
@@ -19702,13 +19727,15 @@ class AcpAgentAdapter {
|
|
|
19702
19727
|
}
|
|
19703
19728
|
runState.succeeded = !timedOut && lastResponse?.stopReason === "end_turn";
|
|
19704
19729
|
} finally {
|
|
19705
|
-
if (runState.succeeded) {
|
|
19730
|
+
if (runState.succeeded && !options.keepSessionOpen) {
|
|
19706
19731
|
await closeAcpSession(session);
|
|
19707
19732
|
if (options.featureName && options.storyId) {
|
|
19708
19733
|
await clearAcpSession(options.workdir, options.featureName, options.storyId);
|
|
19709
19734
|
}
|
|
19710
|
-
} else {
|
|
19735
|
+
} else if (!runState.succeeded) {
|
|
19711
19736
|
getSafeLogger()?.info("acp-adapter", "Keeping session open for retry", { sessionName });
|
|
19737
|
+
} else {
|
|
19738
|
+
getSafeLogger()?.debug("acp-adapter", "Keeping session open (keepSessionOpen=true)", { sessionName });
|
|
19712
19739
|
}
|
|
19713
19740
|
await client.close().catch(() => {});
|
|
19714
19741
|
}
|
|
@@ -20726,6 +20753,18 @@ function mergePackageConfig(root, packageOverride) {
|
|
|
20726
20753
|
...packageOverride.review,
|
|
20727
20754
|
commands: {
|
|
20728
20755
|
...root.review.commands,
|
|
20756
|
+
...packageOverride.quality?.commands?.lint !== undefined && {
|
|
20757
|
+
lint: packageOverride.quality.commands.lint
|
|
20758
|
+
},
|
|
20759
|
+
...packageOverride.quality?.commands?.lintFix !== undefined && {
|
|
20760
|
+
lintFix: packageOverride.quality.commands.lintFix
|
|
20761
|
+
},
|
|
20762
|
+
...packageOverride.quality?.commands?.typecheck !== undefined && {
|
|
20763
|
+
typecheck: packageOverride.quality.commands.typecheck
|
|
20764
|
+
},
|
|
20765
|
+
...packageOverride.quality?.commands?.test !== undefined && {
|
|
20766
|
+
test: packageOverride.quality.commands.test
|
|
20767
|
+
},
|
|
20729
20768
|
...packageOverride.review?.commands
|
|
20730
20769
|
}
|
|
20731
20770
|
},
|
|
@@ -22250,7 +22289,7 @@ var package_default;
|
|
|
22250
22289
|
var init_package = __esm(() => {
|
|
22251
22290
|
package_default = {
|
|
22252
22291
|
name: "@nathapp/nax",
|
|
22253
|
-
version: "0.49.
|
|
22292
|
+
version: "0.49.6",
|
|
22254
22293
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22255
22294
|
type: "module",
|
|
22256
22295
|
bin: {
|
|
@@ -22323,8 +22362,8 @@ var init_version = __esm(() => {
|
|
|
22323
22362
|
NAX_VERSION = package_default.version;
|
|
22324
22363
|
NAX_COMMIT = (() => {
|
|
22325
22364
|
try {
|
|
22326
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22327
|
-
return "
|
|
22365
|
+
if (/^[0-9a-f]{6,10}$/.test("a1f7e2d"))
|
|
22366
|
+
return "a1f7e2d";
|
|
22328
22367
|
} catch {}
|
|
22329
22368
|
try {
|
|
22330
22369
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23309,6 +23348,7 @@ class WebhookInteractionPlugin {
|
|
|
23309
23348
|
server = null;
|
|
23310
23349
|
serverStartPromise = null;
|
|
23311
23350
|
pendingResponses = new Map;
|
|
23351
|
+
receiveCallbacks = new Map;
|
|
23312
23352
|
async init(config2) {
|
|
23313
23353
|
const cfg = WebhookConfigSchema.parse(config2);
|
|
23314
23354
|
this.config = {
|
|
@@ -23358,27 +23398,39 @@ class WebhookInteractionPlugin {
|
|
|
23358
23398
|
}
|
|
23359
23399
|
async receive(requestId, timeout = 60000) {
|
|
23360
23400
|
await this.startServer();
|
|
23361
|
-
const
|
|
23362
|
-
|
|
23363
|
-
|
|
23364
|
-
|
|
23365
|
-
const response = this.pendingResponses.get(requestId);
|
|
23366
|
-
if (response) {
|
|
23367
|
-
this.pendingResponses.delete(requestId);
|
|
23368
|
-
return response;
|
|
23369
|
-
}
|
|
23370
|
-
await _webhookPluginDeps.sleep(backoffMs);
|
|
23371
|
-
backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
|
|
23401
|
+
const early = this.pendingResponses.get(requestId);
|
|
23402
|
+
if (early) {
|
|
23403
|
+
this.pendingResponses.delete(requestId);
|
|
23404
|
+
return early;
|
|
23372
23405
|
}
|
|
23373
|
-
return {
|
|
23374
|
-
|
|
23375
|
-
|
|
23376
|
-
|
|
23377
|
-
|
|
23378
|
-
|
|
23406
|
+
return new Promise((resolve7) => {
|
|
23407
|
+
const timer = setTimeout(() => {
|
|
23408
|
+
this.receiveCallbacks.delete(requestId);
|
|
23409
|
+
resolve7({
|
|
23410
|
+
requestId,
|
|
23411
|
+
action: "skip",
|
|
23412
|
+
respondedBy: "timeout",
|
|
23413
|
+
respondedAt: Date.now()
|
|
23414
|
+
});
|
|
23415
|
+
}, timeout);
|
|
23416
|
+
this.receiveCallbacks.set(requestId, (response) => {
|
|
23417
|
+
clearTimeout(timer);
|
|
23418
|
+
this.receiveCallbacks.delete(requestId);
|
|
23419
|
+
resolve7(response);
|
|
23420
|
+
});
|
|
23421
|
+
});
|
|
23379
23422
|
}
|
|
23380
23423
|
async cancel(requestId) {
|
|
23381
23424
|
this.pendingResponses.delete(requestId);
|
|
23425
|
+
this.receiveCallbacks.delete(requestId);
|
|
23426
|
+
}
|
|
23427
|
+
deliverResponse(requestId, response) {
|
|
23428
|
+
const cb = this.receiveCallbacks.get(requestId);
|
|
23429
|
+
if (cb) {
|
|
23430
|
+
cb(response);
|
|
23431
|
+
} else {
|
|
23432
|
+
this.pendingResponses.set(requestId, response);
|
|
23433
|
+
}
|
|
23382
23434
|
}
|
|
23383
23435
|
async startServer() {
|
|
23384
23436
|
if (this.server)
|
|
@@ -23431,7 +23483,7 @@ class WebhookInteractionPlugin {
|
|
|
23431
23483
|
try {
|
|
23432
23484
|
const parsed = JSON.parse(body);
|
|
23433
23485
|
const response = InteractionResponseSchema.parse(parsed);
|
|
23434
|
-
this.
|
|
23486
|
+
this.deliverResponse(requestId, response);
|
|
23435
23487
|
} catch {
|
|
23436
23488
|
return new Response("Bad Request: Invalid response format", { status: 400 });
|
|
23437
23489
|
}
|
|
@@ -23439,7 +23491,7 @@ class WebhookInteractionPlugin {
|
|
|
23439
23491
|
try {
|
|
23440
23492
|
const parsed = await req.json();
|
|
23441
23493
|
const response = InteractionResponseSchema.parse(parsed);
|
|
23442
|
-
this.
|
|
23494
|
+
this.deliverResponse(requestId, response);
|
|
23443
23495
|
} catch {
|
|
23444
23496
|
return new Response("Bad Request: Invalid response format", { status: 400 });
|
|
23445
23497
|
}
|
|
@@ -23466,12 +23518,9 @@ class WebhookInteractionPlugin {
|
|
|
23466
23518
|
}
|
|
23467
23519
|
}
|
|
23468
23520
|
}
|
|
23469
|
-
var
|
|
23521
|
+
var WebhookConfigSchema, InteractionResponseSchema;
|
|
23470
23522
|
var init_webhook = __esm(() => {
|
|
23471
23523
|
init_zod();
|
|
23472
|
-
_webhookPluginDeps = {
|
|
23473
|
-
sleep: (ms) => Bun.sleep(ms)
|
|
23474
|
-
};
|
|
23475
23524
|
WebhookConfigSchema = exports_external.object({
|
|
23476
23525
|
url: exports_external.string().url().optional(),
|
|
23477
23526
|
callbackPort: exports_external.number().int().min(1024).max(65535).optional(),
|
|
@@ -24357,6 +24406,8 @@ async function resolveCommand(check2, config2, executionConfig, workdir) {
|
|
|
24357
24406
|
}
|
|
24358
24407
|
async function runCheck(check2, command, workdir) {
|
|
24359
24408
|
const startTime = Date.now();
|
|
24409
|
+
const logger = getSafeLogger();
|
|
24410
|
+
logger?.info("review", `Running ${check2} check`, { check: check2, command, workdir });
|
|
24360
24411
|
try {
|
|
24361
24412
|
const parts = command.split(/\s+/);
|
|
24362
24413
|
const executable = parts[0];
|
|
@@ -24395,6 +24446,17 @@ async function runCheck(check2, command, workdir) {
|
|
|
24395
24446
|
const stderr = await new Response(proc.stderr).text();
|
|
24396
24447
|
const output = [stdout, stderr].filter(Boolean).join(`
|
|
24397
24448
|
`);
|
|
24449
|
+
if (exitCode !== 0) {
|
|
24450
|
+
logger?.warn("review", `${check2} check failed`, {
|
|
24451
|
+
check: check2,
|
|
24452
|
+
command,
|
|
24453
|
+
workdir,
|
|
24454
|
+
exitCode,
|
|
24455
|
+
output: output.slice(0, 2000)
|
|
24456
|
+
});
|
|
24457
|
+
} else {
|
|
24458
|
+
logger?.debug("review", `${check2} check passed`, { check: check2, command, durationMs: Date.now() - startTime });
|
|
24459
|
+
}
|
|
24398
24460
|
return {
|
|
24399
24461
|
check: check2,
|
|
24400
24462
|
command,
|
|
@@ -24680,8 +24742,8 @@ async function recheckReview(ctx) {
|
|
|
24680
24742
|
const { reviewStage: reviewStage2 } = await Promise.resolve().then(() => (init_review(), exports_review));
|
|
24681
24743
|
if (!reviewStage2.enabled(ctx))
|
|
24682
24744
|
return true;
|
|
24683
|
-
|
|
24684
|
-
return
|
|
24745
|
+
await reviewStage2.execute(ctx);
|
|
24746
|
+
return ctx.reviewResult?.success === true;
|
|
24685
24747
|
}
|
|
24686
24748
|
function collectFailedChecks(ctx) {
|
|
24687
24749
|
return (ctx.reviewResult?.checks ?? []).filter((c) => !c.success);
|
|
@@ -24793,11 +24855,18 @@ var init_autofix = __esm(() => {
|
|
|
24793
24855
|
const lintFixCmd = effectiveConfig.quality.commands.lintFix;
|
|
24794
24856
|
const formatFixCmd = effectiveConfig.quality.commands.formatFix;
|
|
24795
24857
|
const effectiveWorkdir = ctx.story.workdir ? join18(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
24796
|
-
|
|
24858
|
+
const failedCheckNames = new Set((reviewResult.checks ?? []).filter((c) => !c.success).map((c) => c.check));
|
|
24859
|
+
const hasLintFailure = failedCheckNames.has("lint");
|
|
24860
|
+
logger.info("autofix", "Starting autofix", {
|
|
24861
|
+
storyId: ctx.story.id,
|
|
24862
|
+
failedChecks: [...failedCheckNames],
|
|
24863
|
+
workdir: effectiveWorkdir
|
|
24864
|
+
});
|
|
24865
|
+
if (hasLintFailure && (lintFixCmd || formatFixCmd)) {
|
|
24797
24866
|
if (lintFixCmd) {
|
|
24798
24867
|
pipelineEventBus.emit({ type: "autofix:started", storyId: ctx.story.id, command: lintFixCmd });
|
|
24799
24868
|
const lintResult = await _autofixDeps.runCommand(lintFixCmd, effectiveWorkdir);
|
|
24800
|
-
logger.debug("autofix", `lintFix exit=${lintResult.exitCode}`, { storyId: ctx.story.id });
|
|
24869
|
+
logger.debug("autofix", `lintFix exit=${lintResult.exitCode}`, { storyId: ctx.story.id, command: lintFixCmd });
|
|
24801
24870
|
if (lintResult.exitCode !== 0) {
|
|
24802
24871
|
logger.warn("autofix", "lintFix command failed \u2014 may not have fixed all issues", {
|
|
24803
24872
|
storyId: ctx.story.id,
|
|
@@ -24808,7 +24877,10 @@ var init_autofix = __esm(() => {
|
|
|
24808
24877
|
if (formatFixCmd) {
|
|
24809
24878
|
pipelineEventBus.emit({ type: "autofix:started", storyId: ctx.story.id, command: formatFixCmd });
|
|
24810
24879
|
const fmtResult = await _autofixDeps.runCommand(formatFixCmd, effectiveWorkdir);
|
|
24811
|
-
logger.debug("autofix", `formatFix exit=${fmtResult.exitCode}`, {
|
|
24880
|
+
logger.debug("autofix", `formatFix exit=${fmtResult.exitCode}`, {
|
|
24881
|
+
storyId: ctx.story.id,
|
|
24882
|
+
command: formatFixCmd
|
|
24883
|
+
});
|
|
24812
24884
|
if (fmtResult.exitCode !== 0) {
|
|
24813
24885
|
logger.warn("autofix", "formatFix command failed \u2014 may not have fixed all issues", {
|
|
24814
24886
|
storyId: ctx.story.id,
|
|
@@ -24819,11 +24891,12 @@ var init_autofix = __esm(() => {
|
|
|
24819
24891
|
const recheckPassed = await _autofixDeps.recheckReview(ctx);
|
|
24820
24892
|
pipelineEventBus.emit({ type: "autofix:completed", storyId: ctx.story.id, fixed: recheckPassed });
|
|
24821
24893
|
if (recheckPassed) {
|
|
24822
|
-
if (ctx.reviewResult)
|
|
24823
|
-
ctx.reviewResult = { ...ctx.reviewResult, success: true };
|
|
24824
24894
|
logger.info("autofix", "Mechanical autofix succeeded \u2014 retrying review", { storyId: ctx.story.id });
|
|
24825
24895
|
return { action: "retry", fromStage: "review" };
|
|
24826
24896
|
}
|
|
24897
|
+
logger.info("autofix", "Mechanical autofix did not resolve all failures \u2014 proceeding to agent rectification", {
|
|
24898
|
+
storyId: ctx.story.id
|
|
24899
|
+
});
|
|
24827
24900
|
}
|
|
24828
24901
|
const agentFixed = await _autofixDeps.runAgentRectification(ctx);
|
|
24829
24902
|
if (agentFixed) {
|
|
@@ -26013,13 +26086,13 @@ function isSourceFile(filePath) {
|
|
|
26013
26086
|
return SRC_PATTERNS.some((pattern) => pattern.test(filePath));
|
|
26014
26087
|
}
|
|
26015
26088
|
async function getChangedFiles2(workdir, fromRef = "HEAD") {
|
|
26016
|
-
const proc =
|
|
26089
|
+
const proc = _isolationDeps.spawn(["git", "diff", "--name-only", fromRef], {
|
|
26017
26090
|
cwd: workdir,
|
|
26018
26091
|
stdout: "pipe",
|
|
26019
26092
|
stderr: "pipe"
|
|
26020
26093
|
});
|
|
26094
|
+
const output = await Bun.readableStreamToText(proc.stdout);
|
|
26021
26095
|
await proc.exited;
|
|
26022
|
-
const output = await new Response(proc.stdout).text();
|
|
26023
26096
|
return output.trim().split(`
|
|
26024
26097
|
`).filter(Boolean);
|
|
26025
26098
|
}
|
|
@@ -26066,8 +26139,9 @@ async function verifyImplementerIsolation(workdir, beforeRef) {
|
|
|
26066
26139
|
description: "Implementer should not modify test files"
|
|
26067
26140
|
};
|
|
26068
26141
|
}
|
|
26069
|
-
var TEST_PATTERNS, SRC_PATTERNS;
|
|
26142
|
+
var _isolationDeps, TEST_PATTERNS, SRC_PATTERNS;
|
|
26070
26143
|
var init_isolation = __esm(() => {
|
|
26144
|
+
_isolationDeps = { spawn: Bun.spawn };
|
|
26071
26145
|
TEST_PATTERNS = [/^test\//, /^tests\//, /^__tests__\//, /\.spec\.\w+$/, /\.test\.\w+$/, /\.e2e-spec\.\w+$/];
|
|
26072
26146
|
SRC_PATTERNS = [/^src\//, /^lib\//, /^packages\//];
|
|
26073
26147
|
});
|
|
@@ -26265,7 +26339,7 @@ async function executeWithTimeout(command, timeoutSeconds, env2, options) {
|
|
|
26265
26339
|
const shell = options?.shell ?? "/bin/sh";
|
|
26266
26340
|
const gracePeriodMs = options?.gracePeriodMs ?? 5000;
|
|
26267
26341
|
const drainTimeoutMs = options?.drainTimeoutMs ?? 2000;
|
|
26268
|
-
const proc =
|
|
26342
|
+
const proc = _executorDeps.spawn([shell, "-c", command], {
|
|
26269
26343
|
stdout: "pipe",
|
|
26270
26344
|
stderr: "pipe",
|
|
26271
26345
|
env: env2 || normalizeEnvironment(process.env),
|
|
@@ -26359,8 +26433,9 @@ function buildTestCommand(baseCommand, options) {
|
|
|
26359
26433
|
}
|
|
26360
26434
|
return command;
|
|
26361
26435
|
}
|
|
26362
|
-
var DEFAULT_STRIP_ENV_VARS;
|
|
26436
|
+
var _executorDeps, DEFAULT_STRIP_ENV_VARS;
|
|
26363
26437
|
var init_executor = __esm(() => {
|
|
26438
|
+
_executorDeps = { spawn: Bun.spawn };
|
|
26364
26439
|
DEFAULT_STRIP_ENV_VARS = ["CLAUDECODE", "REPL_ID", "AGENT"];
|
|
26365
26440
|
});
|
|
26366
26441
|
|
|
@@ -26660,15 +26735,15 @@ var init_verification = __esm(() => {
|
|
|
26660
26735
|
// src/tdd/cleanup.ts
|
|
26661
26736
|
async function getPgid(pid) {
|
|
26662
26737
|
try {
|
|
26663
|
-
const proc =
|
|
26738
|
+
const proc = _cleanupDeps.spawn(["ps", "-o", "pgid=", "-p", String(pid)], {
|
|
26664
26739
|
stdout: "pipe",
|
|
26665
26740
|
stderr: "pipe"
|
|
26666
26741
|
});
|
|
26742
|
+
const output = await Bun.readableStreamToText(proc.stdout);
|
|
26667
26743
|
const exitCode = await proc.exited;
|
|
26668
26744
|
if (exitCode !== 0) {
|
|
26669
26745
|
return null;
|
|
26670
26746
|
}
|
|
26671
|
-
const output = await new Response(proc.stdout).text();
|
|
26672
26747
|
const pgid = Number.parseInt(output.trim(), 10);
|
|
26673
26748
|
return Number.isNaN(pgid) ? null : pgid;
|
|
26674
26749
|
} catch {
|
|
@@ -26682,7 +26757,7 @@ async function cleanupProcessTree(pid, gracePeriodMs = 3000) {
|
|
|
26682
26757
|
return;
|
|
26683
26758
|
}
|
|
26684
26759
|
try {
|
|
26685
|
-
|
|
26760
|
+
_cleanupDeps.kill(-pgid, "SIGTERM");
|
|
26686
26761
|
} catch (error48) {
|
|
26687
26762
|
const err = error48;
|
|
26688
26763
|
if (err.code !== "ESRCH") {
|
|
@@ -26690,11 +26765,11 @@ async function cleanupProcessTree(pid, gracePeriodMs = 3000) {
|
|
|
26690
26765
|
}
|
|
26691
26766
|
return;
|
|
26692
26767
|
}
|
|
26693
|
-
await
|
|
26768
|
+
await _cleanupDeps.sleep(gracePeriodMs);
|
|
26694
26769
|
const pgidAfterWait = await getPgid(pid);
|
|
26695
26770
|
if (pgidAfterWait && pgidAfterWait === pgid) {
|
|
26696
26771
|
try {
|
|
26697
|
-
|
|
26772
|
+
_cleanupDeps.kill(-pgid, "SIGKILL");
|
|
26698
26773
|
} catch {}
|
|
26699
26774
|
}
|
|
26700
26775
|
} catch (error48) {
|
|
@@ -26705,8 +26780,14 @@ async function cleanupProcessTree(pid, gracePeriodMs = 3000) {
|
|
|
26705
26780
|
});
|
|
26706
26781
|
}
|
|
26707
26782
|
}
|
|
26783
|
+
var _cleanupDeps;
|
|
26708
26784
|
var init_cleanup = __esm(() => {
|
|
26709
26785
|
init_logger2();
|
|
26786
|
+
_cleanupDeps = {
|
|
26787
|
+
spawn: Bun.spawn,
|
|
26788
|
+
sleep: Bun.sleep,
|
|
26789
|
+
kill: process.kill.bind(process)
|
|
26790
|
+
};
|
|
26710
26791
|
});
|
|
26711
26792
|
|
|
26712
26793
|
// src/tdd/prompts.ts
|
|
@@ -26729,10 +26810,12 @@ async function runFullSuiteGate(story, config2, workdir, agent, implementerTier,
|
|
|
26729
26810
|
storyId: story.id,
|
|
26730
26811
|
timeout: fullSuiteTimeout
|
|
26731
26812
|
});
|
|
26732
|
-
const fullSuiteResult = await executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
26813
|
+
const fullSuiteResult = await _rectificationGateDeps.executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
26814
|
+
cwd: workdir
|
|
26815
|
+
});
|
|
26733
26816
|
const fullSuitePassed = fullSuiteResult.success && fullSuiteResult.exitCode === 0;
|
|
26734
26817
|
if (!fullSuitePassed && fullSuiteResult.output) {
|
|
26735
|
-
const testSummary = parseBunTestOutput(fullSuiteResult.output);
|
|
26818
|
+
const testSummary = _rectificationGateDeps.parseBunTestOutput(fullSuiteResult.output);
|
|
26736
26819
|
if (testSummary.failed > 0) {
|
|
26737
26820
|
return await runRectificationLoop(story, config2, workdir, agent, implementerTier, contextMarkdown, lite, logger, testSummary, rectificationConfig, testCmd, fullSuiteTimeout, featureName);
|
|
26738
26821
|
}
|
|
@@ -26773,8 +26856,14 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
26773
26856
|
failedTests: testSummary.failed,
|
|
26774
26857
|
passedTests: testSummary.passed
|
|
26775
26858
|
});
|
|
26776
|
-
|
|
26859
|
+
const rectificationSessionName = buildSessionName(workdir, featureName, story.id, "implementer");
|
|
26860
|
+
logger.debug("tdd", "Rectification session name (shared across all attempts)", {
|
|
26861
|
+
storyId: story.id,
|
|
26862
|
+
sessionName: rectificationSessionName
|
|
26863
|
+
});
|
|
26864
|
+
while (_rectificationGateDeps.shouldRetryRectification(rectificationState, rectificationConfig)) {
|
|
26777
26865
|
rectificationState.attempt++;
|
|
26866
|
+
const isLastAttempt = rectificationState.attempt >= rectificationConfig.maxRetries;
|
|
26778
26867
|
logger.info("tdd", `-> Implementer rectification attempt ${rectificationState.attempt}/${rectificationConfig.maxRetries}`, { storyId: story.id, currentFailures: rectificationState.currentFailures });
|
|
26779
26868
|
const rectificationPrompt = buildImplementerRectificationPrompt(testSummary.failures, story, contextMarkdown, rectificationConfig);
|
|
26780
26869
|
const rectifyBeforeRef = await captureGitRef(workdir) ?? "HEAD";
|
|
@@ -26790,7 +26879,9 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
26790
26879
|
maxInteractionTurns: config2.agent?.maxInteractionTurns,
|
|
26791
26880
|
featureName,
|
|
26792
26881
|
storyId: story.id,
|
|
26793
|
-
sessionRole: "implementer"
|
|
26882
|
+
sessionRole: "implementer",
|
|
26883
|
+
acpSessionName: rectificationSessionName,
|
|
26884
|
+
keepSessionOpen: !isLastAttempt
|
|
26794
26885
|
});
|
|
26795
26886
|
if (!rectifyResult.success && rectifyResult.pid) {
|
|
26796
26887
|
await cleanupProcessTree(rectifyResult.pid);
|
|
@@ -26818,7 +26909,9 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
26818
26909
|
});
|
|
26819
26910
|
break;
|
|
26820
26911
|
}
|
|
26821
|
-
const retryFullSuite = await executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
26912
|
+
const retryFullSuite = await _rectificationGateDeps.executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
26913
|
+
cwd: workdir
|
|
26914
|
+
});
|
|
26822
26915
|
const retrySuitePassed = retryFullSuite.success && retryFullSuite.exitCode === 0;
|
|
26823
26916
|
if (retrySuitePassed) {
|
|
26824
26917
|
logger.info("tdd", "Full suite gate passed after rectification!", {
|
|
@@ -26828,7 +26921,7 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
26828
26921
|
return true;
|
|
26829
26922
|
}
|
|
26830
26923
|
if (retryFullSuite.output) {
|
|
26831
|
-
const newTestSummary = parseBunTestOutput(retryFullSuite.output);
|
|
26924
|
+
const newTestSummary = _rectificationGateDeps.parseBunTestOutput(retryFullSuite.output);
|
|
26832
26925
|
rectificationState.currentFailures = newTestSummary.failed;
|
|
26833
26926
|
testSummary.failures = newTestSummary.failures;
|
|
26834
26927
|
testSummary.failed = newTestSummary.failed;
|
|
@@ -26840,7 +26933,9 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
26840
26933
|
remainingFailures: rectificationState.currentFailures
|
|
26841
26934
|
});
|
|
26842
26935
|
}
|
|
26843
|
-
const finalFullSuite = await executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
26936
|
+
const finalFullSuite = await _rectificationGateDeps.executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
26937
|
+
cwd: workdir
|
|
26938
|
+
});
|
|
26844
26939
|
const finalSuitePassed = finalFullSuite.success && finalFullSuite.exitCode === 0;
|
|
26845
26940
|
if (!finalSuitePassed) {
|
|
26846
26941
|
logger.warn("tdd", "[WARN] Full suite gate failed after rectification exhausted", {
|
|
@@ -26853,13 +26948,20 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
26853
26948
|
logger.info("tdd", "Full suite gate passed", { storyId: story.id });
|
|
26854
26949
|
return true;
|
|
26855
26950
|
}
|
|
26951
|
+
var _rectificationGateDeps;
|
|
26856
26952
|
var init_rectification_gate = __esm(() => {
|
|
26953
|
+
init_adapter2();
|
|
26857
26954
|
init_config();
|
|
26858
26955
|
init_git();
|
|
26859
26956
|
init_verification();
|
|
26860
26957
|
init_cleanup();
|
|
26861
26958
|
init_isolation();
|
|
26862
26959
|
init_prompts();
|
|
26960
|
+
_rectificationGateDeps = {
|
|
26961
|
+
executeWithTimeout,
|
|
26962
|
+
parseBunTestOutput,
|
|
26963
|
+
shouldRetryRectification
|
|
26964
|
+
};
|
|
26863
26965
|
});
|
|
26864
26966
|
|
|
26865
26967
|
// src/prompts/sections/conventions.ts
|
|
@@ -27349,7 +27451,7 @@ ${tail}`;
|
|
|
27349
27451
|
async function rollbackToRef(workdir, ref) {
|
|
27350
27452
|
const logger = getLogger();
|
|
27351
27453
|
logger.warn("tdd", "Rolling back git changes", { ref });
|
|
27352
|
-
const resetProc =
|
|
27454
|
+
const resetProc = _sessionRunnerDeps.spawn(["git", "reset", "--hard", ref], {
|
|
27353
27455
|
cwd: workdir,
|
|
27354
27456
|
stdout: "pipe",
|
|
27355
27457
|
stderr: "pipe"
|
|
@@ -27360,7 +27462,7 @@ async function rollbackToRef(workdir, ref) {
|
|
|
27360
27462
|
logger.error("tdd", "Failed to rollback git changes", { ref, stderr });
|
|
27361
27463
|
throw new Error(`Git rollback failed: ${stderr}`);
|
|
27362
27464
|
}
|
|
27363
|
-
const cleanProc =
|
|
27465
|
+
const cleanProc = _sessionRunnerDeps.spawn(["git", "clean", "-fd"], {
|
|
27364
27466
|
cwd: workdir,
|
|
27365
27467
|
stdout: "pipe",
|
|
27366
27468
|
stderr: "pipe"
|
|
@@ -27375,19 +27477,24 @@ async function rollbackToRef(workdir, ref) {
|
|
|
27375
27477
|
async function runTddSession(role, agent, story, config2, workdir, modelTier, beforeRef, contextMarkdown, lite = false, skipIsolation = false, constitution, featureName) {
|
|
27376
27478
|
const startTime = Date.now();
|
|
27377
27479
|
let prompt;
|
|
27378
|
-
|
|
27379
|
-
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27383
|
-
|
|
27384
|
-
|
|
27385
|
-
|
|
27386
|
-
|
|
27387
|
-
|
|
27480
|
+
if (_sessionRunnerDeps.buildPrompt) {
|
|
27481
|
+
prompt = await _sessionRunnerDeps.buildPrompt(role, config2, story, workdir, contextMarkdown, lite, constitution);
|
|
27482
|
+
} else {
|
|
27483
|
+
switch (role) {
|
|
27484
|
+
case "test-writer":
|
|
27485
|
+
prompt = await PromptBuilder.for("test-writer", { isolation: lite ? "lite" : "strict" }).withLoader(workdir, config2).story(story).context(contextMarkdown).constitution(constitution).testCommand(config2.quality?.commands?.test).build();
|
|
27486
|
+
break;
|
|
27487
|
+
case "implementer":
|
|
27488
|
+
prompt = await PromptBuilder.for("implementer", { variant: lite ? "lite" : "standard" }).withLoader(workdir, config2).story(story).context(contextMarkdown).constitution(constitution).testCommand(config2.quality?.commands?.test).build();
|
|
27489
|
+
break;
|
|
27490
|
+
case "verifier":
|
|
27491
|
+
prompt = await PromptBuilder.for("verifier").withLoader(workdir, config2).story(story).context(contextMarkdown).constitution(constitution).testCommand(config2.quality?.commands?.test).build();
|
|
27492
|
+
break;
|
|
27493
|
+
}
|
|
27388
27494
|
}
|
|
27389
27495
|
const logger = getLogger();
|
|
27390
27496
|
logger.info("tdd", `-> Session: ${role}`, { role, storyId: story.id, lite });
|
|
27497
|
+
const keepSessionOpen = role === "implementer" && (config2.execution.rectification?.enabled ?? false);
|
|
27391
27498
|
const result = await agent.run({
|
|
27392
27499
|
prompt,
|
|
27393
27500
|
workdir,
|
|
@@ -27400,10 +27507,11 @@ async function runTddSession(role, agent, story, config2, workdir, modelTier, be
|
|
|
27400
27507
|
maxInteractionTurns: config2.agent?.maxInteractionTurns,
|
|
27401
27508
|
featureName,
|
|
27402
27509
|
storyId: story.id,
|
|
27403
|
-
sessionRole: role
|
|
27510
|
+
sessionRole: role,
|
|
27511
|
+
keepSessionOpen
|
|
27404
27512
|
});
|
|
27405
27513
|
if (!result.success && result.pid) {
|
|
27406
|
-
await cleanupProcessTree(result.pid);
|
|
27514
|
+
await _sessionRunnerDeps.cleanupProcessTree(result.pid);
|
|
27407
27515
|
}
|
|
27408
27516
|
if (result.success) {
|
|
27409
27517
|
logger.info("tdd", `Session complete: ${role}`, {
|
|
@@ -27425,12 +27533,12 @@ async function runTddSession(role, agent, story, config2, workdir, modelTier, be
|
|
|
27425
27533
|
if (!skipIsolation) {
|
|
27426
27534
|
if (role === "test-writer") {
|
|
27427
27535
|
const allowedPaths = config2.tdd.testWriterAllowedPaths ?? ["src/index.ts", "src/**/index.ts"];
|
|
27428
|
-
isolation = await verifyTestWriterIsolation(workdir, beforeRef, allowedPaths);
|
|
27536
|
+
isolation = await _sessionRunnerDeps.verifyTestWriterIsolation(workdir, beforeRef, allowedPaths);
|
|
27429
27537
|
} else if (role === "implementer" || role === "verifier") {
|
|
27430
|
-
isolation = await verifyImplementerIsolation(workdir, beforeRef);
|
|
27538
|
+
isolation = await _sessionRunnerDeps.verifyImplementerIsolation(workdir, beforeRef);
|
|
27431
27539
|
}
|
|
27432
27540
|
}
|
|
27433
|
-
const filesChanged = await
|
|
27541
|
+
const filesChanged = await _sessionRunnerDeps.getChangedFiles(workdir, beforeRef);
|
|
27434
27542
|
const durationMs = Date.now() - startTime;
|
|
27435
27543
|
if (isolation && !isolation.passed) {
|
|
27436
27544
|
logger.error("tdd", "Isolation violated", {
|
|
@@ -27473,10 +27581,18 @@ var init_session_runner = __esm(() => {
|
|
|
27473
27581
|
init_logger2();
|
|
27474
27582
|
init_prompts2();
|
|
27475
27583
|
init_git();
|
|
27584
|
+
init_git();
|
|
27476
27585
|
init_cleanup();
|
|
27477
27586
|
init_isolation();
|
|
27478
27587
|
_sessionRunnerDeps = {
|
|
27479
|
-
autoCommitIfDirty
|
|
27588
|
+
autoCommitIfDirty,
|
|
27589
|
+
spawn: Bun.spawn,
|
|
27590
|
+
getChangedFiles: getChangedFiles2,
|
|
27591
|
+
verifyTestWriterIsolation,
|
|
27592
|
+
verifyImplementerIsolation,
|
|
27593
|
+
captureGitRef,
|
|
27594
|
+
cleanupProcessTree,
|
|
27595
|
+
buildPrompt: null
|
|
27480
27596
|
};
|
|
27481
27597
|
});
|
|
27482
27598
|
|
|
@@ -28957,7 +29073,7 @@ class AcceptanceStrategy {
|
|
|
28957
29073
|
}
|
|
28958
29074
|
const start = Date.now();
|
|
28959
29075
|
const timeoutMs = ctx.timeoutSeconds * 1000;
|
|
28960
|
-
const proc =
|
|
29076
|
+
const proc = _acceptanceDeps.spawn(["bun", "test", testPath], {
|
|
28961
29077
|
cwd: ctx.workdir,
|
|
28962
29078
|
stdout: "pipe",
|
|
28963
29079
|
stderr: "pipe"
|
|
@@ -29025,8 +29141,10 @@ ${stderr}`;
|
|
|
29025
29141
|
});
|
|
29026
29142
|
}
|
|
29027
29143
|
}
|
|
29144
|
+
var _acceptanceDeps;
|
|
29028
29145
|
var init_acceptance3 = __esm(() => {
|
|
29029
29146
|
init_logger2();
|
|
29147
|
+
_acceptanceDeps = { spawn: Bun.spawn };
|
|
29030
29148
|
});
|
|
29031
29149
|
|
|
29032
29150
|
// src/verification/strategies/regression.ts
|
|
@@ -29553,7 +29671,7 @@ var init_routing2 = __esm(() => {
|
|
|
29553
29671
|
logger.debug("routing", ctx.routing.reasoning);
|
|
29554
29672
|
}
|
|
29555
29673
|
const decomposeConfig = ctx.config.decompose;
|
|
29556
|
-
if (decomposeConfig) {
|
|
29674
|
+
if (decomposeConfig && ctx.story.status !== "decomposed") {
|
|
29557
29675
|
const acCount = ctx.story.acceptanceCriteria.length;
|
|
29558
29676
|
const complexity = ctx.routing.complexity;
|
|
29559
29677
|
const isOversized = acCount > decomposeConfig.maxAcceptanceCriteria && (complexity === "complex" || complexity === "expert");
|
|
@@ -34256,6 +34374,7 @@ async function handlePipelineFailure(ctx, pipelineResult) {
|
|
|
34256
34374
|
const logger = getSafeLogger();
|
|
34257
34375
|
let prd = ctx.prd;
|
|
34258
34376
|
let prdDirty = false;
|
|
34377
|
+
const costDelta = pipelineResult.context.agentResult?.estimatedCost || 0;
|
|
34259
34378
|
switch (pipelineResult.finalAction) {
|
|
34260
34379
|
case "pause":
|
|
34261
34380
|
markStoryPaused(prd, ctx.story.id);
|
|
@@ -34322,7 +34441,7 @@ async function handlePipelineFailure(ctx, pipelineResult) {
|
|
|
34322
34441
|
break;
|
|
34323
34442
|
}
|
|
34324
34443
|
}
|
|
34325
|
-
return { prd, prdDirty };
|
|
34444
|
+
return { prd, prdDirty, costDelta };
|
|
34326
34445
|
}
|
|
34327
34446
|
var init_pipeline_result_handler = __esm(() => {
|
|
34328
34447
|
init_logger2();
|
|
@@ -34427,7 +34546,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
34427
34546
|
return {
|
|
34428
34547
|
prd: r.prd,
|
|
34429
34548
|
storiesCompletedDelta: 0,
|
|
34430
|
-
costDelta:
|
|
34549
|
+
costDelta: r.costDelta,
|
|
34431
34550
|
prdDirty: r.prdDirty,
|
|
34432
34551
|
finalAction: pipelineResult.finalAction,
|
|
34433
34552
|
reason: pipelineResult.reason
|
|
@@ -34465,7 +34584,7 @@ function buildPreviewRouting(story, config2) {
|
|
|
34465
34584
|
function selectNextStories(prd, config2, batchPlan, currentBatchIndex, lastStoryId, useBatch) {
|
|
34466
34585
|
if (useBatch && currentBatchIndex < batchPlan.length) {
|
|
34467
34586
|
const batch = batchPlan[currentBatchIndex];
|
|
34468
|
-
const storiesToExecute = batch.stories.filter((s) => !s.passes && s.status !== "passed" && s.status !== "skipped" && s.status !== "blocked" && s.status !== "failed" && s.status !== "paused");
|
|
34587
|
+
const storiesToExecute = batch.stories.filter((s) => !s.passes && s.status !== "passed" && s.status !== "skipped" && s.status !== "blocked" && s.status !== "failed" && s.status !== "paused" && s.status !== "decomposed");
|
|
34469
34588
|
if (storiesToExecute.length === 0) {
|
|
34470
34589
|
return { selection: null, nextBatchIndex: currentBatchIndex + 1 };
|
|
34471
34590
|
}
|
|
@@ -68218,6 +68337,45 @@ class PipelineEventEmitter {
|
|
|
68218
68337
|
init_stages();
|
|
68219
68338
|
init_prd();
|
|
68220
68339
|
|
|
68340
|
+
// src/cli/prompts-shared.ts
|
|
68341
|
+
function buildFrontmatter(story, ctx, role) {
|
|
68342
|
+
const lines = [];
|
|
68343
|
+
lines.push(`storyId: ${story.id}`);
|
|
68344
|
+
lines.push(`title: "${story.title}"`);
|
|
68345
|
+
lines.push(`testStrategy: ${ctx.routing.testStrategy}`);
|
|
68346
|
+
lines.push(`modelTier: ${ctx.routing.modelTier}`);
|
|
68347
|
+
if (role) {
|
|
68348
|
+
lines.push(`role: ${role}`);
|
|
68349
|
+
}
|
|
68350
|
+
const builtContext = ctx.builtContext;
|
|
68351
|
+
const contextTokens = builtContext?.totalTokens ?? 0;
|
|
68352
|
+
const promptTokens = ctx.prompt ? Math.ceil(ctx.prompt.length / 3) : 0;
|
|
68353
|
+
lines.push(`contextTokens: ${contextTokens}`);
|
|
68354
|
+
lines.push(`promptTokens: ${promptTokens}`);
|
|
68355
|
+
if (story.dependencies && story.dependencies.length > 0) {
|
|
68356
|
+
lines.push(`dependencies: [${story.dependencies.join(", ")}]`);
|
|
68357
|
+
}
|
|
68358
|
+
lines.push("contextElements:");
|
|
68359
|
+
if (builtContext) {
|
|
68360
|
+
for (const element of builtContext.elements) {
|
|
68361
|
+
lines.push(` - type: ${element.type}`);
|
|
68362
|
+
if (element.storyId) {
|
|
68363
|
+
lines.push(` storyId: ${element.storyId}`);
|
|
68364
|
+
}
|
|
68365
|
+
if (element.filePath) {
|
|
68366
|
+
lines.push(` filePath: ${element.filePath}`);
|
|
68367
|
+
}
|
|
68368
|
+
lines.push(` tokens: ${element.tokens}`);
|
|
68369
|
+
}
|
|
68370
|
+
}
|
|
68371
|
+
if (builtContext?.truncated) {
|
|
68372
|
+
lines.push("truncated: true");
|
|
68373
|
+
}
|
|
68374
|
+
return `${lines.join(`
|
|
68375
|
+
`)}
|
|
68376
|
+
`;
|
|
68377
|
+
}
|
|
68378
|
+
|
|
68221
68379
|
// src/cli/prompts-tdd.ts
|
|
68222
68380
|
init_prompts2();
|
|
68223
68381
|
import { join as join28 } from "path";
|
|
@@ -68363,43 +68521,6 @@ ${"=".repeat(80)}`);
|
|
|
68363
68521
|
});
|
|
68364
68522
|
return processedStories;
|
|
68365
68523
|
}
|
|
68366
|
-
function buildFrontmatter(story, ctx, role) {
|
|
68367
|
-
const lines = [];
|
|
68368
|
-
lines.push(`storyId: ${story.id}`);
|
|
68369
|
-
lines.push(`title: "${story.title}"`);
|
|
68370
|
-
lines.push(`testStrategy: ${ctx.routing.testStrategy}`);
|
|
68371
|
-
lines.push(`modelTier: ${ctx.routing.modelTier}`);
|
|
68372
|
-
if (role) {
|
|
68373
|
-
lines.push(`role: ${role}`);
|
|
68374
|
-
}
|
|
68375
|
-
const builtContext = ctx.builtContext;
|
|
68376
|
-
const contextTokens = builtContext?.totalTokens ?? 0;
|
|
68377
|
-
const promptTokens = ctx.prompt ? Math.ceil(ctx.prompt.length / 3) : 0;
|
|
68378
|
-
lines.push(`contextTokens: ${contextTokens}`);
|
|
68379
|
-
lines.push(`promptTokens: ${promptTokens}`);
|
|
68380
|
-
if (story.dependencies && story.dependencies.length > 0) {
|
|
68381
|
-
lines.push(`dependencies: [${story.dependencies.join(", ")}]`);
|
|
68382
|
-
}
|
|
68383
|
-
lines.push("contextElements:");
|
|
68384
|
-
if (builtContext) {
|
|
68385
|
-
for (const element of builtContext.elements) {
|
|
68386
|
-
lines.push(` - type: ${element.type}`);
|
|
68387
|
-
if (element.storyId) {
|
|
68388
|
-
lines.push(` storyId: ${element.storyId}`);
|
|
68389
|
-
}
|
|
68390
|
-
if (element.filePath) {
|
|
68391
|
-
lines.push(` filePath: ${element.filePath}`);
|
|
68392
|
-
}
|
|
68393
|
-
lines.push(` tokens: ${element.tokens}`);
|
|
68394
|
-
}
|
|
68395
|
-
}
|
|
68396
|
-
if (builtContext?.truncated) {
|
|
68397
|
-
lines.push("truncated: true");
|
|
68398
|
-
}
|
|
68399
|
-
return `${lines.join(`
|
|
68400
|
-
`)}
|
|
68401
|
-
`;
|
|
68402
|
-
}
|
|
68403
68524
|
// src/cli/prompts-init.ts
|
|
68404
68525
|
import { existsSync as existsSync19, mkdirSync as mkdirSync4 } from "fs";
|
|
68405
68526
|
import { join as join30 } from "path";
|