@nathapp/nax 0.54.5 → 0.54.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/dist/nax.js +118 -44
- package/package.json +2 -2
package/dist/nax.js
CHANGED
|
@@ -19826,7 +19826,7 @@ async function saveAcpSession(workdir, featureName, storyId, sessionName, agentN
|
|
|
19826
19826
|
getSafeLogger()?.warn("acp-adapter", "Failed to save session to sidecar", { error: String(err) });
|
|
19827
19827
|
}
|
|
19828
19828
|
}
|
|
19829
|
-
async function clearAcpSession(workdir, featureName, storyId) {
|
|
19829
|
+
async function clearAcpSession(workdir, featureName, storyId, sessionRole) {
|
|
19830
19830
|
try {
|
|
19831
19831
|
const path = acpSessionsPath(workdir, featureName);
|
|
19832
19832
|
let data = {};
|
|
@@ -19836,7 +19836,8 @@ async function clearAcpSession(workdir, featureName, storyId) {
|
|
|
19836
19836
|
} catch {
|
|
19837
19837
|
return;
|
|
19838
19838
|
}
|
|
19839
|
-
|
|
19839
|
+
const sidecarKey = sessionRole ? `${storyId}:${sessionRole}` : storyId;
|
|
19840
|
+
delete data[sidecarKey];
|
|
19840
19841
|
await Bun.write(path, JSON.stringify(data, null, 2));
|
|
19841
19842
|
} catch (err) {
|
|
19842
19843
|
getSafeLogger()?.warn("acp-adapter", "Failed to clear session from sidecar", { error: String(err) });
|
|
@@ -20024,7 +20025,8 @@ class AcpAgentAdapter {
|
|
|
20024
20025
|
await client.start();
|
|
20025
20026
|
let sessionName = options.acpSessionName;
|
|
20026
20027
|
if (!sessionName && options.featureName && options.storyId) {
|
|
20027
|
-
|
|
20028
|
+
const sidecarKey = options.sessionRole ? `${options.storyId}:${options.sessionRole}` : options.storyId;
|
|
20029
|
+
sessionName = await readAcpSession(options.workdir, options.featureName, sidecarKey) ?? undefined;
|
|
20028
20030
|
}
|
|
20029
20031
|
sessionName ??= buildSessionName(options.workdir, options.featureName, options.storyId, options.sessionRole);
|
|
20030
20032
|
const resolvedPerm = resolvePermissions(options.config, options.pipelineStage ?? "run");
|
|
@@ -20035,7 +20037,8 @@ class AcpAgentAdapter {
|
|
|
20035
20037
|
});
|
|
20036
20038
|
const session = await ensureAcpSession(client, sessionName, this.name, permissionMode);
|
|
20037
20039
|
if (options.featureName && options.storyId) {
|
|
20038
|
-
|
|
20040
|
+
const sidecarKey = options.sessionRole ? `${options.storyId}:${options.sessionRole}` : options.storyId;
|
|
20041
|
+
await saveAcpSession(options.workdir, options.featureName, sidecarKey, sessionName, this.name);
|
|
20039
20042
|
}
|
|
20040
20043
|
let lastResponse = null;
|
|
20041
20044
|
let timedOut = false;
|
|
@@ -20072,7 +20075,8 @@ class AcpAgentAdapter {
|
|
|
20072
20075
|
totalExactCostUsd = (totalExactCostUsd ?? 0) + lastResponse.exactCostUsd;
|
|
20073
20076
|
}
|
|
20074
20077
|
const outputText = extractOutput(lastResponse);
|
|
20075
|
-
const
|
|
20078
|
+
const isEndTurn = lastResponse.stopReason === "end_turn";
|
|
20079
|
+
const question = isEndTurn ? extractQuestion(outputText) : null;
|
|
20076
20080
|
if (!question || !options.interactionBridge)
|
|
20077
20081
|
break;
|
|
20078
20082
|
getSafeLogger()?.debug("acp-adapter", "Agent asked question, routing to interactionBridge", { question });
|
|
@@ -22348,7 +22352,7 @@ var package_default;
|
|
|
22348
22352
|
var init_package = __esm(() => {
|
|
22349
22353
|
package_default = {
|
|
22350
22354
|
name: "@nathapp/nax",
|
|
22351
|
-
version: "0.54.
|
|
22355
|
+
version: "0.54.6",
|
|
22352
22356
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22353
22357
|
type: "module",
|
|
22354
22358
|
bin: {
|
|
@@ -22361,7 +22365,7 @@ var init_package = __esm(() => {
|
|
|
22361
22365
|
typecheck: "bun x tsc --noEmit",
|
|
22362
22366
|
lint: "bun x biome check src/ bin/",
|
|
22363
22367
|
release: "bun scripts/release.ts",
|
|
22364
|
-
test: "bun test test/ --timeout=60000",
|
|
22368
|
+
test: "bun test test/unit/ --timeout=60000 && bun test test/integration/ --timeout=60000 && bun test test/ui/ --timeout=60000",
|
|
22365
22369
|
"test:watch": "bun test --watch",
|
|
22366
22370
|
"test:unit": "bun test ./test/unit/ --timeout=60000",
|
|
22367
22371
|
"test:integration": "bun test ./test/integration/ --timeout=60000",
|
|
@@ -22425,8 +22429,8 @@ var init_version = __esm(() => {
|
|
|
22425
22429
|
NAX_VERSION = package_default.version;
|
|
22426
22430
|
NAX_COMMIT = (() => {
|
|
22427
22431
|
try {
|
|
22428
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22429
|
-
return "
|
|
22432
|
+
if (/^[0-9a-f]{6,10}$/.test("955ab31"))
|
|
22433
|
+
return "955ab31";
|
|
22430
22434
|
} catch {}
|
|
22431
22435
|
try {
|
|
22432
22436
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -27464,6 +27468,9 @@ function shouldRetryRectification(state, config2) {
|
|
|
27464
27468
|
if (state.attempt >= config2.maxRetries) {
|
|
27465
27469
|
return false;
|
|
27466
27470
|
}
|
|
27471
|
+
if (state.lastExitCode !== undefined && state.lastExitCode !== 0 && state.currentFailures === 0) {
|
|
27472
|
+
return true;
|
|
27473
|
+
}
|
|
27467
27474
|
if (state.currentFailures === 0) {
|
|
27468
27475
|
return false;
|
|
27469
27476
|
}
|
|
@@ -29754,7 +29761,8 @@ async function runRectificationLoop2(opts) {
|
|
|
29754
29761
|
const rectificationState = {
|
|
29755
29762
|
attempt: 0,
|
|
29756
29763
|
initialFailures: testSummary.failed,
|
|
29757
|
-
currentFailures: testSummary.failed
|
|
29764
|
+
currentFailures: testSummary.failed,
|
|
29765
|
+
lastExitCode: 1
|
|
29758
29766
|
};
|
|
29759
29767
|
logger?.info("rectification", `Starting ${label} loop`, {
|
|
29760
29768
|
storyId: story.id,
|
|
@@ -29832,6 +29840,7 @@ ${rectificationPrompt}`;
|
|
|
29832
29840
|
if (retryVerification.output) {
|
|
29833
29841
|
const newTestSummary = parseBunTestOutput(retryVerification.output);
|
|
29834
29842
|
rectificationState.currentFailures = newTestSummary.failed;
|
|
29843
|
+
rectificationState.lastExitCode = retryVerification.status === "SUCCESS" ? 0 : 1;
|
|
29835
29844
|
testSummary.failures = newTestSummary.failures;
|
|
29836
29845
|
testSummary.failed = newTestSummary.failed;
|
|
29837
29846
|
testSummary.passed = newTestSummary.passed;
|
|
@@ -30040,7 +30049,8 @@ function makeFailResult(storyId, strategy, status, opts = {}) {
|
|
|
30040
30049
|
failures: opts.failures ?? [],
|
|
30041
30050
|
rawOutput: opts.rawOutput,
|
|
30042
30051
|
durationMs: opts.durationMs ?? 0,
|
|
30043
|
-
countsTowardEscalation: opts.countsTowardEscalation ?? true
|
|
30052
|
+
countsTowardEscalation: opts.countsTowardEscalation ?? true,
|
|
30053
|
+
exitCode: opts.exitCode
|
|
30044
30054
|
};
|
|
30045
30055
|
}
|
|
30046
30056
|
function makePassResult(storyId, strategy, opts = {}) {
|
|
@@ -30449,7 +30459,8 @@ class ScopedStrategy {
|
|
|
30449
30459
|
passCount: parsed.passed,
|
|
30450
30460
|
failCount: parsed.failed,
|
|
30451
30461
|
failures: parsed.failures,
|
|
30452
|
-
durationMs
|
|
30462
|
+
durationMs,
|
|
30463
|
+
exitCode: result.status === "TEST_FAILURE" ? 1 : undefined
|
|
30453
30464
|
});
|
|
30454
30465
|
}
|
|
30455
30466
|
}
|
|
@@ -34298,7 +34309,7 @@ async function executeStoryInWorktree(story, worktreePath, context, routing, eve
|
|
|
34298
34309
|
};
|
|
34299
34310
|
}
|
|
34300
34311
|
}
|
|
34301
|
-
async function executeParallelBatch(stories, projectRoot, config2, context, worktreePaths, maxConcurrency, eventEmitter) {
|
|
34312
|
+
async function executeParallelBatch(stories, projectRoot, config2, context, worktreePaths, maxConcurrency, eventEmitter, storyEffectiveConfigs) {
|
|
34302
34313
|
const logger = getSafeLogger();
|
|
34303
34314
|
const results = {
|
|
34304
34315
|
pipelinePassed: [],
|
|
@@ -34319,7 +34330,9 @@ async function executeParallelBatch(stories, projectRoot, config2, context, work
|
|
|
34319
34330
|
continue;
|
|
34320
34331
|
}
|
|
34321
34332
|
const routing = routeTask(story.title, story.description, story.acceptanceCriteria, story.tags, config2);
|
|
34322
|
-
const
|
|
34333
|
+
const storyConfig = storyEffectiveConfigs?.get(story.id);
|
|
34334
|
+
const storyContext = storyConfig ? { ...context, effectiveConfig: storyConfig } : context;
|
|
34335
|
+
const executePromise = executeStoryInWorktree(story, worktreePath, storyContext, routing, eventEmitter).then((result) => {
|
|
34323
34336
|
results.totalCost += result.cost;
|
|
34324
34337
|
results.storyCosts.set(story.id, result.cost);
|
|
34325
34338
|
if (result.success) {
|
|
@@ -34354,6 +34367,7 @@ var init_parallel_worker = __esm(() => {
|
|
|
34354
34367
|
});
|
|
34355
34368
|
|
|
34356
34369
|
// src/execution/parallel-coordinator.ts
|
|
34370
|
+
import { existsSync as existsSync33, symlinkSync as symlinkSync2 } from "fs";
|
|
34357
34371
|
import os3 from "os";
|
|
34358
34372
|
import { join as join48 } from "path";
|
|
34359
34373
|
function groupStoriesByDependencies(stories) {
|
|
@@ -34398,7 +34412,7 @@ function resolveMaxConcurrency(parallel) {
|
|
|
34398
34412
|
}
|
|
34399
34413
|
return Math.max(1, parallel);
|
|
34400
34414
|
}
|
|
34401
|
-
async function executeParallel(stories, prdPath, projectRoot, config2, hooks, plugins, prd, featureDir, parallel, eventEmitter, agentGetFn) {
|
|
34415
|
+
async function executeParallel(stories, prdPath, projectRoot, config2, hooks, plugins, prd, featureDir, parallel, eventEmitter, agentGetFn, pidRegistry, interactionChain) {
|
|
34402
34416
|
const logger = getSafeLogger();
|
|
34403
34417
|
const maxConcurrency = resolveMaxConcurrency(parallel);
|
|
34404
34418
|
const worktreeManager = new WorktreeManager;
|
|
@@ -34430,9 +34444,12 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
34430
34444
|
hooks,
|
|
34431
34445
|
plugins,
|
|
34432
34446
|
storyStartTime: new Date().toISOString(),
|
|
34433
|
-
agentGetFn
|
|
34447
|
+
agentGetFn,
|
|
34448
|
+
pidRegistry,
|
|
34449
|
+
interaction: interactionChain ?? undefined
|
|
34434
34450
|
};
|
|
34435
34451
|
const worktreePaths = new Map;
|
|
34452
|
+
const storyEffectiveConfigs = new Map;
|
|
34436
34453
|
for (const story of batch) {
|
|
34437
34454
|
const worktreePath = join48(projectRoot, ".nax-wt", story.id);
|
|
34438
34455
|
try {
|
|
@@ -34442,6 +34459,27 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
34442
34459
|
storyId: story.id,
|
|
34443
34460
|
worktreePath
|
|
34444
34461
|
});
|
|
34462
|
+
if (story.workdir) {
|
|
34463
|
+
const pkgNodeModulesSrc = join48(projectRoot, story.workdir, "node_modules");
|
|
34464
|
+
const pkgNodeModulesDst = join48(worktreePath, story.workdir, "node_modules");
|
|
34465
|
+
if (existsSync33(pkgNodeModulesSrc) && !existsSync33(pkgNodeModulesDst)) {
|
|
34466
|
+
try {
|
|
34467
|
+
symlinkSync2(pkgNodeModulesSrc, pkgNodeModulesDst, "dir");
|
|
34468
|
+
logger?.debug("parallel", "Symlinked package node_modules", {
|
|
34469
|
+
storyId: story.id,
|
|
34470
|
+
src: pkgNodeModulesSrc
|
|
34471
|
+
});
|
|
34472
|
+
} catch (symlinkError) {
|
|
34473
|
+
logger?.warn("parallel", "Failed to symlink package node_modules \u2014 test runner may not find deps", {
|
|
34474
|
+
storyId: story.id,
|
|
34475
|
+
error: errorMessage(symlinkError)
|
|
34476
|
+
});
|
|
34477
|
+
}
|
|
34478
|
+
}
|
|
34479
|
+
}
|
|
34480
|
+
const rootConfigPath = join48(projectRoot, ".nax", "config.json");
|
|
34481
|
+
const effectiveConfig = story.workdir ? await loadConfigForWorkdir(rootConfigPath, story.workdir) : config2;
|
|
34482
|
+
storyEffectiveConfigs.set(story.id, effectiveConfig);
|
|
34445
34483
|
} catch (error48) {
|
|
34446
34484
|
markStoryFailed(currentPrd, story.id, undefined, undefined);
|
|
34447
34485
|
logger?.error("parallel", "Failed to create worktree", {
|
|
@@ -34450,7 +34488,7 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
34450
34488
|
});
|
|
34451
34489
|
}
|
|
34452
34490
|
}
|
|
34453
|
-
const batchResult = await executeParallelBatch(batch, projectRoot, config2, baseContext, worktreePaths, maxConcurrency, eventEmitter);
|
|
34491
|
+
const batchResult = await executeParallelBatch(batch, projectRoot, config2, baseContext, worktreePaths, maxConcurrency, eventEmitter, storyEffectiveConfigs);
|
|
34454
34492
|
totalCost += batchResult.totalCost;
|
|
34455
34493
|
if (batchResult.pipelinePassed.length > 0) {
|
|
34456
34494
|
const successfulIds = batchResult.pipelinePassed.map((s) => s.id);
|
|
@@ -34520,6 +34558,7 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
34520
34558
|
return { storiesCompleted, totalCost, updatedPrd: currentPrd, mergeConflicts: allMergeConflicts };
|
|
34521
34559
|
}
|
|
34522
34560
|
var init_parallel_coordinator = __esm(() => {
|
|
34561
|
+
init_loader();
|
|
34523
34562
|
init_logger2();
|
|
34524
34563
|
init_prd();
|
|
34525
34564
|
init_manager();
|
|
@@ -34807,7 +34846,7 @@ async function runParallelExecution(options, initialPrd) {
|
|
|
34807
34846
|
const batchStoryMetrics = [];
|
|
34808
34847
|
let conflictedStories = [];
|
|
34809
34848
|
try {
|
|
34810
|
-
const parallelResult = await _parallelExecutorDeps.executeParallel(readyStories, prdPath, workdir, config2, hooks, pluginRegistry, prd, featureDir, parallelCount, eventEmitter, options.agentGetFn);
|
|
34849
|
+
const parallelResult = await _parallelExecutorDeps.executeParallel(readyStories, prdPath, workdir, config2, hooks, pluginRegistry, prd, featureDir, parallelCount, eventEmitter, options.agentGetFn, options.pidRegistry, options.interactionChain);
|
|
34811
34850
|
const batchDurationMs = Date.now() - batchStartMs;
|
|
34812
34851
|
const batchCompletedAt = new Date().toISOString();
|
|
34813
34852
|
prd = parallelResult.updatedPrd;
|
|
@@ -67812,7 +67851,7 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
67812
67851
|
|
|
67813
67852
|
// bin/nax.ts
|
|
67814
67853
|
init_source();
|
|
67815
|
-
import { existsSync as
|
|
67854
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync6 } from "fs";
|
|
67816
67855
|
import { homedir as homedir8 } from "os";
|
|
67817
67856
|
import { join as join56 } from "path";
|
|
67818
67857
|
|
|
@@ -69067,9 +69106,10 @@ async function planCommand(workdir, config2, options) {
|
|
|
69067
69106
|
const timeoutSeconds = config2?.execution?.sessionTimeoutSeconds ?? 600;
|
|
69068
69107
|
let rawResponse;
|
|
69069
69108
|
if (options.auto) {
|
|
69070
|
-
const
|
|
69071
|
-
const
|
|
69072
|
-
|
|
69109
|
+
const isAcp = config2?.agent?.protocol === "acp";
|
|
69110
|
+
const prompt = buildPlanningPrompt(specContent, codebaseContext, isAcp ? outputPath : undefined, relativePackages, packageDetails, config2?.project);
|
|
69111
|
+
const adapter = _planDeps.getAgent(agentName, config2);
|
|
69112
|
+
if (!adapter)
|
|
69073
69113
|
throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
69074
69114
|
let autoModel;
|
|
69075
69115
|
try {
|
|
@@ -69080,20 +69120,52 @@ async function planCommand(workdir, config2, options) {
|
|
|
69080
69120
|
if (entry)
|
|
69081
69121
|
autoModel = resolveModel2(entry).model;
|
|
69082
69122
|
} catch {}
|
|
69083
|
-
|
|
69084
|
-
|
|
69085
|
-
|
|
69086
|
-
|
|
69087
|
-
|
|
69088
|
-
|
|
69089
|
-
|
|
69090
|
-
|
|
69091
|
-
|
|
69092
|
-
|
|
69093
|
-
|
|
69094
|
-
|
|
69123
|
+
if (isAcp) {
|
|
69124
|
+
logger?.info("plan", "Starting ACP auto planning session", {
|
|
69125
|
+
agent: agentName,
|
|
69126
|
+
model: autoModel ?? config2?.plan?.model ?? "balanced",
|
|
69127
|
+
workdir,
|
|
69128
|
+
feature: options.feature,
|
|
69129
|
+
timeoutSeconds
|
|
69130
|
+
});
|
|
69131
|
+
const pidRegistry = new PidRegistry(workdir);
|
|
69132
|
+
try {
|
|
69133
|
+
await adapter.plan({
|
|
69134
|
+
prompt,
|
|
69135
|
+
workdir,
|
|
69136
|
+
interactive: false,
|
|
69137
|
+
timeoutSeconds,
|
|
69138
|
+
config: config2,
|
|
69139
|
+
modelTier: config2?.plan?.model ?? "balanced",
|
|
69140
|
+
dangerouslySkipPermissions: resolvePermissions(config2, "plan").skipPermissions,
|
|
69141
|
+
maxInteractionTurns: config2?.agent?.maxInteractionTurns,
|
|
69142
|
+
featureName: options.feature,
|
|
69143
|
+
pidRegistry,
|
|
69144
|
+
sessionRole: "plan"
|
|
69145
|
+
});
|
|
69146
|
+
} finally {
|
|
69147
|
+
await pidRegistry.killAll().catch(() => {});
|
|
69095
69148
|
}
|
|
69096
|
-
|
|
69149
|
+
if (!_planDeps.existsSync(outputPath)) {
|
|
69150
|
+
throw new Error(`[plan] ACP agent did not write PRD to ${outputPath}. Check agent logs for errors.`);
|
|
69151
|
+
}
|
|
69152
|
+
rawResponse = await _planDeps.readFile(outputPath);
|
|
69153
|
+
} else {
|
|
69154
|
+
rawResponse = await adapter.complete(prompt, {
|
|
69155
|
+
model: autoModel,
|
|
69156
|
+
jsonMode: true,
|
|
69157
|
+
workdir,
|
|
69158
|
+
config: config2,
|
|
69159
|
+
featureName: options.feature,
|
|
69160
|
+
sessionRole: "plan"
|
|
69161
|
+
});
|
|
69162
|
+
try {
|
|
69163
|
+
const envelope = JSON.parse(rawResponse);
|
|
69164
|
+
if (envelope?.type === "result" && typeof envelope?.result === "string") {
|
|
69165
|
+
rawResponse = envelope.result;
|
|
69166
|
+
}
|
|
69167
|
+
} catch {}
|
|
69168
|
+
}
|
|
69097
69169
|
} else {
|
|
69098
69170
|
const prompt = buildPlanningPrompt(specContent, codebaseContext, outputPath, relativePackages, packageDetails, config2?.project);
|
|
69099
69171
|
const adapter = _planDeps.getAgent(agentName, config2);
|
|
@@ -72116,7 +72188,9 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
|
|
|
72116
72188
|
pluginRegistry,
|
|
72117
72189
|
formatterMode: options.formatterMode,
|
|
72118
72190
|
headless: options.headless,
|
|
72119
|
-
agentGetFn: options.agentGetFn
|
|
72191
|
+
agentGetFn: options.agentGetFn,
|
|
72192
|
+
pidRegistry: options.pidRegistry,
|
|
72193
|
+
interactionChain: options.interactionChain
|
|
72120
72194
|
}, prd);
|
|
72121
72195
|
prd = parallelResult.prd;
|
|
72122
72196
|
totalCost = parallelResult.totalCost;
|
|
@@ -79712,7 +79786,7 @@ Next: nax generate --package ${options.package}`));
|
|
|
79712
79786
|
return;
|
|
79713
79787
|
}
|
|
79714
79788
|
const naxDir = join56(workdir, ".nax");
|
|
79715
|
-
if (
|
|
79789
|
+
if (existsSync35(naxDir) && !options.force) {
|
|
79716
79790
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
79717
79791
|
return;
|
|
79718
79792
|
}
|
|
@@ -79829,7 +79903,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
79829
79903
|
console.error(source_default.red("Error: --plan requires --from <spec-path>"));
|
|
79830
79904
|
process.exit(1);
|
|
79831
79905
|
}
|
|
79832
|
-
if (options.from && !
|
|
79906
|
+
if (options.from && !existsSync35(options.from)) {
|
|
79833
79907
|
console.error(source_default.red(`Error: File not found: ${options.from} (required with --plan)`));
|
|
79834
79908
|
process.exit(1);
|
|
79835
79909
|
}
|
|
@@ -79861,7 +79935,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
79861
79935
|
const featureDir = join56(naxDir, "features", options.feature);
|
|
79862
79936
|
const prdPath = join56(featureDir, "prd.json");
|
|
79863
79937
|
if (options.plan && options.from) {
|
|
79864
|
-
if (
|
|
79938
|
+
if (existsSync35(prdPath) && !options.force) {
|
|
79865
79939
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
79866
79940
|
console.error(source_default.dim(" Use --force to overwrite, or run without --plan to use the existing PRD."));
|
|
79867
79941
|
process.exit(1);
|
|
@@ -79917,7 +79991,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
79917
79991
|
process.exit(1);
|
|
79918
79992
|
}
|
|
79919
79993
|
}
|
|
79920
|
-
if (!
|
|
79994
|
+
if (!existsSync35(prdPath)) {
|
|
79921
79995
|
console.error(source_default.red(`Feature "${options.feature}" not found or missing prd.json`));
|
|
79922
79996
|
process.exit(1);
|
|
79923
79997
|
}
|
|
@@ -79992,7 +80066,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
79992
80066
|
});
|
|
79993
80067
|
const latestSymlink = join56(runsDir, "latest.jsonl");
|
|
79994
80068
|
try {
|
|
79995
|
-
if (
|
|
80069
|
+
if (existsSync35(latestSymlink)) {
|
|
79996
80070
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
79997
80071
|
}
|
|
79998
80072
|
Bun.spawnSync(["ln", "-s", `${runId}.jsonl`, latestSymlink], {
|
|
@@ -80090,7 +80164,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
80090
80164
|
process.exit(1);
|
|
80091
80165
|
}
|
|
80092
80166
|
const featuresDir = join56(naxDir, "features");
|
|
80093
|
-
if (!
|
|
80167
|
+
if (!existsSync35(featuresDir)) {
|
|
80094
80168
|
console.log(source_default.dim("No features yet."));
|
|
80095
80169
|
return;
|
|
80096
80170
|
}
|
|
@@ -80105,7 +80179,7 @@ Features:
|
|
|
80105
80179
|
`));
|
|
80106
80180
|
for (const name of entries) {
|
|
80107
80181
|
const prdPath = join56(featuresDir, name, "prd.json");
|
|
80108
|
-
if (
|
|
80182
|
+
if (existsSync35(prdPath)) {
|
|
80109
80183
|
const prd = await loadPRD(prdPath);
|
|
80110
80184
|
const c = countStories(prd);
|
|
80111
80185
|
console.log(` ${name} \u2014 ${c.passed}/${c.total} stories done`);
|
|
@@ -80176,7 +80250,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
80176
80250
|
process.exit(1);
|
|
80177
80251
|
}
|
|
80178
80252
|
const featureDir = join56(naxDir, "features", options.feature);
|
|
80179
|
-
if (!
|
|
80253
|
+
if (!existsSync35(featureDir)) {
|
|
80180
80254
|
console.error(source_default.red(`Feature "${options.feature}" not found.`));
|
|
80181
80255
|
process.exit(1);
|
|
80182
80256
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nathapp/nax",
|
|
3
|
-
"version": "0.54.
|
|
3
|
+
"version": "0.54.6",
|
|
4
4
|
"description": "AI Coding Agent Orchestrator — loops until done",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"typecheck": "bun x tsc --noEmit",
|
|
14
14
|
"lint": "bun x biome check src/ bin/",
|
|
15
15
|
"release": "bun scripts/release.ts",
|
|
16
|
-
"test": "bun test test/ --timeout=60000",
|
|
16
|
+
"test": "bun test test/unit/ --timeout=60000 && bun test test/integration/ --timeout=60000 && bun test test/ui/ --timeout=60000",
|
|
17
17
|
"test:watch": "bun test --watch",
|
|
18
18
|
"test:unit": "bun test ./test/unit/ --timeout=60000",
|
|
19
19
|
"test:integration": "bun test ./test/integration/ --timeout=60000",
|