@nathapp/nax 0.48.1 → 0.48.3
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 +209 -174
- package/package.json +1 -1
- package/src/agents/acp/spawn-client.ts +1 -1
- package/src/cli/generate.ts +36 -27
- package/src/cli/init.ts +0 -1
- package/src/context/generator.ts +66 -36
- package/src/execution/crash-recovery.ts +2 -0
- package/src/execution/crash-signals.ts +15 -0
- package/src/execution/lifecycle/run-setup.ts +5 -0
- package/src/pipeline/stages/routing.ts +7 -1
- package/src/precheck/checks-git.ts +1 -0
package/dist/nax.js
CHANGED
|
@@ -19241,7 +19241,7 @@ class SpawnAcpSession {
|
|
|
19241
19241
|
} catch {}
|
|
19242
19242
|
this.activeProc = null;
|
|
19243
19243
|
}
|
|
19244
|
-
const cmd = ["acpx", this.agentName, "sessions", "close", this.sessionName];
|
|
19244
|
+
const cmd = ["acpx", "--cwd", this.cwd, this.agentName, "sessions", "close", this.sessionName];
|
|
19245
19245
|
getSafeLogger()?.debug("acp-adapter", `Closing session: ${this.sessionName}`);
|
|
19246
19246
|
const proc = _spawnClientDeps.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
19247
19247
|
const exitCode = await proc.exited;
|
|
@@ -22210,7 +22210,7 @@ var package_default;
|
|
|
22210
22210
|
var init_package = __esm(() => {
|
|
22211
22211
|
package_default = {
|
|
22212
22212
|
name: "@nathapp/nax",
|
|
22213
|
-
version: "0.48.
|
|
22213
|
+
version: "0.48.3",
|
|
22214
22214
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22215
22215
|
type: "module",
|
|
22216
22216
|
bin: {
|
|
@@ -22283,8 +22283,8 @@ var init_version = __esm(() => {
|
|
|
22283
22283
|
NAX_VERSION = package_default.version;
|
|
22284
22284
|
NAX_COMMIT = (() => {
|
|
22285
22285
|
try {
|
|
22286
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22287
|
-
return "
|
|
22286
|
+
if (/^[0-9a-f]{6,10}$/.test("fa27043"))
|
|
22287
|
+
return "fa27043";
|
|
22288
22288
|
} catch {}
|
|
22289
22289
|
try {
|
|
22290
22290
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -29366,6 +29366,7 @@ var init_regression2 = __esm(() => {
|
|
|
29366
29366
|
});
|
|
29367
29367
|
|
|
29368
29368
|
// src/pipeline/stages/routing.ts
|
|
29369
|
+
import { join as join25 } from "path";
|
|
29369
29370
|
async function runDecompose(story, prd, config2, _workdir, agentGetFn) {
|
|
29370
29371
|
const naxDecompose = config2.decompose;
|
|
29371
29372
|
const builderConfig = {
|
|
@@ -29439,11 +29440,13 @@ var init_routing2 = __esm(() => {
|
|
|
29439
29440
|
}
|
|
29440
29441
|
const greenfieldDetectionEnabled = ctx.config.tdd.greenfieldDetection ?? true;
|
|
29441
29442
|
if (greenfieldDetectionEnabled && routing.testStrategy.startsWith("three-session-tdd")) {
|
|
29442
|
-
const
|
|
29443
|
+
const greenfieldScanDir = ctx.story.workdir ? join25(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
29444
|
+
const isGreenfield = await _routingDeps.isGreenfieldStory(ctx.story, greenfieldScanDir);
|
|
29443
29445
|
if (isGreenfield) {
|
|
29444
29446
|
logger.info("routing", "Greenfield detected \u2014 forcing test-after strategy", {
|
|
29445
29447
|
storyId: ctx.story.id,
|
|
29446
|
-
originalStrategy: routing.testStrategy
|
|
29448
|
+
originalStrategy: routing.testStrategy,
|
|
29449
|
+
scanDir: greenfieldScanDir
|
|
29447
29450
|
});
|
|
29448
29451
|
routing.testStrategy = "test-after";
|
|
29449
29452
|
routing.reasoning = `${routing.reasoning} [GREENFIELD OVERRIDE: No test files exist, using test-after instead of TDD]`;
|
|
@@ -29534,7 +29537,7 @@ var init_crash_detector = __esm(() => {
|
|
|
29534
29537
|
});
|
|
29535
29538
|
|
|
29536
29539
|
// src/pipeline/stages/verify.ts
|
|
29537
|
-
import { join as
|
|
29540
|
+
import { join as join26 } from "path";
|
|
29538
29541
|
function coerceSmartTestRunner(val) {
|
|
29539
29542
|
if (val === undefined || val === true)
|
|
29540
29543
|
return DEFAULT_SMART_RUNNER_CONFIG2;
|
|
@@ -29566,7 +29569,7 @@ var init_verify = __esm(() => {
|
|
|
29566
29569
|
skipReason: () => "not needed (full-suite gate already passed)",
|
|
29567
29570
|
async execute(ctx) {
|
|
29568
29571
|
const logger = getLogger();
|
|
29569
|
-
const effectiveConfig = ctx.story.workdir ? await _verifyDeps.loadConfigForWorkdir(
|
|
29572
|
+
const effectiveConfig = ctx.story.workdir ? await _verifyDeps.loadConfigForWorkdir(join26(ctx.workdir, "nax", "config.json"), ctx.story.workdir) : ctx.config;
|
|
29570
29573
|
if (!effectiveConfig.quality.requireTests) {
|
|
29571
29574
|
logger.debug("verify", "Skipping verification (quality.requireTests = false)", { storyId: ctx.story.id });
|
|
29572
29575
|
return { action: "continue" };
|
|
@@ -29578,7 +29581,7 @@ var init_verify = __esm(() => {
|
|
|
29578
29581
|
return { action: "continue" };
|
|
29579
29582
|
}
|
|
29580
29583
|
logger.info("verify", "Running verification", { storyId: ctx.story.id });
|
|
29581
|
-
const effectiveWorkdir = ctx.story.workdir ?
|
|
29584
|
+
const effectiveWorkdir = ctx.story.workdir ? join26(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
29582
29585
|
let effectiveCommand = testCommand;
|
|
29583
29586
|
let isFullSuite = true;
|
|
29584
29587
|
const smartRunnerConfig = coerceSmartTestRunner(ctx.config.execution.smartTestRunner);
|
|
@@ -29752,7 +29755,7 @@ __export(exports_init_context, {
|
|
|
29752
29755
|
});
|
|
29753
29756
|
import { existsSync as existsSync20 } from "fs";
|
|
29754
29757
|
import { mkdir } from "fs/promises";
|
|
29755
|
-
import { basename, join as
|
|
29758
|
+
import { basename, join as join30 } from "path";
|
|
29756
29759
|
async function findFiles(dir, maxFiles = 200) {
|
|
29757
29760
|
try {
|
|
29758
29761
|
const proc = Bun.spawnSync([
|
|
@@ -29780,7 +29783,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
29780
29783
|
return [];
|
|
29781
29784
|
}
|
|
29782
29785
|
async function readPackageManifest(projectRoot) {
|
|
29783
|
-
const packageJsonPath =
|
|
29786
|
+
const packageJsonPath = join30(projectRoot, "package.json");
|
|
29784
29787
|
if (!existsSync20(packageJsonPath)) {
|
|
29785
29788
|
return null;
|
|
29786
29789
|
}
|
|
@@ -29798,7 +29801,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
29798
29801
|
}
|
|
29799
29802
|
}
|
|
29800
29803
|
async function readReadmeSnippet(projectRoot) {
|
|
29801
|
-
const readmePath =
|
|
29804
|
+
const readmePath = join30(projectRoot, "README.md");
|
|
29802
29805
|
if (!existsSync20(readmePath)) {
|
|
29803
29806
|
return null;
|
|
29804
29807
|
}
|
|
@@ -29816,7 +29819,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
29816
29819
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
29817
29820
|
const found = [];
|
|
29818
29821
|
for (const candidate of candidates) {
|
|
29819
|
-
const path12 =
|
|
29822
|
+
const path12 = join30(projectRoot, candidate);
|
|
29820
29823
|
if (existsSync20(path12)) {
|
|
29821
29824
|
found.push(candidate);
|
|
29822
29825
|
}
|
|
@@ -29827,7 +29830,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
29827
29830
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
29828
29831
|
const found = [];
|
|
29829
29832
|
for (const candidate of candidates) {
|
|
29830
|
-
const path12 =
|
|
29833
|
+
const path12 = join30(projectRoot, candidate);
|
|
29831
29834
|
if (existsSync20(path12)) {
|
|
29832
29835
|
found.push(candidate);
|
|
29833
29836
|
}
|
|
@@ -29988,9 +29991,9 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
29988
29991
|
}
|
|
29989
29992
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
29990
29993
|
const logger = getLogger();
|
|
29991
|
-
const packageDir =
|
|
29992
|
-
const naxDir =
|
|
29993
|
-
const contextPath =
|
|
29994
|
+
const packageDir = join30(repoRoot, packagePath);
|
|
29995
|
+
const naxDir = join30(packageDir, "nax");
|
|
29996
|
+
const contextPath = join30(naxDir, "context.md");
|
|
29994
29997
|
if (existsSync20(contextPath) && !force) {
|
|
29995
29998
|
logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
|
|
29996
29999
|
return;
|
|
@@ -30004,8 +30007,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
30004
30007
|
}
|
|
30005
30008
|
async function initContext(projectRoot, options = {}) {
|
|
30006
30009
|
const logger = getLogger();
|
|
30007
|
-
const naxDir =
|
|
30008
|
-
const contextPath =
|
|
30010
|
+
const naxDir = join30(projectRoot, "nax");
|
|
30011
|
+
const contextPath = join30(naxDir, "context.md");
|
|
30009
30012
|
if (existsSync20(contextPath) && !options.force) {
|
|
30010
30013
|
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
|
|
30011
30014
|
return;
|
|
@@ -30594,6 +30597,7 @@ var init_checks_git = __esm(() => {
|
|
|
30594
30597
|
/^.{2} nax\.lock$/,
|
|
30595
30598
|
/^.{2} nax\/metrics\.json$/,
|
|
30596
30599
|
/^.{2} nax\/features\/[^/]+\/status\.json$/,
|
|
30600
|
+
/^.{2} nax\/features\/[^/]+\/prd\.json$/,
|
|
30597
30601
|
/^.{2} nax\/features\/[^/]+\/runs\//,
|
|
30598
30602
|
/^.{2} nax\/features\/[^/]+\/plan\//,
|
|
30599
30603
|
/^.{2} nax\/features\/[^/]+\/acp-sessions\.json$/,
|
|
@@ -31312,19 +31316,19 @@ var init_precheck = __esm(() => {
|
|
|
31312
31316
|
});
|
|
31313
31317
|
|
|
31314
31318
|
// src/hooks/runner.ts
|
|
31315
|
-
import { join as
|
|
31319
|
+
import { join as join43 } from "path";
|
|
31316
31320
|
async function loadHooksConfig(projectDir, globalDir) {
|
|
31317
31321
|
let globalHooks = { hooks: {} };
|
|
31318
31322
|
let projectHooks = { hooks: {} };
|
|
31319
31323
|
let skipGlobal = false;
|
|
31320
|
-
const projectPath =
|
|
31324
|
+
const projectPath = join43(projectDir, "hooks.json");
|
|
31321
31325
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
31322
31326
|
if (projectData) {
|
|
31323
31327
|
projectHooks = projectData;
|
|
31324
31328
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
31325
31329
|
}
|
|
31326
31330
|
if (!skipGlobal && globalDir) {
|
|
31327
|
-
const globalPath =
|
|
31331
|
+
const globalPath = join43(globalDir, "hooks.json");
|
|
31328
31332
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
31329
31333
|
if (globalData) {
|
|
31330
31334
|
globalHooks = globalData;
|
|
@@ -31656,6 +31660,9 @@ function createSignalHandler(ctx) {
|
|
|
31656
31660
|
if (ctx.pidRegistry) {
|
|
31657
31661
|
await ctx.pidRegistry.killAll();
|
|
31658
31662
|
}
|
|
31663
|
+
if (ctx.onShutdown) {
|
|
31664
|
+
await ctx.onShutdown().catch(() => {});
|
|
31665
|
+
}
|
|
31659
31666
|
ctx.emitError?.(signal.toLowerCase());
|
|
31660
31667
|
await writeFatalLog(ctx.jsonlFilePath, signal);
|
|
31661
31668
|
await writeRunComplete(ctx, signal.toLowerCase());
|
|
@@ -31674,6 +31681,9 @@ function createUncaughtExceptionHandler(ctx) {
|
|
|
31674
31681
|
if (ctx.pidRegistry) {
|
|
31675
31682
|
await ctx.pidRegistry.killAll();
|
|
31676
31683
|
}
|
|
31684
|
+
if (ctx.onShutdown) {
|
|
31685
|
+
await ctx.onShutdown().catch(() => {});
|
|
31686
|
+
}
|
|
31677
31687
|
ctx.emitError?.("uncaughtException");
|
|
31678
31688
|
await writeFatalLog(ctx.jsonlFilePath, "uncaughtException", error48);
|
|
31679
31689
|
await updateStatusToCrashed(ctx.statusWriter, ctx.getTotalCost(), ctx.getIterations(), "uncaughtException", ctx.featureDir);
|
|
@@ -31691,6 +31701,9 @@ function createUnhandledRejectionHandler(ctx) {
|
|
|
31691
31701
|
if (ctx.pidRegistry) {
|
|
31692
31702
|
await ctx.pidRegistry.killAll();
|
|
31693
31703
|
}
|
|
31704
|
+
if (ctx.onShutdown) {
|
|
31705
|
+
await ctx.onShutdown().catch(() => {});
|
|
31706
|
+
}
|
|
31694
31707
|
ctx.emitError?.("unhandledRejection");
|
|
31695
31708
|
await writeFatalLog(ctx.jsonlFilePath, "unhandledRejection", error48);
|
|
31696
31709
|
await updateStatusToCrashed(ctx.statusWriter, ctx.getTotalCost(), ctx.getIterations(), "unhandledRejection", ctx.featureDir);
|
|
@@ -32347,12 +32360,12 @@ __export(exports_manager, {
|
|
|
32347
32360
|
WorktreeManager: () => WorktreeManager
|
|
32348
32361
|
});
|
|
32349
32362
|
import { existsSync as existsSync32, symlinkSync } from "fs";
|
|
32350
|
-
import { join as
|
|
32363
|
+
import { join as join44 } from "path";
|
|
32351
32364
|
|
|
32352
32365
|
class WorktreeManager {
|
|
32353
32366
|
async create(projectRoot, storyId) {
|
|
32354
32367
|
validateStoryId(storyId);
|
|
32355
|
-
const worktreePath =
|
|
32368
|
+
const worktreePath = join44(projectRoot, ".nax-wt", storyId);
|
|
32356
32369
|
const branchName = `nax/${storyId}`;
|
|
32357
32370
|
try {
|
|
32358
32371
|
const proc = Bun.spawn(["git", "worktree", "add", worktreePath, "-b", branchName], {
|
|
@@ -32377,9 +32390,9 @@ class WorktreeManager {
|
|
|
32377
32390
|
}
|
|
32378
32391
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
32379
32392
|
}
|
|
32380
|
-
const nodeModulesSource =
|
|
32393
|
+
const nodeModulesSource = join44(projectRoot, "node_modules");
|
|
32381
32394
|
if (existsSync32(nodeModulesSource)) {
|
|
32382
|
-
const nodeModulesTarget =
|
|
32395
|
+
const nodeModulesTarget = join44(worktreePath, "node_modules");
|
|
32383
32396
|
try {
|
|
32384
32397
|
symlinkSync(nodeModulesSource, nodeModulesTarget, "dir");
|
|
32385
32398
|
} catch (error48) {
|
|
@@ -32387,9 +32400,9 @@ class WorktreeManager {
|
|
|
32387
32400
|
throw new Error(`Failed to symlink node_modules: ${errorMessage(error48)}`);
|
|
32388
32401
|
}
|
|
32389
32402
|
}
|
|
32390
|
-
const envSource =
|
|
32403
|
+
const envSource = join44(projectRoot, ".env");
|
|
32391
32404
|
if (existsSync32(envSource)) {
|
|
32392
|
-
const envTarget =
|
|
32405
|
+
const envTarget = join44(worktreePath, ".env");
|
|
32393
32406
|
try {
|
|
32394
32407
|
symlinkSync(envSource, envTarget, "file");
|
|
32395
32408
|
} catch (error48) {
|
|
@@ -32400,7 +32413,7 @@ class WorktreeManager {
|
|
|
32400
32413
|
}
|
|
32401
32414
|
async remove(projectRoot, storyId) {
|
|
32402
32415
|
validateStoryId(storyId);
|
|
32403
|
-
const worktreePath =
|
|
32416
|
+
const worktreePath = join44(projectRoot, ".nax-wt", storyId);
|
|
32404
32417
|
const branchName = `nax/${storyId}`;
|
|
32405
32418
|
try {
|
|
32406
32419
|
const proc = Bun.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -32790,7 +32803,7 @@ var init_parallel_worker = __esm(() => {
|
|
|
32790
32803
|
|
|
32791
32804
|
// src/execution/parallel-coordinator.ts
|
|
32792
32805
|
import os3 from "os";
|
|
32793
|
-
import { join as
|
|
32806
|
+
import { join as join45 } from "path";
|
|
32794
32807
|
function groupStoriesByDependencies(stories) {
|
|
32795
32808
|
const batches = [];
|
|
32796
32809
|
const processed = new Set;
|
|
@@ -32868,7 +32881,7 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
32868
32881
|
};
|
|
32869
32882
|
const worktreePaths = new Map;
|
|
32870
32883
|
for (const story of batch) {
|
|
32871
|
-
const worktreePath =
|
|
32884
|
+
const worktreePath = join45(projectRoot, ".nax-wt", story.id);
|
|
32872
32885
|
try {
|
|
32873
32886
|
await worktreeManager.create(projectRoot, story.id);
|
|
32874
32887
|
worktreePaths.set(story.id, worktreePath);
|
|
@@ -32917,7 +32930,7 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
32917
32930
|
});
|
|
32918
32931
|
logger?.warn("parallel", "Worktree preserved for manual conflict resolution", {
|
|
32919
32932
|
storyId: mergeResult.storyId,
|
|
32920
|
-
worktreePath:
|
|
32933
|
+
worktreePath: join45(projectRoot, ".nax-wt", mergeResult.storyId)
|
|
32921
32934
|
});
|
|
32922
32935
|
}
|
|
32923
32936
|
}
|
|
@@ -33376,12 +33389,12 @@ var init_parallel_executor = __esm(() => {
|
|
|
33376
33389
|
// src/pipeline/subscribers/events-writer.ts
|
|
33377
33390
|
import { appendFile as appendFile2, mkdir as mkdir2 } from "fs/promises";
|
|
33378
33391
|
import { homedir as homedir7 } from "os";
|
|
33379
|
-
import { basename as basename4, join as
|
|
33392
|
+
import { basename as basename4, join as join46 } from "path";
|
|
33380
33393
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
33381
33394
|
const logger = getSafeLogger();
|
|
33382
33395
|
const project = basename4(workdir);
|
|
33383
|
-
const eventsDir =
|
|
33384
|
-
const eventsFile =
|
|
33396
|
+
const eventsDir = join46(homedir7(), ".nax", "events", project);
|
|
33397
|
+
const eventsFile = join46(eventsDir, "events.jsonl");
|
|
33385
33398
|
let dirReady = false;
|
|
33386
33399
|
const write = (line) => {
|
|
33387
33400
|
(async () => {
|
|
@@ -33541,12 +33554,12 @@ var init_interaction2 = __esm(() => {
|
|
|
33541
33554
|
// src/pipeline/subscribers/registry.ts
|
|
33542
33555
|
import { mkdir as mkdir3, writeFile } from "fs/promises";
|
|
33543
33556
|
import { homedir as homedir8 } from "os";
|
|
33544
|
-
import { basename as basename5, join as
|
|
33557
|
+
import { basename as basename5, join as join47 } from "path";
|
|
33545
33558
|
function wireRegistry(bus, feature, runId, workdir) {
|
|
33546
33559
|
const logger = getSafeLogger();
|
|
33547
33560
|
const project = basename5(workdir);
|
|
33548
|
-
const runDir =
|
|
33549
|
-
const metaFile =
|
|
33561
|
+
const runDir = join47(homedir8(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
33562
|
+
const metaFile = join47(runDir, "meta.json");
|
|
33550
33563
|
const unsub = bus.on("run:started", (_ev) => {
|
|
33551
33564
|
(async () => {
|
|
33552
33565
|
try {
|
|
@@ -33556,8 +33569,8 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
33556
33569
|
project,
|
|
33557
33570
|
feature,
|
|
33558
33571
|
workdir,
|
|
33559
|
-
statusPath:
|
|
33560
|
-
eventsDir:
|
|
33572
|
+
statusPath: join47(workdir, "nax", "features", feature, "status.json"),
|
|
33573
|
+
eventsDir: join47(workdir, "nax", "features", feature, "runs"),
|
|
33561
33574
|
registeredAt: new Date().toISOString()
|
|
33562
33575
|
};
|
|
33563
33576
|
await writeFile(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -34553,7 +34566,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
34553
34566
|
var init_status_file = () => {};
|
|
34554
34567
|
|
|
34555
34568
|
// src/execution/status-writer.ts
|
|
34556
|
-
import { join as
|
|
34569
|
+
import { join as join48 } from "path";
|
|
34557
34570
|
|
|
34558
34571
|
class StatusWriter {
|
|
34559
34572
|
statusFile;
|
|
@@ -34621,7 +34634,7 @@ class StatusWriter {
|
|
|
34621
34634
|
if (!this._prd)
|
|
34622
34635
|
return;
|
|
34623
34636
|
const safeLogger = getSafeLogger();
|
|
34624
|
-
const featureStatusPath =
|
|
34637
|
+
const featureStatusPath = join48(featureDir, "status.json");
|
|
34625
34638
|
try {
|
|
34626
34639
|
const base = this.getSnapshot(totalCost, iterations);
|
|
34627
34640
|
if (!base) {
|
|
@@ -34962,6 +34975,10 @@ async function setupRun(options) {
|
|
|
34962
34975
|
getStoriesCompleted: options.getStoriesCompleted,
|
|
34963
34976
|
emitError: (reason) => {
|
|
34964
34977
|
pipelineEventBus.emit({ type: "run:errored", reason, feature: options.feature });
|
|
34978
|
+
},
|
|
34979
|
+
onShutdown: async () => {
|
|
34980
|
+
const { sweepFeatureSessions: sweepFeatureSessions2 } = await Promise.resolve().then(() => (init_adapter2(), exports_adapter));
|
|
34981
|
+
await sweepFeatureSessions2(workdir, feature).catch(() => {});
|
|
34965
34982
|
}
|
|
34966
34983
|
});
|
|
34967
34984
|
let prd = await loadPRD(prdPath);
|
|
@@ -65946,7 +65963,7 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
65946
65963
|
init_source();
|
|
65947
65964
|
import { existsSync as existsSync34, mkdirSync as mkdirSync6 } from "fs";
|
|
65948
65965
|
import { homedir as homedir10 } from "os";
|
|
65949
|
-
import { join as
|
|
65966
|
+
import { join as join49 } from "path";
|
|
65950
65967
|
|
|
65951
65968
|
// node_modules/commander/esm.mjs
|
|
65952
65969
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -66817,10 +66834,11 @@ async function generateFor(agent, options, config2) {
|
|
|
66817
66834
|
return { agent, outputFile: generator.outputFile, content: "", written: false, error: error48 };
|
|
66818
66835
|
}
|
|
66819
66836
|
}
|
|
66820
|
-
async function generateAll(options, config2) {
|
|
66837
|
+
async function generateAll(options, config2, agentFilter) {
|
|
66821
66838
|
const context = await loadContextContent(options, config2);
|
|
66822
66839
|
const results = [];
|
|
66823
|
-
|
|
66840
|
+
const entries = Object.entries(GENERATORS).filter(([agentKey]) => !agentFilter || agentFilter.length === 0 || agentFilter.includes(agentKey));
|
|
66841
|
+
for (const [agentKey, generator] of entries) {
|
|
66824
66842
|
try {
|
|
66825
66843
|
const content = generator.generate(context);
|
|
66826
66844
|
const outputPath = join11(options.outputDir, generator.outputFile);
|
|
@@ -66922,34 +66940,42 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
66922
66940
|
async function generateForPackage(packageDir, config2, dryRun = false) {
|
|
66923
66941
|
const contextPath = join11(packageDir, "nax", "context.md");
|
|
66924
66942
|
if (!existsSync10(contextPath)) {
|
|
66925
|
-
return
|
|
66926
|
-
|
|
66927
|
-
|
|
66928
|
-
|
|
66929
|
-
|
|
66930
|
-
|
|
66931
|
-
|
|
66943
|
+
return [
|
|
66944
|
+
{
|
|
66945
|
+
packageDir,
|
|
66946
|
+
outputFile: "CLAUDE.md",
|
|
66947
|
+
content: "",
|
|
66948
|
+
written: false,
|
|
66949
|
+
error: `context.md not found: ${contextPath}`
|
|
66950
|
+
}
|
|
66951
|
+
];
|
|
66932
66952
|
}
|
|
66933
|
-
|
|
66934
|
-
|
|
66935
|
-
|
|
66936
|
-
|
|
66937
|
-
|
|
66938
|
-
|
|
66939
|
-
|
|
66940
|
-
|
|
66941
|
-
|
|
66942
|
-
|
|
66943
|
-
|
|
66944
|
-
|
|
66945
|
-
|
|
66946
|
-
|
|
66947
|
-
|
|
66948
|
-
|
|
66949
|
-
|
|
66950
|
-
|
|
66951
|
-
|
|
66953
|
+
const agentsToGenerate = config2?.generate?.agents && config2.generate.agents.length > 0 ? config2.generate.agents : ["claude"];
|
|
66954
|
+
const options = {
|
|
66955
|
+
contextPath,
|
|
66956
|
+
outputDir: packageDir,
|
|
66957
|
+
workdir: packageDir,
|
|
66958
|
+
dryRun,
|
|
66959
|
+
autoInject: true
|
|
66960
|
+
};
|
|
66961
|
+
const results = [];
|
|
66962
|
+
for (const agent of agentsToGenerate) {
|
|
66963
|
+
try {
|
|
66964
|
+
const result = await generateFor(agent, options, config2);
|
|
66965
|
+
results.push({
|
|
66966
|
+
packageDir,
|
|
66967
|
+
outputFile: result.outputFile,
|
|
66968
|
+
content: result.content,
|
|
66969
|
+
written: result.written,
|
|
66970
|
+
error: result.error
|
|
66971
|
+
});
|
|
66972
|
+
} catch (err) {
|
|
66973
|
+
const error48 = err instanceof Error ? err.message : String(err);
|
|
66974
|
+
const fallbackFile = GENERATORS[agent]?.outputFile ?? `${agent}.md`;
|
|
66975
|
+
results.push({ packageDir, outputFile: fallbackFile, content: "", written: false, error: error48 });
|
|
66976
|
+
}
|
|
66952
66977
|
}
|
|
66978
|
+
return results;
|
|
66953
66979
|
}
|
|
66954
66980
|
|
|
66955
66981
|
// src/cli/plan.ts
|
|
@@ -68006,7 +68032,7 @@ async function runsShowCommand(options) {
|
|
|
68006
68032
|
// src/cli/prompts-main.ts
|
|
68007
68033
|
init_logger2();
|
|
68008
68034
|
import { existsSync as existsSync18, mkdirSync as mkdirSync3 } from "fs";
|
|
68009
|
-
import { join as
|
|
68035
|
+
import { join as join28 } from "path";
|
|
68010
68036
|
|
|
68011
68037
|
// src/pipeline/index.ts
|
|
68012
68038
|
init_runner();
|
|
@@ -68042,7 +68068,7 @@ init_prd();
|
|
|
68042
68068
|
|
|
68043
68069
|
// src/cli/prompts-tdd.ts
|
|
68044
68070
|
init_prompts2();
|
|
68045
|
-
import { join as
|
|
68071
|
+
import { join as join27 } from "path";
|
|
68046
68072
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
68047
68073
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
68048
68074
|
PromptBuilder.for("test-writer", { isolation: "strict" }).withLoader(ctx.workdir, ctx.config).story(story).context(ctx.contextMarkdown).constitution(ctx.constitution?.content).testCommand(ctx.config.quality?.commands?.test).build(),
|
|
@@ -68061,7 +68087,7 @@ ${frontmatter}---
|
|
|
68061
68087
|
|
|
68062
68088
|
${session.prompt}`;
|
|
68063
68089
|
if (outputDir) {
|
|
68064
|
-
const promptFile =
|
|
68090
|
+
const promptFile = join27(outputDir, `${story.id}.${session.role}.md`);
|
|
68065
68091
|
await Bun.write(promptFile, fullOutput);
|
|
68066
68092
|
logger.info("cli", "Written TDD prompt file", {
|
|
68067
68093
|
storyId: story.id,
|
|
@@ -68077,7 +68103,7 @@ ${"=".repeat(80)}`);
|
|
|
68077
68103
|
}
|
|
68078
68104
|
}
|
|
68079
68105
|
if (outputDir && ctx.contextMarkdown) {
|
|
68080
|
-
const contextFile =
|
|
68106
|
+
const contextFile = join27(outputDir, `${story.id}.context.md`);
|
|
68081
68107
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
68082
68108
|
const contextOutput = `---
|
|
68083
68109
|
${frontmatter}---
|
|
@@ -68091,12 +68117,12 @@ ${ctx.contextMarkdown}`;
|
|
|
68091
68117
|
async function promptsCommand(options) {
|
|
68092
68118
|
const logger = getLogger();
|
|
68093
68119
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
68094
|
-
const naxDir =
|
|
68120
|
+
const naxDir = join28(workdir, "nax");
|
|
68095
68121
|
if (!existsSync18(naxDir)) {
|
|
68096
68122
|
throw new Error(`nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
68097
68123
|
}
|
|
68098
|
-
const featureDir =
|
|
68099
|
-
const prdPath =
|
|
68124
|
+
const featureDir = join28(naxDir, "features", feature);
|
|
68125
|
+
const prdPath = join28(featureDir, "prd.json");
|
|
68100
68126
|
if (!existsSync18(prdPath)) {
|
|
68101
68127
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
68102
68128
|
}
|
|
@@ -68156,10 +68182,10 @@ ${frontmatter}---
|
|
|
68156
68182
|
|
|
68157
68183
|
${ctx.prompt}`;
|
|
68158
68184
|
if (outputDir) {
|
|
68159
|
-
const promptFile =
|
|
68185
|
+
const promptFile = join28(outputDir, `${story.id}.prompt.md`);
|
|
68160
68186
|
await Bun.write(promptFile, fullOutput);
|
|
68161
68187
|
if (ctx.contextMarkdown) {
|
|
68162
|
-
const contextFile =
|
|
68188
|
+
const contextFile = join28(outputDir, `${story.id}.context.md`);
|
|
68163
68189
|
const contextOutput = `---
|
|
68164
68190
|
${frontmatter}---
|
|
68165
68191
|
|
|
@@ -68223,7 +68249,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
68223
68249
|
}
|
|
68224
68250
|
// src/cli/prompts-init.ts
|
|
68225
68251
|
import { existsSync as existsSync19, mkdirSync as mkdirSync4 } from "fs";
|
|
68226
|
-
import { join as
|
|
68252
|
+
import { join as join29 } from "path";
|
|
68227
68253
|
var TEMPLATE_ROLES = [
|
|
68228
68254
|
{ file: "test-writer.md", role: "test-writer" },
|
|
68229
68255
|
{ file: "implementer.md", role: "implementer", variant: "standard" },
|
|
@@ -68247,9 +68273,9 @@ var TEMPLATE_HEADER = `<!--
|
|
|
68247
68273
|
`;
|
|
68248
68274
|
async function promptsInitCommand(options) {
|
|
68249
68275
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
68250
|
-
const templatesDir =
|
|
68276
|
+
const templatesDir = join29(workdir, "nax", "templates");
|
|
68251
68277
|
mkdirSync4(templatesDir, { recursive: true });
|
|
68252
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync19(
|
|
68278
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync19(join29(templatesDir, f)));
|
|
68253
68279
|
if (existingFiles.length > 0 && !force) {
|
|
68254
68280
|
console.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
68255
68281
|
Pass --force to overwrite existing templates.`);
|
|
@@ -68257,7 +68283,7 @@ async function promptsInitCommand(options) {
|
|
|
68257
68283
|
}
|
|
68258
68284
|
const written = [];
|
|
68259
68285
|
for (const template of TEMPLATE_ROLES) {
|
|
68260
|
-
const filePath =
|
|
68286
|
+
const filePath = join29(templatesDir, template.file);
|
|
68261
68287
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
68262
68288
|
const content = TEMPLATE_HEADER + roleBody;
|
|
68263
68289
|
await Bun.write(filePath, content);
|
|
@@ -68273,7 +68299,7 @@ async function promptsInitCommand(options) {
|
|
|
68273
68299
|
return written;
|
|
68274
68300
|
}
|
|
68275
68301
|
async function autoWirePromptsConfig(workdir) {
|
|
68276
|
-
const configPath =
|
|
68302
|
+
const configPath = join29(workdir, "nax.config.json");
|
|
68277
68303
|
if (!existsSync19(configPath)) {
|
|
68278
68304
|
const exampleConfig = JSON.stringify({
|
|
68279
68305
|
prompts: {
|
|
@@ -68438,7 +68464,7 @@ init_config();
|
|
|
68438
68464
|
init_logger2();
|
|
68439
68465
|
init_prd();
|
|
68440
68466
|
import { existsSync as existsSync21, readdirSync as readdirSync5 } from "fs";
|
|
68441
|
-
import { join as
|
|
68467
|
+
import { join as join33 } from "path";
|
|
68442
68468
|
|
|
68443
68469
|
// src/cli/diagnose-analysis.ts
|
|
68444
68470
|
function detectFailurePattern(story, prd, status) {
|
|
@@ -68637,7 +68663,7 @@ function isProcessAlive2(pid) {
|
|
|
68637
68663
|
}
|
|
68638
68664
|
}
|
|
68639
68665
|
async function loadStatusFile2(workdir) {
|
|
68640
|
-
const statusPath =
|
|
68666
|
+
const statusPath = join33(workdir, "nax", "status.json");
|
|
68641
68667
|
if (!existsSync21(statusPath))
|
|
68642
68668
|
return null;
|
|
68643
68669
|
try {
|
|
@@ -68665,7 +68691,7 @@ async function countCommitsSince(workdir, since) {
|
|
|
68665
68691
|
}
|
|
68666
68692
|
}
|
|
68667
68693
|
async function checkLock(workdir) {
|
|
68668
|
-
const lockFile = Bun.file(
|
|
68694
|
+
const lockFile = Bun.file(join33(workdir, "nax.lock"));
|
|
68669
68695
|
if (!await lockFile.exists())
|
|
68670
68696
|
return { lockPresent: false };
|
|
68671
68697
|
try {
|
|
@@ -68683,8 +68709,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
68683
68709
|
const logger = getLogger();
|
|
68684
68710
|
const workdir = options.workdir ?? process.cwd();
|
|
68685
68711
|
const naxSubdir = findProjectDir(workdir);
|
|
68686
|
-
let projectDir = naxSubdir ?
|
|
68687
|
-
if (!projectDir && existsSync21(
|
|
68712
|
+
let projectDir = naxSubdir ? join33(naxSubdir, "..") : null;
|
|
68713
|
+
if (!projectDir && existsSync21(join33(workdir, "nax"))) {
|
|
68688
68714
|
projectDir = workdir;
|
|
68689
68715
|
}
|
|
68690
68716
|
if (!projectDir)
|
|
@@ -68695,7 +68721,7 @@ async function diagnoseCommand(options = {}) {
|
|
|
68695
68721
|
if (status2) {
|
|
68696
68722
|
feature = status2.run.feature;
|
|
68697
68723
|
} else {
|
|
68698
|
-
const featuresDir =
|
|
68724
|
+
const featuresDir = join33(projectDir, "nax", "features");
|
|
68699
68725
|
if (!existsSync21(featuresDir))
|
|
68700
68726
|
throw new Error("No features found in project");
|
|
68701
68727
|
const features = readdirSync5(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -68705,8 +68731,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
68705
68731
|
logger.info("diagnose", "No feature specified, using first found", { feature });
|
|
68706
68732
|
}
|
|
68707
68733
|
}
|
|
68708
|
-
const featureDir =
|
|
68709
|
-
const prdPath =
|
|
68734
|
+
const featureDir = join33(projectDir, "nax", "features", feature);
|
|
68735
|
+
const prdPath = join33(featureDir, "prd.json");
|
|
68710
68736
|
if (!existsSync21(prdPath))
|
|
68711
68737
|
throw new Error(`Feature not found: ${feature}`);
|
|
68712
68738
|
const prd = await loadPRD(prdPath);
|
|
@@ -68749,7 +68775,7 @@ init_interaction();
|
|
|
68749
68775
|
init_source();
|
|
68750
68776
|
init_loader2();
|
|
68751
68777
|
import { existsSync as existsSync22 } from "fs";
|
|
68752
|
-
import { join as
|
|
68778
|
+
import { join as join34 } from "path";
|
|
68753
68779
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
68754
68780
|
async function generateCommand(options) {
|
|
68755
68781
|
const workdir = process.cwd();
|
|
@@ -68770,16 +68796,18 @@ async function generateCommand(options) {
|
|
|
68770
68796
|
console.log(source_default.yellow(" No packages found (no */nax/context.md or */*/nax/context.md)"));
|
|
68771
68797
|
return;
|
|
68772
68798
|
}
|
|
68773
|
-
console.log(source_default.blue(`\u2192 Generating
|
|
68799
|
+
console.log(source_default.blue(`\u2192 Generating agent files for ${packages.length} package(s)...`));
|
|
68774
68800
|
let errorCount = 0;
|
|
68775
68801
|
for (const pkgDir of packages) {
|
|
68776
|
-
const
|
|
68777
|
-
|
|
68778
|
-
|
|
68779
|
-
|
|
68780
|
-
|
|
68781
|
-
|
|
68782
|
-
|
|
68802
|
+
const results = await generateForPackage(pkgDir, config2, dryRun);
|
|
68803
|
+
for (const result of results) {
|
|
68804
|
+
if (result.error) {
|
|
68805
|
+
console.error(source_default.red(`\u2717 ${pkgDir}: ${result.error}`));
|
|
68806
|
+
errorCount++;
|
|
68807
|
+
} else {
|
|
68808
|
+
const suffix = dryRun ? " (dry run)" : "";
|
|
68809
|
+
console.log(source_default.green(`\u2713 ${pkgDir}/${result.outputFile} (${result.content.length} bytes${suffix})`));
|
|
68810
|
+
}
|
|
68783
68811
|
}
|
|
68784
68812
|
}
|
|
68785
68813
|
if (errorCount > 0) {
|
|
@@ -68790,22 +68818,28 @@ async function generateCommand(options) {
|
|
|
68790
68818
|
return;
|
|
68791
68819
|
}
|
|
68792
68820
|
if (options.package) {
|
|
68793
|
-
const packageDir =
|
|
68821
|
+
const packageDir = join34(workdir, options.package);
|
|
68794
68822
|
if (dryRun) {
|
|
68795
68823
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
68796
68824
|
}
|
|
68797
|
-
console.log(source_default.blue(`\u2192 Generating
|
|
68798
|
-
const
|
|
68799
|
-
|
|
68800
|
-
|
|
68801
|
-
|
|
68825
|
+
console.log(source_default.blue(`\u2192 Generating agent files for package: ${options.package}`));
|
|
68826
|
+
const pkgResults = await generateForPackage(packageDir, config2, dryRun);
|
|
68827
|
+
let pkgHasError = false;
|
|
68828
|
+
for (const result of pkgResults) {
|
|
68829
|
+
if (result.error) {
|
|
68830
|
+
console.error(source_default.red(`\u2717 ${result.error}`));
|
|
68831
|
+
pkgHasError = true;
|
|
68832
|
+
} else {
|
|
68833
|
+
const suffix = dryRun ? " (dry run)" : "";
|
|
68834
|
+
console.log(source_default.green(`\u2713 ${options.package}/${result.outputFile} (${result.content.length} bytes${suffix})`));
|
|
68835
|
+
}
|
|
68802
68836
|
}
|
|
68803
|
-
|
|
68804
|
-
|
|
68837
|
+
if (pkgHasError)
|
|
68838
|
+
process.exit(1);
|
|
68805
68839
|
return;
|
|
68806
68840
|
}
|
|
68807
|
-
const contextPath = options.context ?
|
|
68808
|
-
const outputDir = options.output ?
|
|
68841
|
+
const contextPath = options.context ? join34(workdir, options.context) : join34(workdir, "nax/context.md");
|
|
68842
|
+
const outputDir = options.output ? join34(workdir, options.output) : workdir;
|
|
68809
68843
|
const autoInject = !options.noAutoInject;
|
|
68810
68844
|
if (!existsSync22(contextPath)) {
|
|
68811
68845
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -68856,8 +68890,7 @@ async function generateCommand(options) {
|
|
|
68856
68890
|
} else {
|
|
68857
68891
|
console.log(source_default.blue("\u2192 Generating configs for all agents..."));
|
|
68858
68892
|
}
|
|
68859
|
-
const
|
|
68860
|
-
const results = agentFilter ? allResults.filter((r) => agentFilter.includes(r.agent)) : allResults;
|
|
68893
|
+
const results = await generateAll(genOptions, config2, agentFilter ?? undefined);
|
|
68861
68894
|
let errorCount = 0;
|
|
68862
68895
|
for (const result of results) {
|
|
68863
68896
|
if (result.error) {
|
|
@@ -68876,17 +68909,19 @@ async function generateCommand(options) {
|
|
|
68876
68909
|
const packages = await discoverPackages(workdir);
|
|
68877
68910
|
if (packages.length > 0) {
|
|
68878
68911
|
console.log(source_default.blue(`
|
|
68879
|
-
\u2192 Discovered ${packages.length} package(s) with nax/context.md \u2014 generating
|
|
68912
|
+
\u2192 Discovered ${packages.length} package(s) with nax/context.md \u2014 generating agent files...`));
|
|
68880
68913
|
let pkgErrorCount = 0;
|
|
68881
68914
|
for (const pkgDir of packages) {
|
|
68882
|
-
const
|
|
68883
|
-
|
|
68884
|
-
|
|
68885
|
-
|
|
68886
|
-
|
|
68887
|
-
|
|
68888
|
-
|
|
68889
|
-
|
|
68915
|
+
const pkgResults = await generateForPackage(pkgDir, config2, dryRun);
|
|
68916
|
+
for (const result of pkgResults) {
|
|
68917
|
+
if (result.error) {
|
|
68918
|
+
console.error(source_default.red(`\u2717 ${pkgDir}: ${result.error}`));
|
|
68919
|
+
pkgErrorCount++;
|
|
68920
|
+
} else {
|
|
68921
|
+
const suffix = dryRun ? " (dry run)" : "";
|
|
68922
|
+
const rel = pkgDir.startsWith(workdir) ? pkgDir.slice(workdir.length + 1) : pkgDir;
|
|
68923
|
+
console.log(source_default.green(`\u2713 ${rel}/${result.outputFile} (${result.content.length} bytes${suffix})`));
|
|
68924
|
+
}
|
|
68890
68925
|
}
|
|
68891
68926
|
}
|
|
68892
68927
|
if (pkgErrorCount > 0) {
|
|
@@ -68909,7 +68944,7 @@ async function generateCommand(options) {
|
|
|
68909
68944
|
// src/cli/config-display.ts
|
|
68910
68945
|
init_loader2();
|
|
68911
68946
|
import { existsSync as existsSync24 } from "fs";
|
|
68912
|
-
import { join as
|
|
68947
|
+
import { join as join36 } from "path";
|
|
68913
68948
|
|
|
68914
68949
|
// src/cli/config-descriptions.ts
|
|
68915
68950
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -69118,7 +69153,7 @@ function deepEqual(a, b) {
|
|
|
69118
69153
|
init_defaults();
|
|
69119
69154
|
init_loader2();
|
|
69120
69155
|
import { existsSync as existsSync23 } from "fs";
|
|
69121
|
-
import { join as
|
|
69156
|
+
import { join as join35 } from "path";
|
|
69122
69157
|
async function loadConfigFile(path14) {
|
|
69123
69158
|
if (!existsSync23(path14))
|
|
69124
69159
|
return null;
|
|
@@ -69140,7 +69175,7 @@ async function loadProjectConfig() {
|
|
|
69140
69175
|
const projectDir = findProjectDir();
|
|
69141
69176
|
if (!projectDir)
|
|
69142
69177
|
return null;
|
|
69143
|
-
const projectPath =
|
|
69178
|
+
const projectPath = join35(projectDir, "config.json");
|
|
69144
69179
|
return await loadConfigFile(projectPath);
|
|
69145
69180
|
}
|
|
69146
69181
|
|
|
@@ -69200,7 +69235,7 @@ async function configCommand(config2, options = {}) {
|
|
|
69200
69235
|
function determineConfigSources() {
|
|
69201
69236
|
const globalPath = globalConfigPath();
|
|
69202
69237
|
const projectDir = findProjectDir();
|
|
69203
|
-
const projectPath = projectDir ?
|
|
69238
|
+
const projectPath = projectDir ? join36(projectDir, "config.json") : null;
|
|
69204
69239
|
return {
|
|
69205
69240
|
global: fileExists(globalPath) ? globalPath : null,
|
|
69206
69241
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -69380,21 +69415,21 @@ async function diagnose(options) {
|
|
|
69380
69415
|
|
|
69381
69416
|
// src/commands/logs.ts
|
|
69382
69417
|
import { existsSync as existsSync26 } from "fs";
|
|
69383
|
-
import { join as
|
|
69418
|
+
import { join as join39 } from "path";
|
|
69384
69419
|
|
|
69385
69420
|
// src/commands/logs-formatter.ts
|
|
69386
69421
|
init_source();
|
|
69387
69422
|
init_formatter();
|
|
69388
69423
|
import { readdirSync as readdirSync7 } from "fs";
|
|
69389
|
-
import { join as
|
|
69424
|
+
import { join as join38 } from "path";
|
|
69390
69425
|
|
|
69391
69426
|
// src/commands/logs-reader.ts
|
|
69392
69427
|
import { existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
|
|
69393
69428
|
import { readdir as readdir3 } from "fs/promises";
|
|
69394
69429
|
import { homedir as homedir5 } from "os";
|
|
69395
|
-
import { join as
|
|
69430
|
+
import { join as join37 } from "path";
|
|
69396
69431
|
var _deps7 = {
|
|
69397
|
-
getRunsDir: () => process.env.NAX_RUNS_DIR ??
|
|
69432
|
+
getRunsDir: () => process.env.NAX_RUNS_DIR ?? join37(homedir5(), ".nax", "runs")
|
|
69398
69433
|
};
|
|
69399
69434
|
async function resolveRunFileFromRegistry(runId) {
|
|
69400
69435
|
const runsDir = _deps7.getRunsDir();
|
|
@@ -69406,7 +69441,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
69406
69441
|
}
|
|
69407
69442
|
let matched = null;
|
|
69408
69443
|
for (const entry of entries) {
|
|
69409
|
-
const metaPath =
|
|
69444
|
+
const metaPath = join37(runsDir, entry, "meta.json");
|
|
69410
69445
|
try {
|
|
69411
69446
|
const meta3 = await Bun.file(metaPath).json();
|
|
69412
69447
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -69428,14 +69463,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
69428
69463
|
return null;
|
|
69429
69464
|
}
|
|
69430
69465
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
69431
|
-
return
|
|
69466
|
+
return join37(matched.eventsDir, specificFile ?? files[0]);
|
|
69432
69467
|
}
|
|
69433
69468
|
async function selectRunFile(runsDir) {
|
|
69434
69469
|
const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
69435
69470
|
if (files.length === 0) {
|
|
69436
69471
|
return null;
|
|
69437
69472
|
}
|
|
69438
|
-
return
|
|
69473
|
+
return join37(runsDir, files[0]);
|
|
69439
69474
|
}
|
|
69440
69475
|
async function extractRunSummary(filePath) {
|
|
69441
69476
|
const file2 = Bun.file(filePath);
|
|
@@ -69520,7 +69555,7 @@ Runs:
|
|
|
69520
69555
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
69521
69556
|
console.log(source_default.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
69522
69557
|
for (const file2 of files) {
|
|
69523
|
-
const filePath =
|
|
69558
|
+
const filePath = join38(runsDir, file2);
|
|
69524
69559
|
const summary = await extractRunSummary(filePath);
|
|
69525
69560
|
const timestamp = file2.replace(".jsonl", "");
|
|
69526
69561
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -69645,7 +69680,7 @@ async function logsCommand(options) {
|
|
|
69645
69680
|
return;
|
|
69646
69681
|
}
|
|
69647
69682
|
const resolved = resolveProject({ dir: options.dir });
|
|
69648
|
-
const naxDir =
|
|
69683
|
+
const naxDir = join39(resolved.projectDir, "nax");
|
|
69649
69684
|
const configPath = resolved.configPath;
|
|
69650
69685
|
const configFile = Bun.file(configPath);
|
|
69651
69686
|
const config2 = await configFile.json();
|
|
@@ -69653,8 +69688,8 @@ async function logsCommand(options) {
|
|
|
69653
69688
|
if (!featureName) {
|
|
69654
69689
|
throw new Error("No feature specified in config.json");
|
|
69655
69690
|
}
|
|
69656
|
-
const featureDir =
|
|
69657
|
-
const runsDir =
|
|
69691
|
+
const featureDir = join39(naxDir, "features", featureName);
|
|
69692
|
+
const runsDir = join39(featureDir, "runs");
|
|
69658
69693
|
if (!existsSync26(runsDir)) {
|
|
69659
69694
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
69660
69695
|
}
|
|
@@ -69679,7 +69714,7 @@ init_config();
|
|
|
69679
69714
|
init_prd();
|
|
69680
69715
|
init_precheck();
|
|
69681
69716
|
import { existsSync as existsSync31 } from "fs";
|
|
69682
|
-
import { join as
|
|
69717
|
+
import { join as join40 } from "path";
|
|
69683
69718
|
async function precheckCommand(options) {
|
|
69684
69719
|
const resolved = resolveProject({
|
|
69685
69720
|
dir: options.dir,
|
|
@@ -69695,9 +69730,9 @@ async function precheckCommand(options) {
|
|
|
69695
69730
|
process.exit(1);
|
|
69696
69731
|
}
|
|
69697
69732
|
}
|
|
69698
|
-
const naxDir =
|
|
69699
|
-
const featureDir =
|
|
69700
|
-
const prdPath =
|
|
69733
|
+
const naxDir = join40(resolved.projectDir, "nax");
|
|
69734
|
+
const featureDir = join40(naxDir, "features", featureName);
|
|
69735
|
+
const prdPath = join40(featureDir, "prd.json");
|
|
69701
69736
|
if (!existsSync31(featureDir)) {
|
|
69702
69737
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
69703
69738
|
process.exit(1);
|
|
@@ -69721,10 +69756,10 @@ async function precheckCommand(options) {
|
|
|
69721
69756
|
init_source();
|
|
69722
69757
|
import { readdir as readdir4 } from "fs/promises";
|
|
69723
69758
|
import { homedir as homedir6 } from "os";
|
|
69724
|
-
import { join as
|
|
69759
|
+
import { join as join41 } from "path";
|
|
69725
69760
|
var DEFAULT_LIMIT = 20;
|
|
69726
69761
|
var _deps9 = {
|
|
69727
|
-
getRunsDir: () =>
|
|
69762
|
+
getRunsDir: () => join41(homedir6(), ".nax", "runs")
|
|
69728
69763
|
};
|
|
69729
69764
|
function formatDuration3(ms) {
|
|
69730
69765
|
if (ms <= 0)
|
|
@@ -69776,7 +69811,7 @@ async function runsCommand(options = {}) {
|
|
|
69776
69811
|
}
|
|
69777
69812
|
const rows = [];
|
|
69778
69813
|
for (const entry of entries) {
|
|
69779
|
-
const metaPath =
|
|
69814
|
+
const metaPath = join41(runsDir, entry, "meta.json");
|
|
69780
69815
|
let meta3;
|
|
69781
69816
|
try {
|
|
69782
69817
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -69853,7 +69888,7 @@ async function runsCommand(options = {}) {
|
|
|
69853
69888
|
|
|
69854
69889
|
// src/commands/unlock.ts
|
|
69855
69890
|
init_source();
|
|
69856
|
-
import { join as
|
|
69891
|
+
import { join as join42 } from "path";
|
|
69857
69892
|
function isProcessAlive3(pid) {
|
|
69858
69893
|
try {
|
|
69859
69894
|
process.kill(pid, 0);
|
|
@@ -69868,7 +69903,7 @@ function formatLockAge(ageMs) {
|
|
|
69868
69903
|
}
|
|
69869
69904
|
async function unlockCommand(options) {
|
|
69870
69905
|
const workdir = options.dir ?? process.cwd();
|
|
69871
|
-
const lockPath =
|
|
69906
|
+
const lockPath = join42(workdir, "nax.lock");
|
|
69872
69907
|
const lockFile = Bun.file(lockPath);
|
|
69873
69908
|
const exists = await lockFile.exists();
|
|
69874
69909
|
if (!exists) {
|
|
@@ -77703,15 +77738,15 @@ Next: nax generate --package ${options.package}`));
|
|
|
77703
77738
|
}
|
|
77704
77739
|
return;
|
|
77705
77740
|
}
|
|
77706
|
-
const naxDir =
|
|
77741
|
+
const naxDir = join49(workdir, "nax");
|
|
77707
77742
|
if (existsSync34(naxDir) && !options.force) {
|
|
77708
77743
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
77709
77744
|
return;
|
|
77710
77745
|
}
|
|
77711
|
-
mkdirSync6(
|
|
77712
|
-
mkdirSync6(
|
|
77713
|
-
await Bun.write(
|
|
77714
|
-
await Bun.write(
|
|
77746
|
+
mkdirSync6(join49(naxDir, "features"), { recursive: true });
|
|
77747
|
+
mkdirSync6(join49(naxDir, "hooks"), { recursive: true });
|
|
77748
|
+
await Bun.write(join49(naxDir, "config.json"), JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
77749
|
+
await Bun.write(join49(naxDir, "hooks.json"), JSON.stringify({
|
|
77715
77750
|
hooks: {
|
|
77716
77751
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
77717
77752
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -77719,12 +77754,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
77719
77754
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
77720
77755
|
}
|
|
77721
77756
|
}, null, 2));
|
|
77722
|
-
await Bun.write(
|
|
77757
|
+
await Bun.write(join49(naxDir, ".gitignore"), `# nax temp files
|
|
77723
77758
|
*.tmp
|
|
77724
77759
|
.paused.json
|
|
77725
77760
|
.nax-verifier-verdict.json
|
|
77726
77761
|
`);
|
|
77727
|
-
await Bun.write(
|
|
77762
|
+
await Bun.write(join49(naxDir, "context.md"), `# Project Context
|
|
77728
77763
|
|
|
77729
77764
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
77730
77765
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -77850,8 +77885,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77850
77885
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
77851
77886
|
process.exit(1);
|
|
77852
77887
|
}
|
|
77853
|
-
const featureDir =
|
|
77854
|
-
const prdPath =
|
|
77888
|
+
const featureDir = join49(naxDir, "features", options.feature);
|
|
77889
|
+
const prdPath = join49(featureDir, "prd.json");
|
|
77855
77890
|
if (options.plan && options.from) {
|
|
77856
77891
|
if (existsSync34(prdPath) && !options.force) {
|
|
77857
77892
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -77873,10 +77908,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77873
77908
|
}
|
|
77874
77909
|
}
|
|
77875
77910
|
try {
|
|
77876
|
-
const planLogDir =
|
|
77911
|
+
const planLogDir = join49(featureDir, "plan");
|
|
77877
77912
|
mkdirSync6(planLogDir, { recursive: true });
|
|
77878
77913
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
77879
|
-
const planLogPath =
|
|
77914
|
+
const planLogPath = join49(planLogDir, `${planLogId}.jsonl`);
|
|
77880
77915
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
77881
77916
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
77882
77917
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -77914,10 +77949,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77914
77949
|
process.exit(1);
|
|
77915
77950
|
}
|
|
77916
77951
|
resetLogger();
|
|
77917
|
-
const runsDir =
|
|
77952
|
+
const runsDir = join49(featureDir, "runs");
|
|
77918
77953
|
mkdirSync6(runsDir, { recursive: true });
|
|
77919
77954
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
77920
|
-
const logFilePath =
|
|
77955
|
+
const logFilePath = join49(runsDir, `${runId}.jsonl`);
|
|
77921
77956
|
const isTTY = process.stdout.isTTY ?? false;
|
|
77922
77957
|
const headlessFlag = options.headless ?? false;
|
|
77923
77958
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -77933,7 +77968,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77933
77968
|
config2.autoMode.defaultAgent = options.agent;
|
|
77934
77969
|
}
|
|
77935
77970
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
77936
|
-
const globalNaxDir =
|
|
77971
|
+
const globalNaxDir = join49(homedir10(), ".nax");
|
|
77937
77972
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
77938
77973
|
const eventEmitter = new PipelineEventEmitter;
|
|
77939
77974
|
let tuiInstance;
|
|
@@ -77956,7 +77991,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77956
77991
|
} else {
|
|
77957
77992
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
77958
77993
|
}
|
|
77959
|
-
const statusFilePath =
|
|
77994
|
+
const statusFilePath = join49(workdir, "nax", "status.json");
|
|
77960
77995
|
let parallel;
|
|
77961
77996
|
if (options.parallel !== undefined) {
|
|
77962
77997
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -77982,7 +78017,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77982
78017
|
headless: useHeadless,
|
|
77983
78018
|
skipPrecheck: options.skipPrecheck ?? false
|
|
77984
78019
|
});
|
|
77985
|
-
const latestSymlink =
|
|
78020
|
+
const latestSymlink = join49(runsDir, "latest.jsonl");
|
|
77986
78021
|
try {
|
|
77987
78022
|
if (existsSync34(latestSymlink)) {
|
|
77988
78023
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -78020,9 +78055,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
78020
78055
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
78021
78056
|
process.exit(1);
|
|
78022
78057
|
}
|
|
78023
|
-
const featureDir =
|
|
78058
|
+
const featureDir = join49(naxDir, "features", name);
|
|
78024
78059
|
mkdirSync6(featureDir, { recursive: true });
|
|
78025
|
-
await Bun.write(
|
|
78060
|
+
await Bun.write(join49(featureDir, "spec.md"), `# Feature: ${name}
|
|
78026
78061
|
|
|
78027
78062
|
## Overview
|
|
78028
78063
|
|
|
@@ -78030,7 +78065,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
78030
78065
|
|
|
78031
78066
|
## Acceptance Criteria
|
|
78032
78067
|
`);
|
|
78033
|
-
await Bun.write(
|
|
78068
|
+
await Bun.write(join49(featureDir, "plan.md"), `# Plan: ${name}
|
|
78034
78069
|
|
|
78035
78070
|
## Architecture
|
|
78036
78071
|
|
|
@@ -78038,7 +78073,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
78038
78073
|
|
|
78039
78074
|
## Dependencies
|
|
78040
78075
|
`);
|
|
78041
|
-
await Bun.write(
|
|
78076
|
+
await Bun.write(join49(featureDir, "tasks.md"), `# Tasks: ${name}
|
|
78042
78077
|
|
|
78043
78078
|
## US-001: [Title]
|
|
78044
78079
|
|
|
@@ -78047,7 +78082,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
78047
78082
|
### Acceptance Criteria
|
|
78048
78083
|
- [ ] Criterion 1
|
|
78049
78084
|
`);
|
|
78050
|
-
await Bun.write(
|
|
78085
|
+
await Bun.write(join49(featureDir, "progress.txt"), `# Progress: ${name}
|
|
78051
78086
|
|
|
78052
78087
|
Created: ${new Date().toISOString()}
|
|
78053
78088
|
|
|
@@ -78075,7 +78110,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
78075
78110
|
console.error(source_default.red("nax not initialized."));
|
|
78076
78111
|
process.exit(1);
|
|
78077
78112
|
}
|
|
78078
|
-
const featuresDir =
|
|
78113
|
+
const featuresDir = join49(naxDir, "features");
|
|
78079
78114
|
if (!existsSync34(featuresDir)) {
|
|
78080
78115
|
console.log(source_default.dim("No features yet."));
|
|
78081
78116
|
return;
|
|
@@ -78090,7 +78125,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
78090
78125
|
Features:
|
|
78091
78126
|
`));
|
|
78092
78127
|
for (const name of entries) {
|
|
78093
|
-
const prdPath =
|
|
78128
|
+
const prdPath = join49(featuresDir, name, "prd.json");
|
|
78094
78129
|
if (existsSync34(prdPath)) {
|
|
78095
78130
|
const prd = await loadPRD(prdPath);
|
|
78096
78131
|
const c = countStories(prd);
|
|
@@ -78121,10 +78156,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
78121
78156
|
process.exit(1);
|
|
78122
78157
|
}
|
|
78123
78158
|
const config2 = await loadConfig(workdir);
|
|
78124
|
-
const featureLogDir =
|
|
78159
|
+
const featureLogDir = join49(naxDir, "features", options.feature, "plan");
|
|
78125
78160
|
mkdirSync6(featureLogDir, { recursive: true });
|
|
78126
78161
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
78127
|
-
const planLogPath =
|
|
78162
|
+
const planLogPath = join49(featureLogDir, `${planLogId}.jsonl`);
|
|
78128
78163
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
78129
78164
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
78130
78165
|
try {
|
|
@@ -78161,7 +78196,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
78161
78196
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
78162
78197
|
process.exit(1);
|
|
78163
78198
|
}
|
|
78164
|
-
const featureDir =
|
|
78199
|
+
const featureDir = join49(naxDir, "features", options.feature);
|
|
78165
78200
|
if (!existsSync34(featureDir)) {
|
|
78166
78201
|
console.error(source_default.red(`Feature "${options.feature}" not found.`));
|
|
78167
78202
|
process.exit(1);
|
|
@@ -78177,7 +78212,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
78177
78212
|
specPath: options.from,
|
|
78178
78213
|
reclassify: options.reclassify
|
|
78179
78214
|
});
|
|
78180
|
-
const prdPath =
|
|
78215
|
+
const prdPath = join49(featureDir, "prd.json");
|
|
78181
78216
|
await Bun.write(prdPath, JSON.stringify(prd, null, 2));
|
|
78182
78217
|
const c = countStories(prd);
|
|
78183
78218
|
console.log(source_default.green(`
|