@nathapp/nax 0.44.0 → 0.46.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/CHANGELOG.md +19 -0
- package/bin/nax.ts +7 -6
- package/dist/nax.js +266 -161
- package/package.json +1 -1
- package/src/agents/acp/adapter.ts +34 -6
- package/src/agents/acp/index.ts +0 -2
- package/src/agents/acp/parser.ts +57 -104
- package/src/agents/acp/spawn-client.ts +2 -1
- package/src/agents/{claude.ts → claude/adapter.ts} +15 -12
- package/src/agents/{claude-complete.ts → claude/complete.ts} +3 -3
- package/src/agents/{cost.ts → claude/cost.ts} +1 -1
- package/src/agents/{claude-execution.ts → claude/execution.ts} +5 -5
- package/src/agents/claude/index.ts +3 -0
- package/src/agents/{claude-interactive.ts → claude/interactive.ts} +4 -4
- package/src/agents/{claude-plan.ts → claude/plan.ts} +12 -9
- package/src/agents/index.ts +5 -5
- package/src/agents/registry.ts +5 -5
- package/src/agents/{claude-decompose.ts → shared/decompose.ts} +7 -22
- package/src/agents/{model-resolution.ts → shared/model-resolution.ts} +2 -2
- package/src/agents/{types-extended.ts → shared/types-extended.ts} +4 -4
- package/src/agents/{validation.ts → shared/validation.ts} +2 -2
- package/src/agents/{version-detection.ts → shared/version-detection.ts} +3 -3
- package/src/agents/types.ts +8 -4
- package/src/cli/agents.ts +1 -1
- package/src/cli/plan.ts +4 -11
- package/src/config/test-strategy.ts +70 -0
- package/src/execution/lifecycle/acceptance-loop.ts +2 -0
- package/src/execution/parallel-coordinator.ts +3 -1
- package/src/execution/parallel-executor.ts +3 -0
- package/src/execution/runner-execution.ts +16 -2
- package/src/execution/story-context.ts +6 -0
- package/src/pipeline/stages/acceptance.ts +5 -8
- package/src/pipeline/stages/regression.ts +2 -0
- package/src/pipeline/stages/verify.ts +5 -10
- package/src/prd/schema.ts +4 -14
- package/src/precheck/checks-agents.ts +1 -1
- package/src/utils/log-test-output.ts +25 -0
- /package/src/agents/{adapters/aider.ts → aider/adapter.ts} +0 -0
- /package/src/agents/{adapters/codex.ts → codex/adapter.ts} +0 -0
- /package/src/agents/{adapters/gemini.ts → gemini/adapter.ts} +0 -0
- /package/src/agents/{adapters/opencode.ts → opencode/adapter.ts} +0 -0
package/dist/nax.js
CHANGED
|
@@ -3240,61 +3240,56 @@ async function withProcessTimeout(proc, timeoutMs, opts) {
|
|
|
3240
3240
|
return { exitCode, timedOut };
|
|
3241
3241
|
}
|
|
3242
3242
|
|
|
3243
|
-
// src/
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
// src/agents/claude-complete.ts
|
|
3257
|
-
async function executeComplete(binary, prompt, options) {
|
|
3258
|
-
const cmd = [binary, "-p", prompt];
|
|
3259
|
-
if (options?.model) {
|
|
3260
|
-
cmd.push("--model", options.model);
|
|
3261
|
-
}
|
|
3262
|
-
if (options?.jsonMode) {
|
|
3263
|
-
cmd.push("--output-format", "json");
|
|
3264
|
-
}
|
|
3265
|
-
const { skipPermissions } = resolvePermissions(options?.config, "complete");
|
|
3266
|
-
if (skipPermissions) {
|
|
3267
|
-
cmd.push("--dangerously-skip-permissions");
|
|
3268
|
-
}
|
|
3269
|
-
const spawnOpts = { stdout: "pipe", stderr: "pipe" };
|
|
3270
|
-
if (options?.workdir)
|
|
3271
|
-
spawnOpts.cwd = options.workdir;
|
|
3272
|
-
const proc = _completeDeps.spawn(cmd, spawnOpts);
|
|
3273
|
-
const exitCode = await proc.exited;
|
|
3274
|
-
const stdout = await new Response(proc.stdout).text();
|
|
3275
|
-
const stderr = await new Response(proc.stderr).text();
|
|
3276
|
-
const trimmed = stdout.trim();
|
|
3277
|
-
if (exitCode !== 0) {
|
|
3278
|
-
const errorDetails = stderr.trim() || trimmed;
|
|
3279
|
-
const errorMessage = errorDetails || `complete() failed with exit code ${exitCode}`;
|
|
3280
|
-
throw new CompleteError(errorMessage, exitCode);
|
|
3281
|
-
}
|
|
3282
|
-
if (!trimmed) {
|
|
3283
|
-
throw new CompleteError("complete() returned empty output");
|
|
3284
|
-
}
|
|
3285
|
-
return trimmed;
|
|
3243
|
+
// src/config/test-strategy.ts
|
|
3244
|
+
function resolveTestStrategy(raw) {
|
|
3245
|
+
if (!raw)
|
|
3246
|
+
return "test-after";
|
|
3247
|
+
if (VALID_TEST_STRATEGIES.includes(raw))
|
|
3248
|
+
return raw;
|
|
3249
|
+
if (raw === "tdd")
|
|
3250
|
+
return "tdd-simple";
|
|
3251
|
+
if (raw === "three-session")
|
|
3252
|
+
return "three-session-tdd";
|
|
3253
|
+
if (raw === "tdd-lite")
|
|
3254
|
+
return "three-session-tdd-lite";
|
|
3255
|
+
return "test-after";
|
|
3286
3256
|
}
|
|
3287
|
-
var
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3257
|
+
var VALID_TEST_STRATEGIES, COMPLEXITY_GUIDE = `## Complexity Classification Guide
|
|
3258
|
+
|
|
3259
|
+
- simple: \u226450 LOC, single-file change, purely additive, no new dependencies \u2192 test-after
|
|
3260
|
+
- medium: 50\u2013200 LOC, 2\u20135 files, standard patterns, clear requirements \u2192 tdd-simple
|
|
3261
|
+
- complex: 200\u2013500 LOC, multiple modules, new abstractions or integrations \u2192 three-session-tdd
|
|
3262
|
+
- expert: 500+ LOC, architectural changes, cross-cutting concerns, high risk \u2192 three-session-tdd-lite
|
|
3263
|
+
|
|
3264
|
+
### Security Override
|
|
3265
|
+
|
|
3266
|
+
Security-critical functions (authentication, cryptography, tokens, sessions, credentials,
|
|
3267
|
+
password hashing, access control) must be classified at MINIMUM "medium" complexity
|
|
3268
|
+
regardless of LOC count. These require at minimum "tdd-simple" test strategy.`, TEST_STRATEGY_GUIDE = `## Test Strategy Guide
|
|
3269
|
+
|
|
3270
|
+
- test-after: Simple changes with well-understood behavior. Write tests after implementation.
|
|
3271
|
+
- tdd-simple: Medium complexity. Write key tests first, implement, then fill coverage.
|
|
3272
|
+
- three-session-tdd: Complex stories. Full TDD cycle with separate test-writer and implementer sessions.
|
|
3273
|
+
- three-session-tdd-lite: Expert/high-risk stories. Full TDD with additional verifier session.`, GROUPING_RULES = `## Grouping Rules
|
|
3274
|
+
|
|
3275
|
+
- Combine small, related tasks into a single "simple" or "medium" story.
|
|
3276
|
+
- Do NOT create separate stories for every single file or function unless complex.
|
|
3277
|
+
- Do NOT create standalone stories purely for test coverage or testing.
|
|
3278
|
+
Each story's testStrategy already handles testing (tdd-simple writes tests first,
|
|
3279
|
+
three-session-tdd uses separate test-writer session, test-after writes tests after).
|
|
3280
|
+
Only create a dedicated test story for unique integration/E2E test logic that spans
|
|
3281
|
+
multiple stories and cannot be covered by individual story test strategies.
|
|
3282
|
+
- Aim for coherent units of value. Maximum recommended stories: 10-15 per feature.`;
|
|
3283
|
+
var init_test_strategy = __esm(() => {
|
|
3284
|
+
VALID_TEST_STRATEGIES = [
|
|
3285
|
+
"test-after",
|
|
3286
|
+
"tdd-simple",
|
|
3287
|
+
"three-session-tdd",
|
|
3288
|
+
"three-session-tdd-lite"
|
|
3289
|
+
];
|
|
3295
3290
|
});
|
|
3296
3291
|
|
|
3297
|
-
// src/agents/
|
|
3292
|
+
// src/agents/shared/decompose.ts
|
|
3298
3293
|
function buildDecomposePrompt(options) {
|
|
3299
3294
|
return `You are a requirements analyst. Break down the following feature specification into user stories and classify each story's complexity.
|
|
3300
3295
|
|
|
@@ -3316,24 +3311,13 @@ Decompose this spec into user stories. For each story, provide:
|
|
|
3316
3311
|
9. reasoning: Why this complexity level
|
|
3317
3312
|
10. estimatedLOC: Estimated lines of code to change
|
|
3318
3313
|
11. risks: Array of implementation risks
|
|
3319
|
-
12. testStrategy: "three-session-tdd" | "
|
|
3314
|
+
12. testStrategy: "test-after" | "tdd-simple" | "three-session-tdd" | "three-session-tdd-lite"
|
|
3320
3315
|
|
|
3321
|
-
|
|
3322
|
-
- "three-session-tdd": ONLY for complex/expert tasks that are security-critical (auth, encryption, tokens, credentials) or define public API contracts consumers depend on
|
|
3323
|
-
- "test-after": for all other tasks including simple/medium complexity
|
|
3324
|
-
- A "simple" complexity task should almost never be "three-session-tdd"
|
|
3316
|
+
${COMPLEXITY_GUIDE}
|
|
3325
3317
|
|
|
3326
|
-
|
|
3327
|
-
- simple: 1-3 files, <100 LOC, straightforward implementation, existing patterns
|
|
3328
|
-
- medium: 3-6 files, 100-300 LOC, moderate logic, some new patterns
|
|
3329
|
-
- complex: 6+ files, 300-800 LOC, architectural changes, cross-cutting concerns
|
|
3330
|
-
- expert: Security/crypto/real-time/distributed systems, >800 LOC, new infrastructure
|
|
3318
|
+
${TEST_STRATEGY_GUIDE}
|
|
3331
3319
|
|
|
3332
|
-
|
|
3333
|
-
- Combine small, related tasks (e.g., multiple utility functions, interfaces) into a single "simple" or "medium" story.
|
|
3334
|
-
- Do NOT create separate stories for every single file or function unless complex.
|
|
3335
|
-
- Aim for coherent units of value (e.g., "Implement User Authentication" vs "Create User Interface", "Create Login Service").
|
|
3336
|
-
- Maximum recommended stories: 10-15 per feature. Group aggressively if list grows too long.
|
|
3320
|
+
${GROUPING_RULES}
|
|
3337
3321
|
|
|
3338
3322
|
Consider:
|
|
3339
3323
|
1. Does infrastructure exist? (e.g., "add caching" when no cache layer exists = complex)
|
|
@@ -3402,7 +3386,7 @@ ${output.slice(0, 500)}`);
|
|
|
3402
3386
|
reasoning: String(record.reasoning || "No reasoning provided"),
|
|
3403
3387
|
estimatedLOC: Number(record.estimatedLOC) || 0,
|
|
3404
3388
|
risks: Array.isArray(record.risks) ? record.risks : [],
|
|
3405
|
-
testStrategy: record.testStrategy === "
|
|
3389
|
+
testStrategy: resolveTestStrategy(typeof record.testStrategy === "string" ? record.testStrategy : undefined)
|
|
3406
3390
|
};
|
|
3407
3391
|
});
|
|
3408
3392
|
if (stories.length === 0) {
|
|
@@ -3416,8 +3400,65 @@ function coerceComplexity(value) {
|
|
|
3416
3400
|
}
|
|
3417
3401
|
return "medium";
|
|
3418
3402
|
}
|
|
3403
|
+
var init_decompose = __esm(() => {
|
|
3404
|
+
init_test_strategy();
|
|
3405
|
+
});
|
|
3419
3406
|
|
|
3420
|
-
// src/agents/
|
|
3407
|
+
// src/agents/types.ts
|
|
3408
|
+
var CompleteError;
|
|
3409
|
+
var init_types2 = __esm(() => {
|
|
3410
|
+
CompleteError = class CompleteError extends Error {
|
|
3411
|
+
exitCode;
|
|
3412
|
+
constructor(message, exitCode) {
|
|
3413
|
+
super(message);
|
|
3414
|
+
this.exitCode = exitCode;
|
|
3415
|
+
this.name = "CompleteError";
|
|
3416
|
+
}
|
|
3417
|
+
};
|
|
3418
|
+
});
|
|
3419
|
+
|
|
3420
|
+
// src/agents/claude/complete.ts
|
|
3421
|
+
async function executeComplete(binary, prompt, options) {
|
|
3422
|
+
const cmd = [binary, "-p", prompt];
|
|
3423
|
+
if (options?.model) {
|
|
3424
|
+
cmd.push("--model", options.model);
|
|
3425
|
+
}
|
|
3426
|
+
if (options?.jsonMode) {
|
|
3427
|
+
cmd.push("--output-format", "json");
|
|
3428
|
+
}
|
|
3429
|
+
const { skipPermissions } = resolvePermissions(options?.config, "complete");
|
|
3430
|
+
if (skipPermissions) {
|
|
3431
|
+
cmd.push("--dangerously-skip-permissions");
|
|
3432
|
+
}
|
|
3433
|
+
const spawnOpts = { stdout: "pipe", stderr: "pipe" };
|
|
3434
|
+
if (options?.workdir)
|
|
3435
|
+
spawnOpts.cwd = options.workdir;
|
|
3436
|
+
const proc = _completeDeps.spawn(cmd, spawnOpts);
|
|
3437
|
+
const exitCode = await proc.exited;
|
|
3438
|
+
const stdout = await new Response(proc.stdout).text();
|
|
3439
|
+
const stderr = await new Response(proc.stderr).text();
|
|
3440
|
+
const trimmed = stdout.trim();
|
|
3441
|
+
if (exitCode !== 0) {
|
|
3442
|
+
const errorDetails = stderr.trim() || trimmed;
|
|
3443
|
+
const errorMessage = errorDetails || `complete() failed with exit code ${exitCode}`;
|
|
3444
|
+
throw new CompleteError(errorMessage, exitCode);
|
|
3445
|
+
}
|
|
3446
|
+
if (!trimmed) {
|
|
3447
|
+
throw new CompleteError("complete() returned empty output");
|
|
3448
|
+
}
|
|
3449
|
+
return trimmed;
|
|
3450
|
+
}
|
|
3451
|
+
var _completeDeps;
|
|
3452
|
+
var init_complete = __esm(() => {
|
|
3453
|
+
init_types2();
|
|
3454
|
+
_completeDeps = {
|
|
3455
|
+
spawn(cmd, opts) {
|
|
3456
|
+
return Bun.spawn(cmd, opts);
|
|
3457
|
+
}
|
|
3458
|
+
};
|
|
3459
|
+
});
|
|
3460
|
+
|
|
3461
|
+
// src/agents/claude/cost.ts
|
|
3421
3462
|
function parseTokenUsage(output) {
|
|
3422
3463
|
try {
|
|
3423
3464
|
const jsonMatch = output.match(/\{[^}]*"usage"\s*:\s*\{[^}]*"input_tokens"\s*:\s*(\d+)[^}]*"output_tokens"\s*:\s*(\d+)[^}]*\}[^}]*\}/);
|
|
@@ -3520,7 +3561,7 @@ var init_cost = __esm(() => {
|
|
|
3520
3561
|
};
|
|
3521
3562
|
});
|
|
3522
3563
|
|
|
3523
|
-
// src/agents/claude
|
|
3564
|
+
// src/agents/claude/execution.ts
|
|
3524
3565
|
function buildCommand(binary, options) {
|
|
3525
3566
|
const model = options.modelDef.model;
|
|
3526
3567
|
const { skipPermissions } = resolvePermissions(options.config, options.pipelineStage ?? "run");
|
|
@@ -3622,7 +3663,7 @@ async function executeOnce(binary, options, pidRegistry) {
|
|
|
3622
3663
|
};
|
|
3623
3664
|
}
|
|
3624
3665
|
var MAX_AGENT_OUTPUT_CHARS = 5000, MAX_AGENT_STDERR_CHARS = 1000, SIGKILL_GRACE_PERIOD_MS = 5000, _runOnceDeps;
|
|
3625
|
-
var
|
|
3666
|
+
var init_execution = __esm(() => {
|
|
3626
3667
|
init_logger2();
|
|
3627
3668
|
init_cost();
|
|
3628
3669
|
_runOnceDeps = {
|
|
@@ -3635,7 +3676,7 @@ var init_claude_execution = __esm(() => {
|
|
|
3635
3676
|
};
|
|
3636
3677
|
});
|
|
3637
3678
|
|
|
3638
|
-
// src/agents/claude
|
|
3679
|
+
// src/agents/claude/interactive.ts
|
|
3639
3680
|
function runInteractiveMode(binary, options, pidRegistry) {
|
|
3640
3681
|
const model = options.modelDef.model;
|
|
3641
3682
|
const cmd = [binary, "--model", model, options.prompt];
|
|
@@ -3674,9 +3715,9 @@ function runInteractiveMode(binary, options, pidRegistry) {
|
|
|
3674
3715
|
pid: proc.pid
|
|
3675
3716
|
};
|
|
3676
3717
|
}
|
|
3677
|
-
var
|
|
3718
|
+
var init_interactive = __esm(() => {
|
|
3678
3719
|
init_logger2();
|
|
3679
|
-
|
|
3720
|
+
init_execution();
|
|
3680
3721
|
});
|
|
3681
3722
|
|
|
3682
3723
|
// src/config/schema-types.ts
|
|
@@ -18098,7 +18139,7 @@ var init_schema = __esm(() => {
|
|
|
18098
18139
|
init_defaults();
|
|
18099
18140
|
});
|
|
18100
18141
|
|
|
18101
|
-
// src/agents/model-resolution.ts
|
|
18142
|
+
// src/agents/shared/model-resolution.ts
|
|
18102
18143
|
var exports_model_resolution = {};
|
|
18103
18144
|
__export(exports_model_resolution, {
|
|
18104
18145
|
resolveBalancedModelDef: () => resolveBalancedModelDef
|
|
@@ -18119,7 +18160,7 @@ var init_model_resolution = __esm(() => {
|
|
|
18119
18160
|
init_schema();
|
|
18120
18161
|
});
|
|
18121
18162
|
|
|
18122
|
-
// src/agents/claude
|
|
18163
|
+
// src/agents/claude/plan.ts
|
|
18123
18164
|
import { mkdtempSync, rmSync } from "fs";
|
|
18124
18165
|
import { tmpdir } from "os";
|
|
18125
18166
|
import { join } from "path";
|
|
@@ -18238,12 +18279,12 @@ async function runPlan(binary, options, pidRegistry, buildAllowedEnv2) {
|
|
|
18238
18279
|
}
|
|
18239
18280
|
}
|
|
18240
18281
|
}
|
|
18241
|
-
var
|
|
18282
|
+
var init_plan = __esm(() => {
|
|
18242
18283
|
init_logger2();
|
|
18243
18284
|
init_model_resolution();
|
|
18244
18285
|
});
|
|
18245
18286
|
|
|
18246
|
-
// src/agents/claude.ts
|
|
18287
|
+
// src/agents/claude/adapter.ts
|
|
18247
18288
|
class ClaudeCodeAdapter {
|
|
18248
18289
|
name = "claude";
|
|
18249
18290
|
displayName = "Claude Code";
|
|
@@ -18394,13 +18435,14 @@ class ClaudeCodeAdapter {
|
|
|
18394
18435
|
}
|
|
18395
18436
|
}
|
|
18396
18437
|
var _decomposeDeps, _claudeAdapterDeps;
|
|
18397
|
-
var
|
|
18438
|
+
var init_adapter = __esm(() => {
|
|
18398
18439
|
init_pid_registry();
|
|
18399
18440
|
init_logger2();
|
|
18400
|
-
|
|
18401
|
-
|
|
18402
|
-
|
|
18403
|
-
|
|
18441
|
+
init_decompose();
|
|
18442
|
+
init_complete();
|
|
18443
|
+
init_execution();
|
|
18444
|
+
init_interactive();
|
|
18445
|
+
init_plan();
|
|
18404
18446
|
_decomposeDeps = {
|
|
18405
18447
|
spawn(cmd, opts) {
|
|
18406
18448
|
return Bun.spawn(cmd, opts);
|
|
@@ -18411,6 +18453,12 @@ var init_claude = __esm(() => {
|
|
|
18411
18453
|
};
|
|
18412
18454
|
});
|
|
18413
18455
|
|
|
18456
|
+
// src/agents/claude/index.ts
|
|
18457
|
+
var init_claude = __esm(() => {
|
|
18458
|
+
init_adapter();
|
|
18459
|
+
init_execution();
|
|
18460
|
+
});
|
|
18461
|
+
|
|
18414
18462
|
// src/utils/errors.ts
|
|
18415
18463
|
function errorMessage(err) {
|
|
18416
18464
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -18935,11 +18983,40 @@ function parseAcpxJsonOutput(rawOutput) {
|
|
|
18935
18983
|
`).filter((l) => l.trim());
|
|
18936
18984
|
let text = "";
|
|
18937
18985
|
let tokenUsage;
|
|
18986
|
+
let exactCostUsd;
|
|
18938
18987
|
let stopReason;
|
|
18939
18988
|
let error48;
|
|
18940
18989
|
for (const line of lines) {
|
|
18941
18990
|
try {
|
|
18942
18991
|
const event = JSON.parse(line);
|
|
18992
|
+
if (event.jsonrpc === "2.0") {
|
|
18993
|
+
if (event.method === "session/update" && event.params?.update) {
|
|
18994
|
+
const update = event.params.update;
|
|
18995
|
+
if (update.sessionUpdate === "agent_message_chunk" && update.content?.type === "text" && update.content.text) {
|
|
18996
|
+
text += update.content.text;
|
|
18997
|
+
}
|
|
18998
|
+
if (update.sessionUpdate === "usage_update" && typeof update.cost?.amount === "number") {
|
|
18999
|
+
exactCostUsd = update.cost.amount;
|
|
19000
|
+
}
|
|
19001
|
+
}
|
|
19002
|
+
if (event.id !== undefined && event.result && typeof event.result === "object") {
|
|
19003
|
+
const result = event.result;
|
|
19004
|
+
if (result.stopReason)
|
|
19005
|
+
stopReason = result.stopReason;
|
|
19006
|
+
if (result.stop_reason)
|
|
19007
|
+
stopReason = result.stop_reason;
|
|
19008
|
+
if (result.usage && typeof result.usage === "object") {
|
|
19009
|
+
const u = result.usage;
|
|
19010
|
+
tokenUsage = {
|
|
19011
|
+
input_tokens: u.inputTokens ?? u.input_tokens ?? 0,
|
|
19012
|
+
output_tokens: u.outputTokens ?? u.output_tokens ?? 0,
|
|
19013
|
+
cache_read_input_tokens: u.cachedReadTokens ?? u.cache_read_input_tokens ?? 0,
|
|
19014
|
+
cache_creation_input_tokens: u.cachedWriteTokens ?? u.cache_creation_input_tokens ?? 0
|
|
19015
|
+
};
|
|
19016
|
+
}
|
|
19017
|
+
}
|
|
19018
|
+
continue;
|
|
19019
|
+
}
|
|
18943
19020
|
if (event.content && typeof event.content === "string")
|
|
18944
19021
|
text += event.content;
|
|
18945
19022
|
if (event.text && typeof event.text === "string")
|
|
@@ -18966,7 +19043,7 @@ function parseAcpxJsonOutput(rawOutput) {
|
|
|
18966
19043
|
text = line;
|
|
18967
19044
|
}
|
|
18968
19045
|
}
|
|
18969
|
-
return { text: text.trim(), tokenUsage, stopReason, error: error48 };
|
|
19046
|
+
return { text: text.trim(), tokenUsage, exactCostUsd, stopReason, error: error48 };
|
|
18970
19047
|
}
|
|
18971
19048
|
|
|
18972
19049
|
// src/agents/acp/spawn-client.ts
|
|
@@ -19065,8 +19142,9 @@ class SpawnAcpSession {
|
|
|
19065
19142
|
const parsed = parseAcpxJsonOutput(stdout);
|
|
19066
19143
|
return {
|
|
19067
19144
|
messages: [{ role: "assistant", content: parsed.text || "" }],
|
|
19068
|
-
stopReason: "end_turn",
|
|
19069
|
-
cumulative_token_usage: parsed.tokenUsage
|
|
19145
|
+
stopReason: parsed.stopReason ?? "end_turn",
|
|
19146
|
+
cumulative_token_usage: parsed.tokenUsage,
|
|
19147
|
+
exactCostUsd: parsed.exactCostUsd
|
|
19070
19148
|
};
|
|
19071
19149
|
} catch (err) {
|
|
19072
19150
|
getSafeLogger()?.warn("acp-adapter", "Failed to parse session prompt response", {
|
|
@@ -19527,7 +19605,13 @@ class AcpAgentAdapter {
|
|
|
19527
19605
|
let lastResponse = null;
|
|
19528
19606
|
let timedOut = false;
|
|
19529
19607
|
const runState = { succeeded: false };
|
|
19530
|
-
const totalTokenUsage = {
|
|
19608
|
+
const totalTokenUsage = {
|
|
19609
|
+
input_tokens: 0,
|
|
19610
|
+
output_tokens: 0,
|
|
19611
|
+
cache_read_input_tokens: 0,
|
|
19612
|
+
cache_creation_input_tokens: 0
|
|
19613
|
+
};
|
|
19614
|
+
let totalExactCostUsd;
|
|
19531
19615
|
try {
|
|
19532
19616
|
let currentPrompt = options.prompt;
|
|
19533
19617
|
let turnCount = 0;
|
|
@@ -19546,6 +19630,11 @@ class AcpAgentAdapter {
|
|
|
19546
19630
|
if (lastResponse.cumulative_token_usage) {
|
|
19547
19631
|
totalTokenUsage.input_tokens += lastResponse.cumulative_token_usage.input_tokens ?? 0;
|
|
19548
19632
|
totalTokenUsage.output_tokens += lastResponse.cumulative_token_usage.output_tokens ?? 0;
|
|
19633
|
+
totalTokenUsage.cache_read_input_tokens += lastResponse.cumulative_token_usage.cache_read_input_tokens ?? 0;
|
|
19634
|
+
totalTokenUsage.cache_creation_input_tokens += lastResponse.cumulative_token_usage.cache_creation_input_tokens ?? 0;
|
|
19635
|
+
}
|
|
19636
|
+
if (lastResponse.exactCostUsd !== undefined) {
|
|
19637
|
+
totalExactCostUsd = (totalExactCostUsd ?? 0) + lastResponse.exactCostUsd;
|
|
19549
19638
|
}
|
|
19550
19639
|
const outputText = extractOutput(lastResponse);
|
|
19551
19640
|
const question = extractQuestion(outputText);
|
|
@@ -19592,7 +19681,7 @@ class AcpAgentAdapter {
|
|
|
19592
19681
|
}
|
|
19593
19682
|
const success2 = lastResponse?.stopReason === "end_turn";
|
|
19594
19683
|
const output = extractOutput(lastResponse);
|
|
19595
|
-
const estimatedCost = totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0;
|
|
19684
|
+
const estimatedCost = totalExactCostUsd ?? (totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0);
|
|
19596
19685
|
return {
|
|
19597
19686
|
success: success2,
|
|
19598
19687
|
exitCode: success2 ? 0 : 1,
|
|
@@ -19642,6 +19731,12 @@ class AcpAgentAdapter {
|
|
|
19642
19731
|
if (!unwrapped) {
|
|
19643
19732
|
throw new CompleteError("complete() returned empty output");
|
|
19644
19733
|
}
|
|
19734
|
+
if (response.exactCostUsd !== undefined) {
|
|
19735
|
+
getSafeLogger()?.info("acp-adapter", "complete() cost", {
|
|
19736
|
+
costUsd: response.exactCostUsd,
|
|
19737
|
+
model
|
|
19738
|
+
});
|
|
19739
|
+
}
|
|
19645
19740
|
return unwrapped;
|
|
19646
19741
|
} catch (err) {
|
|
19647
19742
|
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
@@ -19728,8 +19823,9 @@ class AcpAgentAdapter {
|
|
|
19728
19823
|
}
|
|
19729
19824
|
}
|
|
19730
19825
|
var MAX_AGENT_OUTPUT_CHARS2 = 5000, MAX_RATE_LIMIT_RETRIES = 3, INTERACTION_TIMEOUT_MS, AGENT_REGISTRY, DEFAULT_ENTRY, _acpAdapterDeps, MAX_SESSION_AGE_MS;
|
|
19731
|
-
var
|
|
19826
|
+
var init_adapter2 = __esm(() => {
|
|
19732
19827
|
init_logger2();
|
|
19828
|
+
init_decompose();
|
|
19733
19829
|
init_spawn_client();
|
|
19734
19830
|
init_types2();
|
|
19735
19831
|
init_cost2();
|
|
@@ -19774,7 +19870,7 @@ var init_adapter = __esm(() => {
|
|
|
19774
19870
|
MAX_SESSION_AGE_MS = 2 * 60 * 60 * 1000;
|
|
19775
19871
|
});
|
|
19776
19872
|
|
|
19777
|
-
// src/agents/
|
|
19873
|
+
// src/agents/aider/adapter.ts
|
|
19778
19874
|
class AiderAdapter {
|
|
19779
19875
|
name = "aider";
|
|
19780
19876
|
displayName = "Aider";
|
|
@@ -19839,7 +19935,7 @@ class AiderAdapter {
|
|
|
19839
19935
|
}
|
|
19840
19936
|
}
|
|
19841
19937
|
var _aiderCompleteDeps, MAX_AGENT_OUTPUT_CHARS3 = 5000;
|
|
19842
|
-
var
|
|
19938
|
+
var init_adapter3 = __esm(() => {
|
|
19843
19939
|
init_types2();
|
|
19844
19940
|
_aiderCompleteDeps = {
|
|
19845
19941
|
which(name) {
|
|
@@ -19851,7 +19947,7 @@ var init_aider = __esm(() => {
|
|
|
19851
19947
|
};
|
|
19852
19948
|
});
|
|
19853
19949
|
|
|
19854
|
-
// src/agents/
|
|
19950
|
+
// src/agents/codex/adapter.ts
|
|
19855
19951
|
class CodexAdapter {
|
|
19856
19952
|
name = "codex";
|
|
19857
19953
|
displayName = "Codex";
|
|
@@ -19914,7 +20010,7 @@ class CodexAdapter {
|
|
|
19914
20010
|
}
|
|
19915
20011
|
}
|
|
19916
20012
|
var _codexRunDeps, _codexCompleteDeps, MAX_AGENT_OUTPUT_CHARS4 = 5000;
|
|
19917
|
-
var
|
|
20013
|
+
var init_adapter4 = __esm(() => {
|
|
19918
20014
|
init_types2();
|
|
19919
20015
|
_codexRunDeps = {
|
|
19920
20016
|
which(name) {
|
|
@@ -19931,7 +20027,7 @@ var init_codex = __esm(() => {
|
|
|
19931
20027
|
};
|
|
19932
20028
|
});
|
|
19933
20029
|
|
|
19934
|
-
// src/agents/
|
|
20030
|
+
// src/agents/gemini/adapter.ts
|
|
19935
20031
|
class GeminiAdapter {
|
|
19936
20032
|
name = "gemini";
|
|
19937
20033
|
displayName = "Gemini CLI";
|
|
@@ -20014,7 +20110,7 @@ class GeminiAdapter {
|
|
|
20014
20110
|
}
|
|
20015
20111
|
}
|
|
20016
20112
|
var _geminiRunDeps, _geminiCompleteDeps, MAX_AGENT_OUTPUT_CHARS5 = 5000;
|
|
20017
|
-
var
|
|
20113
|
+
var init_adapter5 = __esm(() => {
|
|
20018
20114
|
init_types2();
|
|
20019
20115
|
_geminiRunDeps = {
|
|
20020
20116
|
which(name) {
|
|
@@ -20031,7 +20127,7 @@ var init_gemini = __esm(() => {
|
|
|
20031
20127
|
};
|
|
20032
20128
|
});
|
|
20033
20129
|
|
|
20034
|
-
// src/agents/
|
|
20130
|
+
// src/agents/opencode/adapter.ts
|
|
20035
20131
|
class OpenCodeAdapter {
|
|
20036
20132
|
name = "opencode";
|
|
20037
20133
|
displayName = "OpenCode";
|
|
@@ -20076,7 +20172,7 @@ class OpenCodeAdapter {
|
|
|
20076
20172
|
}
|
|
20077
20173
|
}
|
|
20078
20174
|
var _opencodeCompleteDeps;
|
|
20079
|
-
var
|
|
20175
|
+
var init_adapter6 = __esm(() => {
|
|
20080
20176
|
init_types2();
|
|
20081
20177
|
_opencodeCompleteDeps = {
|
|
20082
20178
|
which(name) {
|
|
@@ -20168,12 +20264,12 @@ function createAgentRegistry(config2) {
|
|
|
20168
20264
|
var ALL_AGENTS;
|
|
20169
20265
|
var init_registry = __esm(() => {
|
|
20170
20266
|
init_logger2();
|
|
20267
|
+
init_adapter2();
|
|
20268
|
+
init_adapter3();
|
|
20171
20269
|
init_adapter();
|
|
20172
|
-
|
|
20173
|
-
|
|
20174
|
-
|
|
20175
|
-
init_opencode();
|
|
20176
|
-
init_claude();
|
|
20270
|
+
init_adapter4();
|
|
20271
|
+
init_adapter5();
|
|
20272
|
+
init_adapter6();
|
|
20177
20273
|
ALL_AGENTS = [
|
|
20178
20274
|
new ClaudeCodeAdapter,
|
|
20179
20275
|
new CodexAdapter,
|
|
@@ -22042,7 +22138,7 @@ var package_default;
|
|
|
22042
22138
|
var init_package = __esm(() => {
|
|
22043
22139
|
package_default = {
|
|
22044
22140
|
name: "@nathapp/nax",
|
|
22045
|
-
version: "0.
|
|
22141
|
+
version: "0.46.0",
|
|
22046
22142
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22047
22143
|
type: "module",
|
|
22048
22144
|
bin: {
|
|
@@ -22115,8 +22211,8 @@ var init_version = __esm(() => {
|
|
|
22115
22211
|
NAX_VERSION = package_default.version;
|
|
22116
22212
|
NAX_COMMIT = (() => {
|
|
22117
22213
|
try {
|
|
22118
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22119
|
-
return "
|
|
22214
|
+
if (/^[0-9a-f]{6,10}$/.test("6a485b9"))
|
|
22215
|
+
return "6a485b9";
|
|
22120
22216
|
} catch {}
|
|
22121
22217
|
try {
|
|
22122
22218
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23716,6 +23812,20 @@ var init_runner = __esm(() => {
|
|
|
23716
23812
|
init_logger2();
|
|
23717
23813
|
});
|
|
23718
23814
|
|
|
23815
|
+
// src/utils/log-test-output.ts
|
|
23816
|
+
function logTestOutput(logger, stage, output, opts = {}) {
|
|
23817
|
+
if (!logger || !output)
|
|
23818
|
+
return;
|
|
23819
|
+
const tailLines = opts.tailLines ?? 20;
|
|
23820
|
+
const lines = output.split(`
|
|
23821
|
+
`).slice(-tailLines).join(`
|
|
23822
|
+
`);
|
|
23823
|
+
logger.debug(stage, "Test output (tail)", {
|
|
23824
|
+
...opts.storyId !== undefined && { storyId: opts.storyId },
|
|
23825
|
+
output: lines
|
|
23826
|
+
});
|
|
23827
|
+
}
|
|
23828
|
+
|
|
23719
23829
|
// src/pipeline/stages/acceptance.ts
|
|
23720
23830
|
var exports_acceptance = {};
|
|
23721
23831
|
__export(exports_acceptance, {
|
|
@@ -23799,10 +23909,8 @@ ${stderr}`;
|
|
|
23799
23909
|
return { action: "continue" };
|
|
23800
23910
|
}
|
|
23801
23911
|
if (failedACs.length === 0 && exitCode !== 0) {
|
|
23802
|
-
logger.error("acceptance", "Tests errored with no AC failures parsed", {
|
|
23803
|
-
|
|
23804
|
-
output
|
|
23805
|
-
});
|
|
23912
|
+
logger.error("acceptance", "Tests errored with no AC failures parsed", { exitCode });
|
|
23913
|
+
logTestOutput(logger, "acceptance", output);
|
|
23806
23914
|
ctx.acceptanceFailures = {
|
|
23807
23915
|
failedACs: ["AC-ERROR"],
|
|
23808
23916
|
testOutput: output
|
|
@@ -23820,10 +23928,8 @@ ${stderr}`;
|
|
|
23820
23928
|
overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] }))
|
|
23821
23929
|
});
|
|
23822
23930
|
}
|
|
23823
|
-
logger.error("acceptance", "Acceptance tests failed", {
|
|
23824
|
-
|
|
23825
|
-
output
|
|
23826
|
-
});
|
|
23931
|
+
logger.error("acceptance", "Acceptance tests failed", { failedACs: actualFailures });
|
|
23932
|
+
logTestOutput(logger, "acceptance", output);
|
|
23827
23933
|
ctx.acceptanceFailures = {
|
|
23828
23934
|
failedACs: actualFailures,
|
|
23829
23935
|
testOutput: output
|
|
@@ -25394,6 +25500,11 @@ async function buildStoryContextFull(prd, story, config2) {
|
|
|
25394
25500
|
}
|
|
25395
25501
|
function getAllReadyStories(prd) {
|
|
25396
25502
|
const completedIds = new Set(prd.userStories.filter((s) => s.passes || s.status === "skipped").map((s) => s.id));
|
|
25503
|
+
const logger = getSafeLogger2();
|
|
25504
|
+
logger?.debug("routing", "getAllReadyStories: completed set", {
|
|
25505
|
+
completedIds: [...completedIds],
|
|
25506
|
+
totalStories: prd.userStories.length
|
|
25507
|
+
});
|
|
25397
25508
|
return prd.userStories.filter((s) => !s.passes && s.status !== "skipped" && s.status !== "failed" && s.status !== "paused" && s.status !== "blocked" && s.dependencies.every((dep) => completedIds.has(dep)));
|
|
25398
25509
|
}
|
|
25399
25510
|
var CONTEXT_MAX_TOKENS = 1e5, CONTEXT_RESERVED_TOKENS = 1e4;
|
|
@@ -25567,7 +25678,7 @@ ${pluginMarkdown}` : pluginMarkdown;
|
|
|
25567
25678
|
};
|
|
25568
25679
|
});
|
|
25569
25680
|
|
|
25570
|
-
// src/agents/validation.ts
|
|
25681
|
+
// src/agents/shared/validation.ts
|
|
25571
25682
|
function validateAgentForTier(agent, tier) {
|
|
25572
25683
|
return agent.capabilities.supportedTiers.includes(tier);
|
|
25573
25684
|
}
|
|
@@ -25581,7 +25692,7 @@ function describeAgentCapabilities(agent) {
|
|
|
25581
25692
|
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
25582
25693
|
}
|
|
25583
25694
|
|
|
25584
|
-
// src/agents/version-detection.ts
|
|
25695
|
+
// src/agents/shared/version-detection.ts
|
|
25585
25696
|
async function getAgentVersion(binaryName) {
|
|
25586
25697
|
try {
|
|
25587
25698
|
const proc = _versionDetectionDeps.spawn([binaryName, "--version"], {
|
|
@@ -27694,7 +27805,7 @@ function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason) {
|
|
|
27694
27805
|
};
|
|
27695
27806
|
}
|
|
27696
27807
|
var executionStage, _executionDeps;
|
|
27697
|
-
var
|
|
27808
|
+
var init_execution2 = __esm(() => {
|
|
27698
27809
|
init_agents();
|
|
27699
27810
|
init_config();
|
|
27700
27811
|
init_triggers();
|
|
@@ -29049,6 +29160,7 @@ var init_regression2 = __esm(() => {
|
|
|
29049
29160
|
storyId: ctx.story.id,
|
|
29050
29161
|
failCount: result.failCount
|
|
29051
29162
|
});
|
|
29163
|
+
logTestOutput(logger, "regression", result.rawOutput, { storyId: ctx.story.id });
|
|
29052
29164
|
pipelineEventBus.emit({
|
|
29053
29165
|
type: "regression:detected",
|
|
29054
29166
|
storyId: ctx.story.id,
|
|
@@ -29347,16 +29459,8 @@ var init_verify = __esm(() => {
|
|
|
29347
29459
|
storyId: ctx.story.id
|
|
29348
29460
|
});
|
|
29349
29461
|
}
|
|
29350
|
-
if (result.
|
|
29351
|
-
|
|
29352
|
-
`).slice(-20);
|
|
29353
|
-
if (outputLines.length > 0) {
|
|
29354
|
-
logger.debug("verify", "Test output preview", {
|
|
29355
|
-
storyId: ctx.story.id,
|
|
29356
|
-
output: outputLines.join(`
|
|
29357
|
-
`)
|
|
29358
|
-
});
|
|
29359
|
-
}
|
|
29462
|
+
if (result.status !== "TIMEOUT") {
|
|
29463
|
+
logTestOutput(logger, "verify", result.output, { storyId: ctx.story.id });
|
|
29360
29464
|
}
|
|
29361
29465
|
return {
|
|
29362
29466
|
action: "escalate",
|
|
@@ -29401,7 +29505,7 @@ var init_stages = __esm(() => {
|
|
|
29401
29505
|
init_completion();
|
|
29402
29506
|
init_constitution2();
|
|
29403
29507
|
init_context2();
|
|
29404
|
-
|
|
29508
|
+
init_execution2();
|
|
29405
29509
|
init_optimizer2();
|
|
29406
29510
|
init_prompt();
|
|
29407
29511
|
init_queue_check();
|
|
@@ -29416,7 +29520,7 @@ var init_stages = __esm(() => {
|
|
|
29416
29520
|
init_context2();
|
|
29417
29521
|
init_prompt();
|
|
29418
29522
|
init_optimizer2();
|
|
29419
|
-
|
|
29523
|
+
init_execution2();
|
|
29420
29524
|
init_verify();
|
|
29421
29525
|
init_rectify();
|
|
29422
29526
|
init_review();
|
|
@@ -31237,7 +31341,8 @@ async function executeFixStory(ctx, story, prd, iterations) {
|
|
|
31237
31341
|
featureDir: ctx.featureDir,
|
|
31238
31342
|
hooks: ctx.hooks,
|
|
31239
31343
|
plugins: ctx.pluginRegistry,
|
|
31240
|
-
storyStartTime: new Date().toISOString()
|
|
31344
|
+
storyStartTime: new Date().toISOString(),
|
|
31345
|
+
agentGetFn: ctx.agentGetFn
|
|
31241
31346
|
};
|
|
31242
31347
|
const result = await runPipeline(defaultPipeline, fixContext, ctx.eventEmitter);
|
|
31243
31348
|
logger?.info("acceptance", `Fix story ${story.id} ${result.success ? "passed" : "failed"}`);
|
|
@@ -31273,7 +31378,8 @@ async function runAcceptanceLoop(ctx) {
|
|
|
31273
31378
|
workdir: ctx.workdir,
|
|
31274
31379
|
featureDir: ctx.featureDir,
|
|
31275
31380
|
hooks: ctx.hooks,
|
|
31276
|
-
plugins: ctx.pluginRegistry
|
|
31381
|
+
plugins: ctx.pluginRegistry,
|
|
31382
|
+
agentGetFn: ctx.agentGetFn
|
|
31277
31383
|
};
|
|
31278
31384
|
const { acceptanceStage: acceptanceStage2 } = await Promise.resolve().then(() => (init_acceptance2(), exports_acceptance));
|
|
31279
31385
|
const acceptanceResult = await acceptanceStage2.execute(acceptanceContext);
|
|
@@ -32254,7 +32360,7 @@ function resolveMaxConcurrency(parallel) {
|
|
|
32254
32360
|
}
|
|
32255
32361
|
return Math.max(1, parallel);
|
|
32256
32362
|
}
|
|
32257
|
-
async function executeParallel(stories, prdPath, projectRoot, config2, hooks, plugins, prd, featureDir, parallel, eventEmitter) {
|
|
32363
|
+
async function executeParallel(stories, prdPath, projectRoot, config2, hooks, plugins, prd, featureDir, parallel, eventEmitter, agentGetFn) {
|
|
32258
32364
|
const logger = getSafeLogger();
|
|
32259
32365
|
const maxConcurrency = resolveMaxConcurrency(parallel);
|
|
32260
32366
|
const worktreeManager = new WorktreeManager;
|
|
@@ -32284,7 +32390,8 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
32284
32390
|
featureDir,
|
|
32285
32391
|
hooks,
|
|
32286
32392
|
plugins,
|
|
32287
|
-
storyStartTime: new Date().toISOString()
|
|
32393
|
+
storyStartTime: new Date().toISOString(),
|
|
32394
|
+
agentGetFn
|
|
32288
32395
|
};
|
|
32289
32396
|
const worktreePaths = new Map;
|
|
32290
32397
|
for (const story of batch) {
|
|
@@ -32658,7 +32765,7 @@ async function runParallelExecution(options, initialPrd) {
|
|
|
32658
32765
|
const batchStoryMetrics = [];
|
|
32659
32766
|
let conflictedStories = [];
|
|
32660
32767
|
try {
|
|
32661
|
-
const parallelResult = await _parallelExecutorDeps.executeParallel(readyStories, prdPath, workdir, config2, hooks, pluginRegistry, prd, featureDir, parallelCount, eventEmitter);
|
|
32768
|
+
const parallelResult = await _parallelExecutorDeps.executeParallel(readyStories, prdPath, workdir, config2, hooks, pluginRegistry, prd, featureDir, parallelCount, eventEmitter, options.agentGetFn);
|
|
32662
32769
|
const batchDurationMs = Date.now() - batchStartMs;
|
|
32663
32770
|
const batchCompletedAt = new Date().toISOString();
|
|
32664
32771
|
prd = parallelResult.updatedPrd;
|
|
@@ -34403,7 +34510,7 @@ async function setupRun(options) {
|
|
|
34403
34510
|
} else {
|
|
34404
34511
|
logger?.warn("precheck", "Precheck validations skipped (--skip-precheck)");
|
|
34405
34512
|
}
|
|
34406
|
-
const { sweepStaleFeatureSessions: sweepStaleFeatureSessions2 } = await Promise.resolve().then(() => (
|
|
34513
|
+
const { sweepStaleFeatureSessions: sweepStaleFeatureSessions2 } = await Promise.resolve().then(() => (init_adapter2(), exports_adapter));
|
|
34407
34514
|
await sweepStaleFeatureSessions2(workdir, feature).catch(() => {});
|
|
34408
34515
|
const lockAcquired = await acquireLock(workdir);
|
|
34409
34516
|
if (!lockAcquired) {
|
|
@@ -65854,17 +65961,13 @@ init_registry();
|
|
|
65854
65961
|
import { existsSync as existsSync9 } from "fs";
|
|
65855
65962
|
import { join as join10 } from "path";
|
|
65856
65963
|
import { createInterface } from "readline";
|
|
65964
|
+
init_test_strategy();
|
|
65857
65965
|
init_pid_registry();
|
|
65858
65966
|
init_logger2();
|
|
65859
65967
|
|
|
65860
65968
|
// src/prd/schema.ts
|
|
65969
|
+
init_test_strategy();
|
|
65861
65970
|
var VALID_COMPLEXITY = ["simple", "medium", "complex", "expert"];
|
|
65862
|
-
var VALID_TEST_STRATEGIES = [
|
|
65863
|
-
"test-after",
|
|
65864
|
-
"tdd-simple",
|
|
65865
|
-
"three-session-tdd",
|
|
65866
|
-
"three-session-tdd-lite"
|
|
65867
|
-
];
|
|
65868
65971
|
var STORY_ID_NO_SEPARATOR = /^([A-Za-z]+)(\d+)$/;
|
|
65869
65972
|
function extractJsonFromMarkdown(text) {
|
|
65870
65973
|
const match = text.match(/```(?:json)?\s*\n([\s\S]*?)\n?\s*```/);
|
|
@@ -65934,9 +66037,7 @@ function validateStory(raw, index, allIds) {
|
|
|
65934
66037
|
throw new Error(`[schema] story[${index}].routing.complexity "${rawComplexity}" is invalid. Valid values: ${VALID_COMPLEXITY.join(", ")}`);
|
|
65935
66038
|
}
|
|
65936
66039
|
const rawTestStrategy = routing.testStrategy ?? s.testStrategy;
|
|
65937
|
-
const
|
|
65938
|
-
const normalizedStrategy = typeof rawTestStrategy === "string" ? STRATEGY_ALIASES[rawTestStrategy] ?? rawTestStrategy : rawTestStrategy;
|
|
65939
|
-
const testStrategy = normalizedStrategy !== undefined && VALID_TEST_STRATEGIES.includes(normalizedStrategy) ? normalizedStrategy : "tdd-simple";
|
|
66040
|
+
const testStrategy = resolveTestStrategy(typeof rawTestStrategy === "string" ? rawTestStrategy : undefined);
|
|
65940
66041
|
const rawDeps = s.dependencies;
|
|
65941
66042
|
const dependencies = Array.isArray(rawDeps) ? rawDeps : [];
|
|
65942
66043
|
for (const dep of dependencies) {
|
|
@@ -66203,19 +66304,11 @@ Generate a JSON object with this exact structure (no markdown, no explanation \u
|
|
|
66203
66304
|
]
|
|
66204
66305
|
}
|
|
66205
66306
|
|
|
66206
|
-
|
|
66307
|
+
${COMPLEXITY_GUIDE}
|
|
66207
66308
|
|
|
66208
|
-
|
|
66209
|
-
- medium: 50\u2013200 LOC, 2\u20135 files, standard patterns, clear requirements \u2192 tdd-simple
|
|
66210
|
-
- complex: 200\u2013500 LOC, multiple modules, new abstractions or integrations \u2192 three-session-tdd
|
|
66211
|
-
- expert: 500+ LOC, architectural changes, cross-cutting concerns, high risk \u2192 three-session-tdd-lite
|
|
66309
|
+
${TEST_STRATEGY_GUIDE}
|
|
66212
66310
|
|
|
66213
|
-
|
|
66214
|
-
|
|
66215
|
-
- test-after: Simple changes with well-understood behavior. Write tests after implementation.
|
|
66216
|
-
- tdd-simple: Medium complexity. Write key tests first, implement, then fill coverage.
|
|
66217
|
-
- three-session-tdd: Complex stories. Full TDD cycle with separate test-writer and implementer sessions.
|
|
66218
|
-
- three-session-tdd-lite: Expert/high-risk stories. Full TDD with additional verifier session.
|
|
66311
|
+
${GROUPING_RULES}
|
|
66219
66312
|
|
|
66220
66313
|
${outputFilePath ? `Write the PRD JSON directly to this file path: ${outputFilePath}
|
|
66221
66314
|
Do NOT output the JSON to the conversation. Write the file, then reply with a brief confirmation.` : "Output ONLY the JSON object. Do not wrap in markdown code blocks."}`;
|
|
@@ -69051,7 +69144,7 @@ async function unlockCommand(options) {
|
|
|
69051
69144
|
init_config();
|
|
69052
69145
|
|
|
69053
69146
|
// src/execution/runner.ts
|
|
69054
|
-
|
|
69147
|
+
init_adapter2();
|
|
69055
69148
|
init_registry();
|
|
69056
69149
|
init_hooks();
|
|
69057
69150
|
init_logger2();
|
|
@@ -69227,9 +69320,20 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
|
|
|
69227
69320
|
batchingEnabled: options.useBatch
|
|
69228
69321
|
});
|
|
69229
69322
|
clearCache();
|
|
69230
|
-
const
|
|
69323
|
+
const readyStories = getAllReadyStories(prd);
|
|
69324
|
+
logger?.debug("routing", "Ready stories for batch routing", {
|
|
69325
|
+
readyCount: readyStories.length,
|
|
69326
|
+
readyIds: readyStories.map((s) => s.id),
|
|
69327
|
+
allStories: prd.userStories.map((s) => ({
|
|
69328
|
+
id: s.id,
|
|
69329
|
+
status: s.status,
|
|
69330
|
+
passes: s.passes,
|
|
69331
|
+
deps: s.dependencies
|
|
69332
|
+
}))
|
|
69333
|
+
});
|
|
69334
|
+
const batchPlan = options.useBatch ? precomputeBatchPlan(readyStories, 4) : [];
|
|
69231
69335
|
if (options.useBatch) {
|
|
69232
|
-
await tryLlmBatchRoute(options.config,
|
|
69336
|
+
await tryLlmBatchRoute(options.config, readyStories, "routing");
|
|
69233
69337
|
}
|
|
69234
69338
|
if (options.parallel !== undefined) {
|
|
69235
69339
|
const runParallelExecution2 = options.runParallelExecution ?? (await Promise.resolve().then(() => (init_parallel_executor(), exports_parallel_executor))).runParallelExecution;
|
|
@@ -76991,9 +77095,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
76991
77095
|
}
|
|
76992
77096
|
}
|
|
76993
77097
|
try {
|
|
76994
|
-
|
|
77098
|
+
const planLogDir = join43(featureDir, "plan");
|
|
77099
|
+
mkdirSync6(planLogDir, { recursive: true });
|
|
76995
77100
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
76996
|
-
const planLogPath = join43(
|
|
77101
|
+
const planLogPath = join43(planLogDir, `${planLogId}.jsonl`);
|
|
76997
77102
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
76998
77103
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
76999
77104
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -77238,10 +77343,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
77238
77343
|
process.exit(1);
|
|
77239
77344
|
}
|
|
77240
77345
|
const config2 = await loadConfig(workdir);
|
|
77241
|
-
const featureLogDir = join43(naxDir, "features", options.feature);
|
|
77346
|
+
const featureLogDir = join43(naxDir, "features", options.feature, "plan");
|
|
77242
77347
|
mkdirSync6(featureLogDir, { recursive: true });
|
|
77243
77348
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
77244
|
-
const planLogPath = join43(featureLogDir,
|
|
77349
|
+
const planLogPath = join43(featureLogDir, `${planLogId}.jsonl`);
|
|
77245
77350
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
77246
77351
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
77247
77352
|
try {
|