@nathapp/nax 0.69.7 → 0.69.9

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.
Files changed (3) hide show
  1. package/README.md +12 -6
  2. package/dist/nax.js +327 -286
  3. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -35598,7 +35598,7 @@ function resolveAcceptanceTestCandidates(options) {
35598
35598
  return [];
35599
35599
  return [resolveAcceptanceFeatureTestPath(options.featureDir, options.testPathConfig, options.language)];
35600
35600
  }
35601
- function groupStoriesByPackage(prd, workdir, featureName, testPathConfig, language) {
35601
+ async function groupStoriesByPackage(prd, workdir, featureName, testPathConfig, language) {
35602
35602
  const nonFixStories = prd.userStories.filter((s) => !s.id.startsWith("US-FIX-") && s.status !== "decomposed");
35603
35603
  const groupMap = new Map;
35604
35604
  for (const story of nonFixStories) {
@@ -35615,13 +35615,13 @@ function groupStoriesByPackage(prd, workdir, featureName, testPathConfig, langua
35615
35615
  if (groupMap.size === 0) {
35616
35616
  groupMap.set("", { stories: [], criteria: [] });
35617
35617
  }
35618
- const groups = [];
35619
- for (const [wd, { stories, criteria }] of groupMap) {
35618
+ return Promise.all(Array.from(groupMap.entries()).map(async ([wd, { stories, criteria }]) => {
35620
35619
  const packageDir = wd ? path3.join(workdir, wd) : workdir;
35621
- const testPath = resolveAcceptancePackageFeatureTestPath(packageDir, featureName, testPathConfig, language);
35622
- groups.push({ testPath, packageDir, stories, criteria });
35623
- }
35624
- return groups;
35620
+ const detectedLang = await _groupDeps.detectLanguage(packageDir);
35621
+ const resolvedLang = detectedLang ?? language;
35622
+ const testPath = resolveAcceptancePackageFeatureTestPath(packageDir, featureName, testPathConfig, resolvedLang);
35623
+ return { testPath, packageDir, stories, criteria };
35624
+ }));
35625
35625
  }
35626
35626
  function suggestedTestFilename(language) {
35627
35627
  switch (language?.toLowerCase()) {
@@ -35664,7 +35664,13 @@ async function findExistingAcceptanceTestPath(options) {
35664
35664
  }
35665
35665
  return;
35666
35666
  }
35667
- var init_test_path = () => {};
35667
+ var _groupDeps;
35668
+ var init_test_path = __esm(() => {
35669
+ init_detector();
35670
+ _groupDeps = {
35671
+ detectLanguage
35672
+ };
35673
+ });
35668
35674
 
35669
35675
  // src/acceptance/generator-helpers.ts
35670
35676
  function skeletonImportLine(testFramework) {
@@ -37430,7 +37436,7 @@ var init_write_test = __esm(() => {
37430
37436
  if (!input.beforeRef)
37431
37437
  return parsed;
37432
37438
  const allowedPaths = ctx.config.tdd?.testWriterAllowedPaths ?? ["src/index.ts", "src/**/index.ts"];
37433
- const testFilePatterns = typeof ctx.packageView.config.execution?.smartTestRunner === "object" && ctx.packageView.config.execution.smartTestRunner !== null ? ctx.packageView.config.execution.smartTestRunner.testFilePatterns : undefined;
37439
+ const testFilePatterns = input.resolvedTestPatterns?.globs;
37434
37440
  const isolation = await verifyTestWriterIsolation(ctx.packageView.packageDir, input.beforeRef, allowedPaths, testFilePatterns, input.lite ? "lite" : "strict");
37435
37441
  return { ...parsed, isolation };
37436
37442
  }
@@ -38414,57 +38420,69 @@ var init_plan_critic_llm = __esm(() => {
38414
38420
  });
38415
38421
 
38416
38422
  // src/context/greenfield.ts
38417
- import { readdir as readdir2 } from "fs/promises";
38418
- import { join as join23 } from "path";
38419
- async function scanForTestFiles(dir, testPatterns, isRootCall = true) {
38420
- const results = [];
38421
- const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
38423
+ async function gitLsFiles2(workdir) {
38422
38424
  try {
38423
- const entries = await readdir2(dir, { withFileTypes: true });
38424
- for (const entry of entries) {
38425
- const fullPath = join23(dir, entry.name);
38426
- if (entry.isDirectory()) {
38427
- if (ignoreDirs.has(entry.name))
38428
- continue;
38429
- const subResults = await scanForTestFiles(fullPath, testPatterns, false);
38430
- results.push(...subResults);
38431
- } else if (entry.isFile()) {
38432
- if (testPatterns.some((re) => re.test(entry.name))) {
38433
- results.push(fullPath);
38434
- }
38425
+ const proc = _greenfieldDeps.spawn(["git", "ls-files"], {
38426
+ cwd: workdir,
38427
+ stdout: "pipe",
38428
+ stderr: "pipe"
38429
+ });
38430
+ const exitCode = await proc.exited;
38431
+ if (exitCode !== 0)
38432
+ return null;
38433
+ const output = await new Response(proc.stdout).text();
38434
+ return output.split(`
38435
+ `).filter(Boolean);
38436
+ } catch {
38437
+ return null;
38438
+ }
38439
+ }
38440
+ async function hasTestFiles(workdir, patterns) {
38441
+ const files = await gitLsFiles2(workdir);
38442
+ if (files !== null) {
38443
+ return files.some((f) => isTestFileByPatterns(f, patterns));
38444
+ }
38445
+ for (const pattern of patterns) {
38446
+ const g = new Bun.Glob(pattern);
38447
+ for await (const path5 of g.scan({ cwd: workdir, onlyFiles: true })) {
38448
+ if (!path5.split("/").some((seg) => IGNORE_DIRS.has(seg))) {
38449
+ return true;
38435
38450
  }
38436
38451
  }
38437
- } catch (error48) {
38438
- if (isRootCall) {
38439
- throw error48;
38440
- }
38441
38452
  }
38442
- return results;
38453
+ return false;
38443
38454
  }
38444
38455
  async function isGreenfieldStory(_story, workdir, patterns) {
38445
38456
  try {
38446
- const regexes = globsToTestRegex(patterns ?? GREENFIELD_FALLBACK_PATTERNS);
38447
- const testFiles = await scanForTestFiles(workdir, regexes);
38448
- return testFiles.length === 0;
38449
- } catch (error48) {
38457
+ return !await hasTestFiles(workdir, patterns ?? DEFAULT_TEST_FILE_PATTERNS);
38458
+ } catch {
38450
38459
  return false;
38451
38460
  }
38452
38461
  }
38453
- var GREENFIELD_FALLBACK_PATTERNS;
38462
+ var _greenfieldDeps, IGNORE_DIRS;
38454
38463
  var init_greenfield = __esm(() => {
38455
- init_conventions();
38456
- GREENFIELD_FALLBACK_PATTERNS = Object.freeze([
38457
- "**/*.test.ts",
38458
- "**/*.test.js",
38459
- "**/*.test.tsx",
38460
- "**/*.test.jsx",
38461
- "**/*.spec.ts",
38462
- "**/*.spec.js",
38463
- "**/*.spec.tsx",
38464
- "**/*.spec.jsx",
38465
- "**/*_test.go",
38466
- "test_*.py",
38467
- "*_test.py"
38464
+ init_test_runners();
38465
+ _greenfieldDeps = {
38466
+ spawn: Bun.spawn
38467
+ };
38468
+ IGNORE_DIRS = new Set([
38469
+ "node_modules",
38470
+ "dist",
38471
+ ".next",
38472
+ ".nuxt",
38473
+ ".cache",
38474
+ "coverage",
38475
+ "vendor",
38476
+ "__pycache__",
38477
+ ".venv",
38478
+ "venv",
38479
+ ".eggs",
38480
+ "target",
38481
+ ".gradle",
38482
+ "out",
38483
+ "tmp",
38484
+ "temp",
38485
+ ".git"
38468
38486
  ]);
38469
38487
  });
38470
38488
 
@@ -38627,13 +38645,13 @@ __export(exports_runners, {
38627
38645
  _regressionRunnerDeps: () => _regressionRunnerDeps
38628
38646
  });
38629
38647
  import { existsSync as existsSync6 } from "fs";
38630
- import { join as join24 } from "path";
38648
+ import { join as join23 } from "path";
38631
38649
  async function verifyAssets(workingDirectory, expectedFiles) {
38632
38650
  if (!expectedFiles || expectedFiles.length === 0)
38633
38651
  return { success: true, missingFiles: [] };
38634
38652
  const missingFiles = [];
38635
38653
  for (const file3 of expectedFiles) {
38636
- if (!existsSync6(join24(workingDirectory, file3)))
38654
+ if (!existsSync6(join23(workingDirectory, file3)))
38637
38655
  missingFiles.push(file3);
38638
38656
  }
38639
38657
  if (missingFiles.length > 0) {
@@ -39080,7 +39098,7 @@ var init_apply_test_edit_declarations = __esm(() => {
39080
39098
  });
39081
39099
 
39082
39100
  // src/operations/validate-mock-structure-files.ts
39083
- import { join as join25 } from "path";
39101
+ import { join as join24 } from "path";
39084
39102
  async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
39085
39103
  const fileExists = deps?.fileExists ?? defaultFileExists;
39086
39104
  const valid = [];
@@ -39093,7 +39111,7 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
39093
39111
  const files = d.files ?? [d.file];
39094
39112
  let allValid = true;
39095
39113
  for (const file3 of files) {
39096
- const absolutePath = join25(packageDir, file3);
39114
+ const absolutePath = join24(packageDir, file3);
39097
39115
  const exists = await fileExists(absolutePath);
39098
39116
  if (!exists) {
39099
39117
  allValid = false;
@@ -40693,7 +40711,7 @@ var init_lint_parsing = __esm(() => {
40693
40711
  });
40694
40712
 
40695
40713
  // src/review/scoped-lint.ts
40696
- import { join as join26, relative as relative10 } from "path";
40714
+ import { join as join25, relative as relative10 } from "path";
40697
40715
  function shellQuotePath4(path5) {
40698
40716
  return `'${path5.replaceAll("'", "'\\''")}'`;
40699
40717
  }
@@ -40741,7 +40759,7 @@ function uniqueFiles(files) {
40741
40759
  async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
40742
40760
  const inScope = [];
40743
40761
  for (const relPath of files) {
40744
- const absPath = join26(workdir, relPath);
40762
+ const absPath = join25(workdir, relPath);
40745
40763
  const exists = await _scopedLintDeps.fileExists(absPath);
40746
40764
  if (!exists)
40747
40765
  continue;
@@ -44115,7 +44133,7 @@ var init_call = __esm(() => {
44115
44133
 
44116
44134
  // src/runtime/cost-aggregator.ts
44117
44135
  import { mkdirSync as mkdirSync2 } from "fs";
44118
- import { join as join27 } from "path";
44136
+ import { join as join26 } from "path";
44119
44137
  function makeCorrelationId() {
44120
44138
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
44121
44139
  }
@@ -44306,7 +44324,7 @@ class CostAggregator {
44306
44324
  if (events.length === 0 && errors3.length === 0)
44307
44325
  return;
44308
44326
  mkdirSync2(this._drainDir, { recursive: true });
44309
- const path5 = join27(this._drainDir, `${this._runId}.jsonl`);
44327
+ const path5 = join26(this._drainDir, `${this._runId}.jsonl`);
44310
44328
  const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
44311
44329
  await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
44312
44330
  `)}
@@ -44346,7 +44364,7 @@ var init_cost_aggregator = __esm(() => {
44346
44364
  // src/runtime/prompt-auditor.ts
44347
44365
  import { appendFileSync } from "fs";
44348
44366
  import { mkdir as mkdir4 } from "fs/promises";
44349
- import { join as join28 } from "path";
44367
+ import { join as join27 } from "path";
44350
44368
  function createNoOpPromptAuditor() {
44351
44369
  return {
44352
44370
  record() {},
@@ -44412,8 +44430,8 @@ class PromptAuditor {
44412
44430
  _jsonlPath;
44413
44431
  _featureDir;
44414
44432
  constructor(runId, flushDir, featureName) {
44415
- this._featureDir = join28(flushDir, featureName);
44416
- this._jsonlPath = join28(this._featureDir, `${runId}.jsonl`);
44433
+ this._featureDir = join27(flushDir, featureName);
44434
+ this._jsonlPath = join27(this._featureDir, `${runId}.jsonl`);
44417
44435
  }
44418
44436
  record(entry) {
44419
44437
  this._enqueue(entry);
@@ -44462,7 +44480,7 @@ class PromptAuditor {
44462
44480
  const auditEntry = entry;
44463
44481
  const filename = deriveTxtFilename(auditEntry);
44464
44482
  try {
44465
- await _promptAuditorDeps.write(join28(this._featureDir, filename), buildTxtContent(auditEntry));
44483
+ await _promptAuditorDeps.write(join27(this._featureDir, filename), buildTxtContent(auditEntry));
44466
44484
  } catch (err) {
44467
44485
  throw tagAuditError(err, "txt");
44468
44486
  }
@@ -44600,6 +44618,7 @@ function createPackageView(config2, packageDir, repoRoot, hasOverride) {
44600
44618
  function createPackageRegistry(loader, repoRoot) {
44601
44619
  const cache = new Map;
44602
44620
  const mergedConfigs = new Map;
44621
+ let hydrated = false;
44603
44622
  function toRelativeKey(packageDir) {
44604
44623
  if (!packageDir)
44605
44624
  return "";
@@ -44618,6 +44637,9 @@ function createPackageRegistry(loader, repoRoot) {
44618
44637
  }
44619
44638
  const overrideConfig = mergedConfigs.get(key);
44620
44639
  const hasOverride = overrideConfig !== undefined;
44640
+ if (!hasOverride && key && !hydrated) {
44641
+ _packagesDeps.getSafeLogger()?.warn("packages", "resolve() called for non-root package before hydrate(); returning root config (per-package overrides not applied)", { packageDir: key });
44642
+ }
44621
44643
  const config2 = overrideConfig ?? loader.current();
44622
44644
  const view = createPackageView(config2, key, repoRoot, hasOverride);
44623
44645
  cache.set(key, view);
@@ -44638,6 +44660,7 @@ function createPackageRegistry(loader, repoRoot) {
44638
44660
  cache.delete(dir);
44639
44661
  }
44640
44662
  }
44663
+ hydrated = true;
44641
44664
  }
44642
44665
  return {
44643
44666
  all() {
@@ -44650,8 +44673,11 @@ function createPackageRegistry(loader, repoRoot) {
44650
44673
  hydrate
44651
44674
  };
44652
44675
  }
44676
+ var _packagesDeps;
44653
44677
  var init_packages = __esm(() => {
44654
44678
  init_config();
44679
+ init_logger2();
44680
+ _packagesDeps = { getSafeLogger };
44655
44681
  });
44656
44682
 
44657
44683
  // src/runtime/agent-stream-events.ts
@@ -45610,7 +45636,7 @@ var init_pid_registry = __esm(() => {
45610
45636
  // src/session/manager-deps.ts
45611
45637
  import { randomUUID as randomUUID3 } from "crypto";
45612
45638
  import { mkdir as mkdir5 } from "fs/promises";
45613
- import { isAbsolute as isAbsolute9, join as join29, relative as relative11, sep as sep2 } from "path";
45639
+ import { isAbsolute as isAbsolute9, join as join28, relative as relative11, sep as sep2 } from "path";
45614
45640
  function resolveProjectDirFromScratchDir(scratchDir) {
45615
45641
  const marker = `${sep2}.nax${sep2}features${sep2}`;
45616
45642
  const markerIdx = scratchDir.lastIndexOf(marker);
@@ -45631,7 +45657,7 @@ var init_manager_deps = __esm(() => {
45631
45657
  now: () => new Date().toISOString(),
45632
45658
  nowMs: () => Date.now(),
45633
45659
  uuid: () => randomUUID3(),
45634
- sessionScratchDir: (projectDir, featureName, sessionId) => join29(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45660
+ sessionScratchDir: (projectDir, featureName, sessionId) => join28(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45635
45661
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
45636
45662
  await mkdir5(scratchDir, { recursive: true });
45637
45663
  const { handle: _handle, ...persistable } = descriptor;
@@ -45642,7 +45668,7 @@ var init_manager_deps = __esm(() => {
45642
45668
  persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
45643
45669
  }
45644
45670
  }
45645
- await Bun.write(join29(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45671
+ await Bun.write(join28(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45646
45672
  }
45647
45673
  };
45648
45674
  });
@@ -46393,7 +46419,7 @@ __export(exports_runtime, {
46393
46419
  CostAggregator: () => CostAggregator,
46394
46420
  AgentStreamEventBus: () => AgentStreamEventBus
46395
46421
  });
46396
- import { basename as basename5, join as join30 } from "path";
46422
+ import { basename as basename5, join as join29 } from "path";
46397
46423
  function createRuntime(config2, workdir, opts) {
46398
46424
  const runId = crypto.randomUUID();
46399
46425
  const controller = new AbortController;
@@ -46409,10 +46435,10 @@ function createRuntime(config2, workdir, opts) {
46409
46435
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
46410
46436
  const globalDir = globalOutputDir();
46411
46437
  const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
46412
- const costDir = join30(outputDir, "cost");
46438
+ const costDir = join29(outputDir, "cost");
46413
46439
  const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
46414
46440
  const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
46415
- const auditDir = config2.agent?.promptAudit?.dir ?? join30(outputDir, "prompt-audit");
46441
+ const auditDir = config2.agent?.promptAudit?.dir ?? join29(outputDir, "prompt-audit");
46416
46442
  let promptAuditor;
46417
46443
  if (opts?.promptAuditor) {
46418
46444
  promptAuditor = opts.promptAuditor;
@@ -46565,9 +46591,9 @@ async function allSettledBounded(tasks, limit) {
46565
46591
 
46566
46592
  // src/context/injector.ts
46567
46593
  import { existsSync as existsSync8 } from "fs";
46568
- import { join as join31 } from "path";
46594
+ import { join as join30 } from "path";
46569
46595
  async function detectNode(workdir) {
46570
- const pkgPath = join31(workdir, "package.json");
46596
+ const pkgPath = join30(workdir, "package.json");
46571
46597
  if (!existsSync8(pkgPath))
46572
46598
  return null;
46573
46599
  try {
@@ -46584,7 +46610,7 @@ async function detectNode(workdir) {
46584
46610
  }
46585
46611
  }
46586
46612
  async function detectGo(workdir) {
46587
- const goMod = join31(workdir, "go.mod");
46613
+ const goMod = join30(workdir, "go.mod");
46588
46614
  if (!existsSync8(goMod))
46589
46615
  return null;
46590
46616
  try {
@@ -46608,7 +46634,7 @@ async function detectGo(workdir) {
46608
46634
  }
46609
46635
  }
46610
46636
  async function detectRust(workdir) {
46611
- const cargoPath = join31(workdir, "Cargo.toml");
46637
+ const cargoPath = join30(workdir, "Cargo.toml");
46612
46638
  if (!existsSync8(cargoPath))
46613
46639
  return null;
46614
46640
  try {
@@ -46624,8 +46650,8 @@ async function detectRust(workdir) {
46624
46650
  }
46625
46651
  }
46626
46652
  async function detectPython(workdir) {
46627
- const pyproject = join31(workdir, "pyproject.toml");
46628
- const requirements = join31(workdir, "requirements.txt");
46653
+ const pyproject = join30(workdir, "pyproject.toml");
46654
+ const requirements = join30(workdir, "requirements.txt");
46629
46655
  if (!existsSync8(pyproject) && !existsSync8(requirements))
46630
46656
  return null;
46631
46657
  try {
@@ -46644,7 +46670,7 @@ async function detectPython(workdir) {
46644
46670
  }
46645
46671
  }
46646
46672
  async function detectPhp(workdir) {
46647
- const composerPath = join31(workdir, "composer.json");
46673
+ const composerPath = join30(workdir, "composer.json");
46648
46674
  if (!existsSync8(composerPath))
46649
46675
  return null;
46650
46676
  try {
@@ -46657,7 +46683,7 @@ async function detectPhp(workdir) {
46657
46683
  }
46658
46684
  }
46659
46685
  async function detectRuby(workdir) {
46660
- const gemfile = join31(workdir, "Gemfile");
46686
+ const gemfile = join30(workdir, "Gemfile");
46661
46687
  if (!existsSync8(gemfile))
46662
46688
  return null;
46663
46689
  try {
@@ -46669,9 +46695,9 @@ async function detectRuby(workdir) {
46669
46695
  }
46670
46696
  }
46671
46697
  async function detectJvm(workdir) {
46672
- const pom = join31(workdir, "pom.xml");
46673
- const gradle = join31(workdir, "build.gradle");
46674
- const gradleKts = join31(workdir, "build.gradle.kts");
46698
+ const pom = join30(workdir, "pom.xml");
46699
+ const gradle = join30(workdir, "build.gradle");
46700
+ const gradleKts = join30(workdir, "build.gradle.kts");
46675
46701
  if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
46676
46702
  return null;
46677
46703
  try {
@@ -46679,7 +46705,7 @@ async function detectJvm(workdir) {
46679
46705
  const content2 = await Bun.file(pom).text();
46680
46706
  const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
46681
46707
  const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
46682
- const lang2 = existsSync8(join31(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46708
+ const lang2 = existsSync8(join30(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46683
46709
  return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
46684
46710
  }
46685
46711
  const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
@@ -46933,7 +46959,7 @@ var init_windsurf = __esm(() => {
46933
46959
 
46934
46960
  // src/context/generator.ts
46935
46961
  import { existsSync as existsSync9 } from "fs";
46936
- import { join as join32, relative as relative12 } from "path";
46962
+ import { join as join31, relative as relative12 } from "path";
46937
46963
  async function loadContextContent(options, config2) {
46938
46964
  if (!_generatorDeps.existsSync(options.contextPath)) {
46939
46965
  throw new Error(`Context file not found: ${options.contextPath}`);
@@ -46951,7 +46977,7 @@ async function generateFor(agent, options, config2) {
46951
46977
  try {
46952
46978
  const context = await loadContextContent(options, config2);
46953
46979
  const content = generator.generate(context);
46954
- const outputPath = join32(options.outputDir, generator.outputFile);
46980
+ const outputPath = join31(options.outputDir, generator.outputFile);
46955
46981
  validateFilePath(outputPath, options.outputDir);
46956
46982
  if (!options.dryRun) {
46957
46983
  await _generatorDeps.writeFile(outputPath, content);
@@ -46969,7 +46995,7 @@ async function generateAll(options, config2, agentFilter) {
46969
46995
  for (const [agentKey, generator] of entries) {
46970
46996
  try {
46971
46997
  const content = generator.generate(context);
46972
- const outputPath = join32(options.outputDir, generator.outputFile);
46998
+ const outputPath = join31(options.outputDir, generator.outputFile);
46973
46999
  validateFilePath(outputPath, options.outputDir);
46974
47000
  if (!options.dryRun) {
46975
47001
  await _generatorDeps.writeFile(outputPath, content);
@@ -46989,7 +47015,7 @@ async function discoverPackages(repoRoot) {
46989
47015
  const glob = new Bun.Glob(pattern);
46990
47016
  for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
46991
47017
  const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
46992
- const pkgAbsolute = join32(repoRoot, pkgRelative);
47018
+ const pkgAbsolute = join31(repoRoot, pkgRelative);
46993
47019
  if (!seen.has(pkgAbsolute)) {
46994
47020
  seen.add(pkgAbsolute);
46995
47021
  packages.push(pkgAbsolute);
@@ -47021,14 +47047,14 @@ async function discoverWorkspacePackages2(repoRoot) {
47021
47047
  }
47022
47048
  }
47023
47049
  }
47024
- const turboPath = join32(repoRoot, "turbo.json");
47050
+ const turboPath = join31(repoRoot, "turbo.json");
47025
47051
  try {
47026
47052
  const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
47027
47053
  if (Array.isArray(turbo.packages)) {
47028
47054
  await resolveGlobs(turbo.packages);
47029
47055
  }
47030
47056
  } catch {}
47031
- const pkgPath = join32(repoRoot, "package.json");
47057
+ const pkgPath = join31(repoRoot, "package.json");
47032
47058
  try {
47033
47059
  const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
47034
47060
  const ws = pkg.workspaces;
@@ -47036,7 +47062,7 @@ async function discoverWorkspacePackages2(repoRoot) {
47036
47062
  if (patterns.length > 0)
47037
47063
  await resolveGlobs(patterns);
47038
47064
  } catch {}
47039
- const pnpmPath = join32(repoRoot, "pnpm-workspace.yaml");
47065
+ const pnpmPath = join31(repoRoot, "pnpm-workspace.yaml");
47040
47066
  try {
47041
47067
  const raw = await _generatorDeps.readTextFile(pnpmPath);
47042
47068
  const lines = raw.split(`
@@ -47062,7 +47088,7 @@ async function discoverWorkspacePackages2(repoRoot) {
47062
47088
  async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
47063
47089
  const resolvedRepoRoot = repoRoot ?? packageDir;
47064
47090
  const relativePkgPath = relative12(resolvedRepoRoot, packageDir);
47065
- const contextPath = join32(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
47091
+ const contextPath = join31(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
47066
47092
  if (!_generatorDeps.existsSync(contextPath)) {
47067
47093
  return [
47068
47094
  {
@@ -47130,7 +47156,7 @@ var init_generator2 = __esm(() => {
47130
47156
  });
47131
47157
 
47132
47158
  // src/analyze/scanner.ts
47133
- import { join as join33 } from "path";
47159
+ import { join as join32 } from "path";
47134
47160
  function resolveFrameworkAndRunner(language, pkg) {
47135
47161
  if (language === "go")
47136
47162
  return { framework: "", testRunner: "go-test" };
@@ -47152,7 +47178,7 @@ async function scanSourceRoots(workdir) {
47152
47178
  });
47153
47179
  try {
47154
47180
  const language = await deps.detectLanguage(workdir);
47155
- const pkg = await deps.readPackageJson(join33(workdir, "package.json"));
47181
+ const pkg = await deps.readPackageJson(join32(workdir, "package.json"));
47156
47182
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
47157
47183
  return [{ path: ".", language, framework, testRunner }];
47158
47184
  } catch {
@@ -47170,9 +47196,9 @@ async function scanSourceRoots(workdir) {
47170
47196
  packages = packages.slice(0, MAX_SOURCE_ROOTS);
47171
47197
  }
47172
47198
  return Promise.all(packages.map(async (pkgPath) => {
47173
- const pkgDir = pkgPath === "." ? workdir : join33(workdir, pkgPath);
47199
+ const pkgDir = pkgPath === "." ? workdir : join32(workdir, pkgPath);
47174
47200
  const language = await deps.detectLanguage(pkgDir);
47175
- const pkg = await deps.readPackageJson(join33(pkgDir, "package.json"));
47201
+ const pkg = await deps.readPackageJson(join32(pkgDir, "package.json"));
47176
47202
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
47177
47203
  return { path: pkgPath, language, framework, testRunner };
47178
47204
  }));
@@ -47205,7 +47231,7 @@ var init_analyze = __esm(() => {
47205
47231
  });
47206
47232
 
47207
47233
  // src/debate/pre-phase/grounder.ts
47208
- import { join as join34 } from "path";
47234
+ import { join as join33 } from "path";
47209
47235
  async function buildCodebaseContext(workdir) {
47210
47236
  const roots = await _grounderDeps.scanSourceRoots(workdir);
47211
47237
  return buildSourceRootsSection(normalizeRoots(workdir, roots));
@@ -47217,7 +47243,7 @@ function normalizeRoots(workdir, roots) {
47217
47243
  }));
47218
47244
  }
47219
47245
  async function writeManifestArtifact(ctx, manifest) {
47220
- const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47246
+ const manifestPath = join33(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47221
47247
  await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
47222
47248
  }
47223
47249
  var _grounderDeps, grounderStrategy = async (ctx) => {
@@ -47560,7 +47586,7 @@ function formatSpecDeltas(blockers, manifest) {
47560
47586
 
47561
47587
  // src/debate/verifiers/checks.ts
47562
47588
  import { existsSync as defaultExistsSync } from "fs";
47563
- import { join as join35 } from "path";
47589
+ import { join as join34 } from "path";
47564
47590
  function checkFilesExist(prd, workdir, deps) {
47565
47591
  const existsSync10 = deps?.existsSync ?? defaultExistsSync;
47566
47592
  const findings = [];
@@ -47570,7 +47596,7 @@ function checkFilesExist(prd, workdir, deps) {
47570
47596
  for (const entry of story.contextFiles) {
47571
47597
  const filePath = typeof entry === "string" ? entry : entry.path;
47572
47598
  const factId = typeof entry === "string" ? undefined : entry.factId;
47573
- const absPath = join35(workdir, filePath);
47599
+ const absPath = join34(workdir, filePath);
47574
47600
  if (existsSync10(absPath))
47575
47601
  continue;
47576
47602
  if (factId) {
@@ -47681,7 +47707,7 @@ var init_checks3 = () => {};
47681
47707
 
47682
47708
  // src/debate/verifiers/plan-checklist.ts
47683
47709
  import { existsSync as existsSync10 } from "fs";
47684
- import { join as join36 } from "path";
47710
+ import { join as join35 } from "path";
47685
47711
  function parsePrd(output) {
47686
47712
  if (!output)
47687
47713
  return null;
@@ -47692,7 +47718,7 @@ function parsePrd(output) {
47692
47718
  }
47693
47719
  }
47694
47720
  async function loadManifest(ctx) {
47695
- const manifestPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47721
+ const manifestPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47696
47722
  const raw = await _planChecklistDeps.readFile(manifestPath);
47697
47723
  if (!raw)
47698
47724
  return null;
@@ -47705,7 +47731,7 @@ async function loadManifest(ctx) {
47705
47731
  }
47706
47732
  }
47707
47733
  async function emitSpecDeltas(ctx, blockers, manifest) {
47708
- const artifactPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47734
+ const artifactPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47709
47735
  const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
47710
47736
  await _planChecklistDeps.write(artifactPath, content);
47711
47737
  return artifactPath;
@@ -48051,7 +48077,7 @@ var init_runner_plan_helpers = __esm(() => {
48051
48077
  });
48052
48078
 
48053
48079
  // src/debate/runner-plan.ts
48054
- import { join as join37 } from "path";
48080
+ import { join as join36 } from "path";
48055
48081
  async function runPlan(ctx, taskContext, outputFormat, opts) {
48056
48082
  const logger = _debateSessionDeps.getSafeLogger();
48057
48083
  const config2 = ctx.stageConfig;
@@ -48110,7 +48136,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
48110
48136
  sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
48111
48137
  proposers: ctx.stageConfig.proposers
48112
48138
  });
48113
- const outputPaths = resolved.map((_, i) => join37(opts.outputDir, `prd-debate-${i}.json`));
48139
+ const outputPaths = resolved.map((_, i) => join36(opts.outputDir, `prd-debate-${i}.json`));
48114
48140
  const successful = [];
48115
48141
  let rebuttalList;
48116
48142
  if (selectorKind === "verifier-pick") {
@@ -50353,9 +50379,9 @@ function validateFeatureName(feature) {
50353
50379
 
50354
50380
  // src/plan/critic.ts
50355
50381
  import { mkdir as mkdir6 } from "fs/promises";
50356
- import { dirname as dirname7, join as join40 } from "path";
50382
+ import { dirname as dirname7, join as join39 } from "path";
50357
50383
  async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
50358
- const path7 = join40(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50384
+ const path7 = join39(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50359
50385
  await mkdir6(dirname7(path7), { recursive: true });
50360
50386
  await Bun.write(path7, formatSpecDeltas(findings, manifest));
50361
50387
  return path7;
@@ -51568,9 +51594,9 @@ __export(exports_plan_decompose, {
51568
51594
  runReplanLoop: () => runReplanLoop,
51569
51595
  planDecomposeCommand: () => planDecomposeCommand
51570
51596
  });
51571
- import { join as join41 } from "path";
51597
+ import { join as join40 } from "path";
51572
51598
  async function planDecomposeCommand(workdir, config2, options) {
51573
- const prdPath = join41(workdir, ".nax", "features", options.feature, "prd.json");
51599
+ const prdPath = join40(workdir, ".nax", "features", options.feature, "prd.json");
51574
51600
  if (!_planDeps.existsSync(prdPath)) {
51575
51601
  throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
51576
51602
  stage: "decompose",
@@ -51744,7 +51770,7 @@ var init_plan_decompose = __esm(() => {
51744
51770
 
51745
51771
  // src/cli/plan-runtime.ts
51746
51772
  import { existsSync as existsSync15 } from "fs";
51747
- import { join as join42 } from "path";
51773
+ import { join as join41 } from "path";
51748
51774
  function isRuntimeWithAgentManager(value) {
51749
51775
  return typeof value === "object" && value !== null && "agentManager" in value;
51750
51776
  }
@@ -51796,7 +51822,7 @@ var init_plan_runtime = __esm(() => {
51796
51822
  writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
51797
51823
  scanSourceRoots: (workdir) => scanSourceRoots(workdir),
51798
51824
  createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
51799
- readPackageJson: (workdir) => Bun.file(join42(workdir, "package.json")).json().catch(() => null),
51825
+ readPackageJson: (workdir) => Bun.file(join41(workdir, "package.json")).json().catch(() => null),
51800
51826
  spawnSync: (cmd, opts) => {
51801
51827
  const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
51802
51828
  return { stdout: result.stdout, exitCode: result.exitCode };
@@ -52181,7 +52207,7 @@ var init_metrics = __esm(() => {
52181
52207
 
52182
52208
  // src/commands/common.ts
52183
52209
  import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
52184
- import { join as join43, resolve as resolve13 } from "path";
52210
+ import { join as join42, resolve as resolve13 } from "path";
52185
52211
  function resolveProject(options = {}) {
52186
52212
  const { dir, feature } = options;
52187
52213
  let projectRoot;
@@ -52189,12 +52215,12 @@ function resolveProject(options = {}) {
52189
52215
  let configPath;
52190
52216
  if (dir) {
52191
52217
  projectRoot = realpathSync3(resolve13(dir));
52192
- naxDir = join43(projectRoot, ".nax");
52218
+ naxDir = join42(projectRoot, ".nax");
52193
52219
  if (!existsSync16(naxDir)) {
52194
52220
  throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
52195
52221
  Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
52196
52222
  }
52197
- configPath = join43(naxDir, "config.json");
52223
+ configPath = join42(naxDir, "config.json");
52198
52224
  if (!existsSync16(configPath)) {
52199
52225
  throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
52200
52226
  Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
@@ -52202,17 +52228,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
52202
52228
  } else {
52203
52229
  const found = findProjectRoot(process.cwd());
52204
52230
  if (!found) {
52205
- const cwdNaxDir = join43(process.cwd(), ".nax");
52231
+ const cwdNaxDir = join42(process.cwd(), ".nax");
52206
52232
  if (existsSync16(cwdNaxDir)) {
52207
- const cwdConfigPath = join43(cwdNaxDir, "config.json");
52233
+ const cwdConfigPath = join42(cwdNaxDir, "config.json");
52208
52234
  throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
52209
52235
  Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
52210
52236
  }
52211
52237
  throw new NaxError("No nax project found. Run this command from within a nax project directory, or use -d flag to specify the project path.", "PROJECT_NOT_FOUND", { cwd: process.cwd() });
52212
52238
  }
52213
52239
  projectRoot = found;
52214
- naxDir = join43(projectRoot, ".nax");
52215
- configPath = join43(naxDir, "config.json");
52240
+ naxDir = join42(projectRoot, ".nax");
52241
+ configPath = join42(naxDir, "config.json");
52216
52242
  }
52217
52243
  let featureDir;
52218
52244
  if (feature) {
@@ -52221,8 +52247,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
52221
52247
  } catch (error48) {
52222
52248
  throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
52223
52249
  }
52224
- const featuresDir = join43(naxDir, "features");
52225
- featureDir = join43(featuresDir, feature);
52250
+ const featuresDir = join42(naxDir, "features");
52251
+ featureDir = join42(featuresDir, feature);
52226
52252
  if (!existsSync16(featureDir)) {
52227
52253
  const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
52228
52254
  const availableMsg = availableFeatures.length > 0 ? `
@@ -52255,7 +52281,7 @@ async function resolveProjectAsync(options = {}) {
52255
52281
  }
52256
52282
  const isPlainName = !dir.includes("/") && !dir.includes("\\");
52257
52283
  if (isPlainName) {
52258
- const registryIdentityPath = join43(globalConfigDir(), dir, ".identity");
52284
+ const registryIdentityPath = join42(globalConfigDir(), dir, ".identity");
52259
52285
  const identityFile = Bun.file(registryIdentityPath);
52260
52286
  if (await identityFile.exists()) {
52261
52287
  try {
@@ -52287,12 +52313,12 @@ function findProjectRoot(startDir) {
52287
52313
  let current = resolve13(startDir);
52288
52314
  let depth = 0;
52289
52315
  while (depth < MAX_DIRECTORY_DEPTH) {
52290
- const naxDir = join43(current, ".nax");
52291
- const configPath = join43(naxDir, "config.json");
52316
+ const naxDir = join42(current, ".nax");
52317
+ const configPath = join42(naxDir, "config.json");
52292
52318
  if (existsSync16(configPath)) {
52293
52319
  return realpathSync3(current);
52294
52320
  }
52295
- const parent = join43(current, "..");
52321
+ const parent = join42(current, "..");
52296
52322
  if (parent === current) {
52297
52323
  break;
52298
52324
  }
@@ -52619,8 +52645,8 @@ var init_semantic_verdict = __esm(() => {
52619
52645
  await Bun.write(filePath, content);
52620
52646
  },
52621
52647
  readdir: async (dir) => {
52622
- const { readdir: readdir3 } = await import("fs/promises");
52623
- return readdir3(dir);
52648
+ const { readdir: readdir2 } = await import("fs/promises");
52649
+ return readdir2(dir);
52624
52650
  },
52625
52651
  readFile: async (filePath) => {
52626
52652
  return Bun.file(filePath).text();
@@ -52800,7 +52826,8 @@ __export(exports_acceptance, {
52800
52826
  convertFixStoryToUserStory: () => convertFixStoryToUserStory,
52801
52827
  buildAcceptanceTestPrompt: () => buildAcceptanceTestPrompt,
52802
52828
  buildAcceptanceRunCommand: () => buildAcceptanceRunCommand,
52803
- acceptanceTestFilename: () => acceptanceTestFilename2
52829
+ acceptanceTestFilename: () => acceptanceTestFilename2,
52830
+ _groupDeps: () => _groupDeps
52804
52831
  });
52805
52832
  var init_acceptance2 = __esm(() => {
52806
52833
  init_refinement();
@@ -53071,10 +53098,10 @@ var init_acceptance_setup = __esm(() => {
53071
53098
  },
53072
53099
  deleteSemanticVerdicts: async (featureDir) => {
53073
53100
  const dir = `${featureDir}/semantic-verdicts`;
53074
- const { readdir: readdir3, unlink: unlink2 } = await import("fs/promises");
53101
+ const { readdir: readdir2, unlink: unlink2 } = await import("fs/promises");
53075
53102
  let files;
53076
53103
  try {
53077
- files = await readdir3(dir);
53104
+ files = await readdir2(dir);
53078
53105
  } catch (err) {
53079
53106
  if (err.code === "ENOENT")
53080
53107
  return;
@@ -53149,7 +53176,7 @@ ${stderr}` };
53149
53176
  const metaPath = path10.join(ctx.featureDir, "acceptance-meta.json");
53150
53177
  const allCriteria = ctx.prd.userStories.filter((s) => !s.id.startsWith("US-FIX-") && s.status !== "decomposed").flatMap((s) => s.acceptanceCriteria);
53151
53178
  const featureName = ctx.prd.feature ?? ctx.prd.featureName;
53152
- const groups = groupStoriesByPackage(ctx.prd, ctx.workdir, featureName, testPathConfig, language);
53179
+ const groups = await groupStoriesByPackage(ctx.prd, ctx.workdir, featureName, testPathConfig, language);
53153
53180
  const nonFixStories = groups.flatMap((g) => g.stories);
53154
53181
  let totalCriteria = 0;
53155
53182
  let testableCount = 0;
@@ -53451,10 +53478,10 @@ var init_effectiveness = __esm(() => {
53451
53478
 
53452
53479
  // src/execution/progress.ts
53453
53480
  import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
53454
- import { join as join46 } from "path";
53481
+ import { join as join45 } from "path";
53455
53482
  async function appendProgress(featureDir, storyId, status, message) {
53456
53483
  await mkdir7(featureDir, { recursive: true });
53457
- const progressPath = join46(featureDir, "progress.txt");
53484
+ const progressPath = join45(featureDir, "progress.txt");
53458
53485
  const timestamp = new Date().toISOString();
53459
53486
  const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
53460
53487
  `;
@@ -53648,7 +53675,7 @@ var init_completion = __esm(() => {
53648
53675
 
53649
53676
  // src/constitution/loader.ts
53650
53677
  import { existsSync as existsSync19 } from "fs";
53651
- import { join as join47 } from "path";
53678
+ import { join as join46 } from "path";
53652
53679
  function truncateToTokens(text, maxTokens) {
53653
53680
  const maxChars = maxTokens * 3;
53654
53681
  if (text.length <= maxChars) {
@@ -53670,7 +53697,7 @@ async function loadConstitution(projectDir, config2) {
53670
53697
  }
53671
53698
  let combinedContent = "";
53672
53699
  if (!config2.skipGlobal) {
53673
- const globalPath = join47(globalConfigDir(), config2.path);
53700
+ const globalPath = join46(globalConfigDir(), config2.path);
53674
53701
  if (existsSync19(globalPath)) {
53675
53702
  const validatedPath = validateFilePath(globalPath, globalConfigDir());
53676
53703
  const globalFile = Bun.file(validatedPath);
@@ -53680,7 +53707,7 @@ async function loadConstitution(projectDir, config2) {
53680
53707
  }
53681
53708
  }
53682
53709
  }
53683
- const projectPath = join47(projectDir, config2.path);
53710
+ const projectPath = join46(projectDir, config2.path);
53684
53711
  if (existsSync19(projectPath)) {
53685
53712
  const validatedPath = validateFilePath(projectPath, projectDir);
53686
53713
  const projectFile = Bun.file(validatedPath);
@@ -55114,7 +55141,7 @@ var init_story_orchestrator = __esm(() => {
55114
55141
  });
55115
55142
 
55116
55143
  // src/execution/build-plan-for-strategy.ts
55117
- import { join as join48 } from "path";
55144
+ import { join as join47 } from "path";
55118
55145
  function requiresInitialRefCapture(strategy) {
55119
55146
  return isThreeSessionStrategy(strategy);
55120
55147
  }
@@ -55160,7 +55187,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
55160
55187
  }
55161
55188
  if (shouldRunRectification(config2) && inputs.rectification) {
55162
55189
  const sink = makeDeclarationSink();
55163
- const packageDir = join48(ctx.packageDir, story.workdir ?? "");
55190
+ const packageDir = join47(ctx.packageDir, story.workdir ?? "");
55164
55191
  const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
55165
55192
  const strategies = [];
55166
55193
  const pkgQuality = ctx.packageView.select(qualityConfigSelector).quality;
@@ -55230,8 +55257,27 @@ var init_build_plan_for_strategy = __esm(() => {
55230
55257
  init_story_orchestrator();
55231
55258
  });
55232
55259
 
55260
+ // src/utils/paths.ts
55261
+ import { join as join48, relative as relative13, sep as sep4 } from "path";
55262
+ function packageDirRelative(projectDir, workdir) {
55263
+ if (!projectDir || !workdir || workdir === projectDir)
55264
+ return;
55265
+ const rel = relative13(projectDir, workdir);
55266
+ if (rel === ".." || rel.startsWith(`..${sep4}`))
55267
+ return;
55268
+ return rel && rel !== "." ? rel : undefined;
55269
+ }
55270
+ function getRunsDir() {
55271
+ return process.env.NAX_RUNS_DIR ?? join48(globalConfigDir(), "runs");
55272
+ }
55273
+ function getEventsRootDir() {
55274
+ return join48(globalConfigDir(), "events");
55275
+ }
55276
+ var init_paths3 = __esm(() => {
55277
+ init_paths();
55278
+ });
55279
+
55233
55280
  // src/execution/plan-inputs.ts
55234
- import { relative as relative13, sep as sep4 } from "path";
55235
55281
  function validatePlanInputs(story, config2) {
55236
55282
  if (!story.id || story.id.trim() === "") {
55237
55283
  throw new NaxError("Story ID is required and must be non-empty", "STORY_ID_INVALID", {
@@ -55307,15 +55353,8 @@ async function assemblePlanInputsFromCtx(ctx) {
55307
55353
  if (!_isTdd && !ctx.prompt?.trim()) {
55308
55354
  throw new NaxError(`Prompt missing for strategy "${ctx.routing.testStrategy}" \u2014 non-TDD strategies require ctx.prompt`, "PROMPT_NOT_BUILT", { stage: "plan-inputs", storyId: story.id, testStrategy: ctx.routing.testStrategy });
55309
55355
  }
55310
- const packageDirRelative = (() => {
55311
- if (ctx.workdir === ctx.projectDir)
55312
- return;
55313
- const rel = relative13(ctx.projectDir, ctx.workdir);
55314
- if (rel === ".." || rel.startsWith(`..${sep4}`))
55315
- return;
55316
- return rel && rel !== "." ? rel : undefined;
55317
- })();
55318
- const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.projectDir, packageDirRelative);
55356
+ const packageDirRel = packageDirRelative(ctx.projectDir, ctx.workdir);
55357
+ const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.projectDir, packageDirRel);
55319
55358
  const [testWriterPrompt, implementerPrompt, verifierPrompt] = _isTdd ? await Promise.all([
55320
55359
  _isFreshRun ? buildThreeSessionPrompt("test-writer", ctx, isLite) : Promise.resolve(""),
55321
55360
  buildThreeSessionPrompt("implementer", ctx, isLite),
@@ -55326,7 +55365,8 @@ async function assemblePlanInputsFromCtx(ctx) {
55326
55365
  promptMarkdown: testWriterPrompt,
55327
55366
  featureContextMarkdown: ctx.featureContextMarkdown,
55328
55367
  constitution: ctx.constitution?.content,
55329
- lite: isLite
55368
+ lite: isLite,
55369
+ resolvedTestPatterns
55330
55370
  } : undefined;
55331
55371
  const greenfieldGateInput = _isTdd && _isFreshRun && resolvedTestPatterns ? { story, workdir: ctx.workdir, resolvedTestPatterns } : undefined;
55332
55372
  const implementerInput = {
@@ -55351,7 +55391,7 @@ async function assemblePlanInputsFromCtx(ctx) {
55351
55391
  naxIgnoreIndex: ctx.naxIgnoreIndex,
55352
55392
  regressionMode: toVerifyScopedMode(ctx.config.execution?.regressionGate?.mode),
55353
55393
  repoRoot: ctx.projectDir,
55354
- packagePrefix: packageDirRelative,
55394
+ packagePrefix: packageDirRel,
55355
55395
  resolvedTestPatterns
55356
55396
  } : undefined;
55357
55397
  const lintCheckInput = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("lint") && ctx.config.quality.commands.lint ? { workdir: ctx.workdir, storyId: story.id } : undefined;
@@ -55456,6 +55496,7 @@ var init_plan_inputs = __esm(() => {
55456
55496
  init_prompts();
55457
55497
  init_review();
55458
55498
  init_resolver();
55499
+ init_paths3();
55459
55500
  });
55460
55501
 
55461
55502
  // src/pipeline/stages/execution-helpers.ts
@@ -56521,6 +56562,8 @@ var init_queue_check = __esm(() => {
56521
56562
  // src/pipeline/stages/routing.ts
56522
56563
  var routingStage, _routingDeps;
56523
56564
  var init_routing2 = __esm(() => {
56565
+ init_test_runners();
56566
+ init_paths3();
56524
56567
  init_greenfield();
56525
56568
  init_logger2();
56526
56569
  init_prd();
@@ -56564,7 +56607,16 @@ var init_routing2 = __esm(() => {
56564
56607
  const greenfieldDetectionEnabled = ctx.config.tdd.greenfieldDetection ?? true;
56565
56608
  if (greenfieldDetectionEnabled && routing.testStrategy.startsWith("three-session-tdd")) {
56566
56609
  const greenfieldScanDir = ctx.workdir;
56567
- const isGreenfield = await _routingDeps.isGreenfieldStory(ctx.story, greenfieldScanDir);
56610
+ const root = ctx.projectDir ?? ctx.workdir;
56611
+ const packageDir = packageDirRelative(root, ctx.workdir);
56612
+ const resolved = await _routingDeps.resolveTestFilePatterns(ctx.config, root, packageDir, { storyId: ctx.story.id }).catch((err) => {
56613
+ logger.debug("routing", "Test-pattern resolution failed; using default greenfield patterns", {
56614
+ storyId: ctx.story.id,
56615
+ error: errorMessage(err)
56616
+ });
56617
+ return;
56618
+ });
56619
+ const isGreenfield = await _routingDeps.isGreenfieldStory(ctx.story, greenfieldScanDir, resolved?.globs);
56568
56620
  if (isGreenfield) {
56569
56621
  logger.info("routing", "Greenfield detected \u2014 forcing test-after strategy", {
56570
56622
  storyId: ctx.story.id,
@@ -56592,6 +56644,7 @@ var init_routing2 = __esm(() => {
56592
56644
  resolveRouting,
56593
56645
  complexityToModelTier,
56594
56646
  isGreenfieldStory,
56647
+ resolveTestFilePatterns,
56595
56648
  clearCache,
56596
56649
  savePRD
56597
56650
  };
@@ -59508,18 +59561,6 @@ var init_loader4 = __esm(() => {
59508
59561
  FALLBACK_TEST_FILE_RE = /\.(test|spec)\.(ts|js|tsx|jsx|mjs)$/;
59509
59562
  });
59510
59563
 
59511
- // src/utils/paths.ts
59512
- import { join as join68 } from "path";
59513
- function getRunsDir() {
59514
- return process.env.NAX_RUNS_DIR ?? join68(globalConfigDir(), "runs");
59515
- }
59516
- function getEventsRootDir() {
59517
- return join68(globalConfigDir(), "events");
59518
- }
59519
- var init_paths3 = __esm(() => {
59520
- init_paths();
59521
- });
59522
-
59523
59564
  // src/utils/command-argv.ts
59524
59565
  function parseCommandToArgv(command) {
59525
59566
  const safeEnv = buildAllowedEnv();
@@ -59574,7 +59615,7 @@ var init_command_argv = __esm(() => {
59574
59615
  });
59575
59616
 
59576
59617
  // src/hooks/runner.ts
59577
- import { join as join75 } from "path";
59618
+ import { join as join74 } from "path";
59578
59619
  function createDrainDeadline2(deadlineMs) {
59579
59620
  let timeoutId;
59580
59621
  const promise2 = new Promise((resolve16) => {
@@ -59593,14 +59634,14 @@ async function loadHooksConfig(projectDir, globalDir) {
59593
59634
  let globalHooks = { hooks: {} };
59594
59635
  let projectHooks = { hooks: {} };
59595
59636
  let skipGlobal = false;
59596
- const projectPath = join75(projectDir, "hooks.json");
59637
+ const projectPath = join74(projectDir, "hooks.json");
59597
59638
  const projectData = await loadJsonFile(projectPath, "hooks");
59598
59639
  if (projectData) {
59599
59640
  projectHooks = projectData;
59600
59641
  skipGlobal = projectData.skipGlobal ?? false;
59601
59642
  }
59602
59643
  if (!skipGlobal && globalDir) {
59603
- const globalPath = join75(globalDir, "hooks.json");
59644
+ const globalPath = join74(globalDir, "hooks.json");
59604
59645
  const globalData = await loadJsonFile(globalPath, "hooks");
59605
59646
  if (globalData) {
59606
59647
  globalHooks = globalData;
@@ -59770,7 +59811,7 @@ var package_default;
59770
59811
  var init_package = __esm(() => {
59771
59812
  package_default = {
59772
59813
  name: "@nathapp/nax",
59773
- version: "0.69.7",
59814
+ version: "0.69.9",
59774
59815
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
59775
59816
  type: "module",
59776
59817
  bin: {
@@ -59865,8 +59906,8 @@ var init_version = __esm(() => {
59865
59906
  NAX_VERSION = package_default.version;
59866
59907
  NAX_COMMIT = (() => {
59867
59908
  try {
59868
- if (/^[0-9a-f]{6,10}$/.test("d524317e"))
59869
- return "d524317e";
59909
+ if (/^[0-9a-f]{6,10}$/.test("03eecdb1"))
59910
+ return "03eecdb1";
59870
59911
  } catch {}
59871
59912
  try {
59872
59913
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -60743,15 +60784,15 @@ var init_acceptance_loop = __esm(() => {
60743
60784
 
60744
60785
  // src/session/scratch-purge.ts
60745
60786
  import { mkdir as mkdir12, rename, rm } from "fs/promises";
60746
- import { dirname as dirname12, join as join76 } from "path";
60787
+ import { dirname as dirname12, join as join75 } from "path";
60747
60788
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
60748
- const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
60789
+ const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
60749
60790
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
60750
60791
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
60751
60792
  let purged = 0;
60752
60793
  for (const sessionId of sessionIds) {
60753
- const sessionDir = join76(sessionsDir, sessionId);
60754
- const descriptorPath = join76(sessionDir, "descriptor.json");
60794
+ const sessionDir = join75(sessionsDir, sessionId);
60795
+ const descriptorPath = join75(sessionDir, "descriptor.json");
60755
60796
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
60756
60797
  continue;
60757
60798
  let lastActivityAt;
@@ -60767,7 +60808,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
60767
60808
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
60768
60809
  continue;
60769
60810
  if (archiveInsteadOfDelete) {
60770
- const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60811
+ const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60771
60812
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
60772
60813
  } else {
60773
60814
  await _scratchPurgeDeps.remove(sessionDir);
@@ -61505,12 +61546,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
61505
61546
 
61506
61547
  // src/pipeline/subscribers/events-writer.ts
61507
61548
  import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
61508
- import { basename as basename13, join as join77 } from "path";
61549
+ import { basename as basename13, join as join76 } from "path";
61509
61550
  function wireEventsWriter(bus, feature, runId, workdir) {
61510
61551
  const logger = getSafeLogger();
61511
61552
  const project = basename13(workdir);
61512
- const eventsDir = join77(getEventsRootDir(), project);
61513
- const eventsFile = join77(eventsDir, "events.jsonl");
61553
+ const eventsDir = join76(getEventsRootDir(), project);
61554
+ const eventsFile = join76(eventsDir, "events.jsonl");
61514
61555
  let dirReady = false;
61515
61556
  const write = (line) => {
61516
61557
  return (async () => {
@@ -61691,12 +61732,12 @@ var init_interaction2 = __esm(() => {
61691
61732
 
61692
61733
  // src/pipeline/subscribers/registry.ts
61693
61734
  import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
61694
- import { basename as basename14, join as join78 } from "path";
61735
+ import { basename as basename14, join as join77 } from "path";
61695
61736
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
61696
61737
  const logger = getSafeLogger();
61697
61738
  const project = basename14(workdir);
61698
- const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
61699
- const metaFile = join78(runDir, "meta.json");
61739
+ const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
61740
+ const metaFile = join77(runDir, "meta.json");
61700
61741
  const unsub = bus.on("run:started", (_ev) => {
61701
61742
  return (async () => {
61702
61743
  try {
@@ -61706,8 +61747,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
61706
61747
  project,
61707
61748
  feature,
61708
61749
  workdir,
61709
- statusPath: join78(outputDir, "features", feature, "status.json"),
61710
- eventsDir: join78(outputDir, "features", feature, "runs"),
61750
+ statusPath: join77(outputDir, "features", feature, "status.json"),
61751
+ eventsDir: join77(outputDir, "features", feature, "runs"),
61711
61752
  registeredAt: new Date().toISOString()
61712
61753
  };
61713
61754
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -61953,7 +61994,7 @@ var init_types9 = __esm(() => {
61953
61994
 
61954
61995
  // src/worktree/dependencies.ts
61955
61996
  import { existsSync as existsSync30 } from "fs";
61956
- import { join as join79 } from "path";
61997
+ import { join as join78 } from "path";
61957
61998
  async function prepareWorktreeDependencies(options) {
61958
61999
  const mode = options.config.execution.worktreeDependencies.mode;
61959
62000
  const resolvedCwd = resolveDependencyCwd(options);
@@ -61967,7 +62008,7 @@ async function prepareWorktreeDependencies(options) {
61967
62008
  }
61968
62009
  }
61969
62010
  function resolveDependencyCwd(options) {
61970
- return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
62011
+ return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
61971
62012
  }
61972
62013
  function resolveInheritedDependencies(options, resolvedCwd) {
61973
62014
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -61977,7 +62018,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
61977
62018
  }
61978
62019
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
61979
62020
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
61980
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
62021
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join78(directory, filename))));
61981
62022
  }
61982
62023
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
61983
62024
  const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
@@ -62041,13 +62082,13 @@ __export(exports_manager, {
62041
62082
  });
62042
62083
  import { existsSync as existsSync31, symlinkSync } from "fs";
62043
62084
  import { mkdir as mkdir15 } from "fs/promises";
62044
- import { join as join80 } from "path";
62085
+ import { join as join79 } from "path";
62045
62086
 
62046
62087
  class WorktreeManager {
62047
62088
  async ensureGitExcludes(projectRoot) {
62048
62089
  const logger = getSafeLogger();
62049
- const infoDir = join80(projectRoot, ".git", "info");
62050
- const excludePath = join80(infoDir, "exclude");
62090
+ const infoDir = join79(projectRoot, ".git", "info");
62091
+ const excludePath = join79(infoDir, "exclude");
62051
62092
  try {
62052
62093
  await mkdir15(infoDir, { recursive: true });
62053
62094
  let existing = "";
@@ -62074,7 +62115,7 @@ ${missing.join(`
62074
62115
  }
62075
62116
  async create(projectRoot, storyId) {
62076
62117
  validateStoryId(storyId);
62077
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62118
+ const worktreePath = join79(projectRoot, ".nax-wt", storyId);
62078
62119
  const branchName = `nax/${storyId}`;
62079
62120
  try {
62080
62121
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -62115,9 +62156,9 @@ ${missing.join(`
62115
62156
  }
62116
62157
  throw new Error(`Failed to create worktree: ${String(error48)}`);
62117
62158
  }
62118
- const envSource = join80(projectRoot, ".env");
62159
+ const envSource = join79(projectRoot, ".env");
62119
62160
  if (existsSync31(envSource)) {
62120
- const envTarget = join80(worktreePath, ".env");
62161
+ const envTarget = join79(worktreePath, ".env");
62121
62162
  try {
62122
62163
  symlinkSync(envSource, envTarget, "file");
62123
62164
  } catch (error48) {
@@ -62128,7 +62169,7 @@ ${missing.join(`
62128
62169
  }
62129
62170
  async remove(projectRoot, storyId) {
62130
62171
  validateStoryId(storyId);
62131
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62172
+ const worktreePath = join79(projectRoot, ".nax-wt", storyId);
62132
62173
  const branchName = `nax/${storyId}`;
62133
62174
  try {
62134
62175
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -62940,10 +62981,10 @@ var init_merge_conflict_rectify = __esm(() => {
62940
62981
  });
62941
62982
 
62942
62983
  // src/execution/pipeline-result-handler.ts
62943
- import { join as join81 } from "path";
62984
+ import { join as join80 } from "path";
62944
62985
  async function removeWorktreeDirectory(projectRoot, storyId) {
62945
62986
  const logger = getSafeLogger();
62946
- const worktreePath = join81(projectRoot, ".nax-wt", storyId);
62987
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62947
62988
  try {
62948
62989
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
62949
62990
  cwd: projectRoot,
@@ -63160,7 +63201,7 @@ var init_pipeline_result_handler = __esm(() => {
63160
63201
 
63161
63202
  // src/execution/iteration-runner.ts
63162
63203
  import { existsSync as existsSync32 } from "fs";
63163
- import { join as join82 } from "path";
63204
+ import { join as join81 } from "path";
63164
63205
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
63165
63206
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
63166
63207
  if (ctx.dryRun) {
@@ -63185,7 +63226,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63185
63226
  const storyStartTime = Date.now();
63186
63227
  let effectiveWorkdir = ctx.workdir;
63187
63228
  if (ctx.config.execution.storyIsolation === "worktree") {
63188
- const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
63229
+ const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
63189
63230
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
63190
63231
  if (!worktreeExists) {
63191
63232
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -63205,7 +63246,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63205
63246
  }
63206
63247
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
63207
63248
  const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
63208
- const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join82(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
63249
+ const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join81(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
63209
63250
  let dependencyContext;
63210
63251
  if (ctx.config.execution.storyIsolation === "worktree") {
63211
63252
  try {
@@ -63232,7 +63273,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63232
63273
  };
63233
63274
  }
63234
63275
  }
63235
- const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join82(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join82(ctx.workdir, story.workdir) : ctx.workdir;
63276
+ const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join81(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join81(ctx.workdir, story.workdir) : ctx.workdir;
63236
63277
  const pipelineContext = {
63237
63278
  config: effectiveConfig,
63238
63279
  rootConfig: ctx.config,
@@ -63434,7 +63475,7 @@ __export(exports_parallel_worker, {
63434
63475
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
63435
63476
  _parallelWorkerDeps: () => _parallelWorkerDeps
63436
63477
  });
63437
- import { join as join83 } from "path";
63478
+ import { join as join82 } from "path";
63438
63479
  function buildWorktreePipelineContext(base, _story) {
63439
63480
  return { ...base, prd: structuredClone(base.prd) };
63440
63481
  }
@@ -63457,7 +63498,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
63457
63498
  story,
63458
63499
  stories: [story],
63459
63500
  projectDir: context.projectDir,
63460
- workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
63501
+ workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
63461
63502
  worktreeDependencyContext: dependencyContext,
63462
63503
  routing,
63463
63504
  storyGitRef: storyGitRef ?? undefined
@@ -64344,7 +64385,7 @@ async function writeStatusFile(filePath, status) {
64344
64385
  var init_status_file = () => {};
64345
64386
 
64346
64387
  // src/execution/status-writer.ts
64347
- import { join as join84 } from "path";
64388
+ import { join as join83 } from "path";
64348
64389
 
64349
64390
  class StatusWriter {
64350
64391
  statusFile;
@@ -64463,7 +64504,7 @@ class StatusWriter {
64463
64504
  if (!this._prd)
64464
64505
  return;
64465
64506
  const safeLogger = getSafeLogger();
64466
- const featureStatusPath = join84(featureDir, "status.json");
64507
+ const featureStatusPath = join83(featureDir, "status.json");
64467
64508
  const write = async () => {
64468
64509
  try {
64469
64510
  const base = this.getSnapshot(totalCost, iterations);
@@ -64495,7 +64536,7 @@ __export(exports_migrate, {
64495
64536
  detectGeneratedContent: () => detectGeneratedContent
64496
64537
  });
64497
64538
  import { existsSync as existsSync33 } from "fs";
64498
- import { mkdir as mkdir16, readdir as readdir6, rename as rename3 } from "fs/promises";
64539
+ import { mkdir as mkdir16, readdir as readdir5, rename as rename3 } from "fs/promises";
64499
64540
  import path22 from "path";
64500
64541
  async function detectGeneratedContent(naxDir) {
64501
64542
  if (!existsSync33(naxDir))
@@ -64503,7 +64544,7 @@ async function detectGeneratedContent(naxDir) {
64503
64544
  const candidates = [];
64504
64545
  let entries = [];
64505
64546
  try {
64506
- entries = await readdir6(naxDir);
64547
+ entries = await readdir5(naxDir);
64507
64548
  } catch {
64508
64549
  return [];
64509
64550
  }
@@ -64516,13 +64557,13 @@ async function detectGeneratedContent(naxDir) {
64516
64557
  if (existsSync33(featuresDir)) {
64517
64558
  let featureDirs = [];
64518
64559
  try {
64519
- featureDirs = await readdir6(featuresDir);
64560
+ featureDirs = await readdir5(featuresDir);
64520
64561
  } catch {}
64521
64562
  for (const fid of featureDirs) {
64522
64563
  const featureDir = path22.join(featuresDir, fid);
64523
64564
  let subEntries = [];
64524
64565
  try {
64525
- subEntries = await readdir6(featureDir);
64566
+ subEntries = await readdir5(featureDir);
64526
64567
  } catch {
64527
64568
  continue;
64528
64569
  }
@@ -64537,7 +64578,7 @@ async function detectGeneratedContent(naxDir) {
64537
64578
  const storiesDir = path22.join(featureDir, "stories");
64538
64579
  let storyDirs = [];
64539
64580
  try {
64540
- storyDirs = await readdir6(storiesDir);
64581
+ storyDirs = await readdir5(storiesDir);
64541
64582
  } catch {
64542
64583
  continue;
64543
64584
  }
@@ -64545,7 +64586,7 @@ async function detectGeneratedContent(naxDir) {
64545
64586
  const storyDir = path22.join(storiesDir, sid);
64546
64587
  let storyEntries = [];
64547
64588
  try {
64548
- storyEntries = await readdir6(storyDir);
64589
+ storyEntries = await readdir5(storyDir);
64549
64590
  } catch {
64550
64591
  continue;
64551
64592
  }
@@ -64897,7 +64938,7 @@ __export(exports_run_initialization, {
64897
64938
  initializeRun: () => initializeRun,
64898
64939
  _reconcileDeps: () => _reconcileDeps
64899
64940
  });
64900
- import { join as join85 } from "path";
64941
+ import { join as join84 } from "path";
64901
64942
  async function reconcileState(prd, prdPath, workdir, config2) {
64902
64943
  const logger = getSafeLogger();
64903
64944
  let reconciledCount = 0;
@@ -64914,7 +64955,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
64914
64955
  });
64915
64956
  continue;
64916
64957
  }
64917
- const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
64958
+ const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
64918
64959
  try {
64919
64960
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
64920
64961
  if (!reviewResult.success) {
@@ -94718,7 +94759,7 @@ __export(exports_curator, {
94718
94759
  });
94719
94760
  import { readdirSync as readdirSync8 } from "fs";
94720
94761
  import { unlink as unlink4 } from "fs/promises";
94721
- import { basename as basename15, join as join87 } from "path";
94762
+ import { basename as basename15, join as join86 } from "path";
94722
94763
  function getProjectKey(config2, projectDir) {
94723
94764
  return config2.name?.trim() || basename15(projectDir);
94724
94765
  }
@@ -94801,7 +94842,7 @@ async function curatorStatus(options) {
94801
94842
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94802
94843
  const projectKey = getProjectKey(config2, resolved.projectDir);
94803
94844
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94804
- const runsDir = join87(outputDir, "runs");
94845
+ const runsDir = join86(outputDir, "runs");
94805
94846
  const runIds = listRunIds(runsDir);
94806
94847
  let runId;
94807
94848
  if (options.run) {
@@ -94818,8 +94859,8 @@ async function curatorStatus(options) {
94818
94859
  runId = runIds[runIds.length - 1];
94819
94860
  }
94820
94861
  console.log(`Run: ${runId}`);
94821
- const runDir = join87(runsDir, runId);
94822
- const observationsPath = join87(runDir, "observations.jsonl");
94862
+ const runDir = join86(runsDir, runId);
94863
+ const observationsPath = join86(runDir, "observations.jsonl");
94823
94864
  const observations = await parseObservations(observationsPath);
94824
94865
  const counts = new Map;
94825
94866
  for (const obs of observations) {
@@ -94829,7 +94870,7 @@ async function curatorStatus(options) {
94829
94870
  for (const [kind, count] of counts.entries()) {
94830
94871
  console.log(` ${kind}: ${count}`);
94831
94872
  }
94832
- const proposalsPath = join87(runDir, "curator-proposals.md");
94873
+ const proposalsPath = join86(runDir, "curator-proposals.md");
94833
94874
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94834
94875
  if (proposalText !== null) {
94835
94876
  console.log("");
@@ -94843,8 +94884,8 @@ async function curatorCommit(options) {
94843
94884
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94844
94885
  const projectKey = getProjectKey(config2, resolved.projectDir);
94845
94886
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94846
- const runDir = join87(outputDir, "runs", options.runId);
94847
- const proposalsPath = join87(runDir, "curator-proposals.md");
94887
+ const runDir = join86(outputDir, "runs", options.runId);
94888
+ const proposalsPath = join86(runDir, "curator-proposals.md");
94848
94889
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94849
94890
  if (proposalText === null) {
94850
94891
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -94860,7 +94901,7 @@ async function curatorCommit(options) {
94860
94901
  const dropFileState = new Map;
94861
94902
  const skippedDrops = new Set;
94862
94903
  for (const drop2 of drops) {
94863
- const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94904
+ const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94864
94905
  if (!dropFileState.has(targetPath)) {
94865
94906
  const fileExists2 = await Bun.file(targetPath).exists();
94866
94907
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -94894,7 +94935,7 @@ async function curatorCommit(options) {
94894
94935
  if (skippedDrops.has(drop2)) {
94895
94936
  continue;
94896
94937
  }
94897
- const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94938
+ const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94898
94939
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
94899
94940
  const filtered = filterDropContent(existing, drop2.description);
94900
94941
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -94903,7 +94944,7 @@ async function curatorCommit(options) {
94903
94944
  }
94904
94945
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
94905
94946
  for (const add2 of adds) {
94906
- const targetPath = join87(resolved.projectDir, add2.canonicalFile);
94947
+ const targetPath = join86(resolved.projectDir, add2.canonicalFile);
94907
94948
  const content = buildAddContent(add2);
94908
94949
  await _curatorCmdDeps.appendFile(targetPath, content);
94909
94950
  modifiedFiles.add(targetPath);
@@ -94940,7 +94981,7 @@ async function curatorDryrun(options) {
94940
94981
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94941
94982
  const projectKey = getProjectKey(config2, resolved.projectDir);
94942
94983
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94943
- const runsDir = join87(outputDir, "runs");
94984
+ const runsDir = join86(outputDir, "runs");
94944
94985
  const runIds = listRunIds(runsDir);
94945
94986
  if (runIds.length === 0) {
94946
94987
  console.log("No runs found.");
@@ -94951,7 +94992,7 @@ async function curatorDryrun(options) {
94951
94992
  console.log(`Run ${options.run} not found in ${runsDir}.`);
94952
94993
  return;
94953
94994
  }
94954
- const observationsPath = join87(runsDir, runId, "observations.jsonl");
94995
+ const observationsPath = join86(runsDir, runId, "observations.jsonl");
94955
94996
  const observations = await parseObservations(observationsPath);
94956
94997
  const thresholds = getThresholds(config2);
94957
94998
  const proposals = runHeuristics(observations, thresholds);
@@ -94992,12 +95033,12 @@ async function curatorGc(options) {
94992
95033
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
94993
95034
  const projectKey = getProjectKey(config2, resolved.projectDir);
94994
95035
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94995
- const perRunsDir = join87(outputDir, "runs");
95036
+ const perRunsDir = join86(outputDir, "runs");
94996
95037
  for (const runId of uniqueRunIds) {
94997
95038
  if (!keepSet.has(runId)) {
94998
- const runDir = join87(perRunsDir, runId);
94999
- await _curatorCmdDeps.removeFile(join87(runDir, "observations.jsonl"));
95000
- await _curatorCmdDeps.removeFile(join87(runDir, "curator-proposals.md"));
95039
+ const runDir = join86(perRunsDir, runId);
95040
+ await _curatorCmdDeps.removeFile(join86(runDir, "observations.jsonl"));
95041
+ await _curatorCmdDeps.removeFile(join86(runDir, "curator-proposals.md"));
95001
95042
  }
95002
95043
  }
95003
95044
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -95042,7 +95083,7 @@ var init_curator2 = __esm(() => {
95042
95083
  init_source();
95043
95084
  import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
95044
95085
  import { homedir as homedir3 } from "os";
95045
- import { basename as basename16, join as join88 } from "path";
95086
+ import { basename as basename16, join as join87 } from "path";
95046
95087
 
95047
95088
  // node_modules/commander/esm.mjs
95048
95089
  var import__ = __toESM(require_commander(), 1);
@@ -95066,12 +95107,12 @@ init_errors();
95066
95107
  init_operations();
95067
95108
 
95068
95109
  // src/plan/strategies/context-builder.ts
95069
- import { join as join39 } from "path";
95110
+ import { join as join38 } from "path";
95070
95111
  init_config();
95071
95112
  init_errors();
95072
95113
  init_interaction();
95073
95114
  async function buildPlanModeContext(workdir, fullConfig, options, deps) {
95074
- const naxDir = join39(workdir, ".nax");
95115
+ const naxDir = join38(workdir, ".nax");
95075
95116
  if (!deps.existsSync(naxDir)) {
95076
95117
  throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
95077
95118
  stage: "plan",
@@ -95079,8 +95120,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
95079
95120
  });
95080
95121
  }
95081
95122
  validateFeatureName(options.feature);
95082
- const outputDir = join39(naxDir, "features", options.feature);
95083
- const outputPath = join39(outputDir, "prd.json");
95123
+ const outputDir = join38(naxDir, "features", options.feature);
95124
+ const outputPath = join38(outputDir, "prd.json");
95084
95125
  const [specContent, sourceRoots, pkg] = await Promise.all([
95085
95126
  deps.readFile(options.from),
95086
95127
  deps.scanSourceRoots(workdir),
@@ -95095,7 +95136,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
95095
95136
  ...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
95096
95137
  ];
95097
95138
  const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
95098
- const packageJson = await deps.readPackageJsonAt(join39(workdir, relativePath, "package.json"));
95139
+ const packageJson = await deps.readPackageJsonAt(join38(workdir, relativePath, "package.json"));
95099
95140
  return buildPackageSummary(relativePath, packageJson);
95100
95141
  }));
95101
95142
  const projectName = detectProjectName(workdir, pkg);
@@ -95698,7 +95739,7 @@ init_interaction();
95698
95739
  init_prd();
95699
95740
  init_runtime();
95700
95741
  import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
95701
- import { basename as basename7, join as join44, resolve as resolve14 } from "path";
95742
+ import { basename as basename7, join as join43, resolve as resolve14 } from "path";
95702
95743
  var _statusFeaturesDeps = {
95703
95744
  projectOutputDir,
95704
95745
  loadConfig
@@ -95712,7 +95753,7 @@ function isPidAlive(pid) {
95712
95753
  }
95713
95754
  }
95714
95755
  async function loadStatusFile(featureDir) {
95715
- const statusPath = join44(featureDir, "status.json");
95756
+ const statusPath = join43(featureDir, "status.json");
95716
95757
  if (!existsSync17(statusPath)) {
95717
95758
  return null;
95718
95759
  }
@@ -95727,7 +95768,7 @@ async function loadProjectStatusFile(projectDir) {
95727
95768
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95728
95769
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95729
95770
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95730
- const statusPath = join44(outputDir, "status.json");
95771
+ const statusPath = join43(outputDir, "status.json");
95731
95772
  if (!existsSync17(statusPath)) {
95732
95773
  return null;
95733
95774
  }
@@ -95739,7 +95780,7 @@ async function loadProjectStatusFile(projectDir) {
95739
95780
  }
95740
95781
  }
95741
95782
  async function getFeatureSummary(featureName, featureDir) {
95742
- const prdPath = join44(featureDir, "prd.json");
95783
+ const prdPath = join43(featureDir, "prd.json");
95743
95784
  if (!existsSync17(prdPath)) {
95744
95785
  return {
95745
95786
  name: featureName,
@@ -95782,7 +95823,7 @@ async function getFeatureSummary(featureName, featureDir) {
95782
95823
  };
95783
95824
  }
95784
95825
  }
95785
- const runsDir = join44(featureDir, "runs");
95826
+ const runsDir = join43(featureDir, "runs");
95786
95827
  if (existsSync17(runsDir)) {
95787
95828
  const runs = readdirSync3(runsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl") && e.name !== "latest.jsonl").map((e) => e.name).sort().reverse();
95788
95829
  if (runs.length > 0) {
@@ -95796,7 +95837,7 @@ async function displayAllFeatures(projectDir) {
95796
95837
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95797
95838
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95798
95839
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95799
- const featuresDir = join44(outputDir, "features");
95840
+ const featuresDir = join43(outputDir, "features");
95800
95841
  if (!existsSync17(featuresDir)) {
95801
95842
  console.log(source_default.dim("No features found."));
95802
95843
  return;
@@ -95837,7 +95878,7 @@ async function displayAllFeatures(projectDir) {
95837
95878
  console.log();
95838
95879
  }
95839
95880
  }
95840
- const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join44(featuresDir, name))));
95881
+ const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join43(featuresDir, name))));
95841
95882
  console.log(source_default.bold(`\uD83D\uDCCA Features
95842
95883
  `));
95843
95884
  const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
@@ -95863,7 +95904,7 @@ async function displayAllFeatures(projectDir) {
95863
95904
  console.log();
95864
95905
  }
95865
95906
  async function displayFeatureDetails(featureName, featureDir) {
95866
- const prdPath = join44(featureDir, "prd.json");
95907
+ const prdPath = join43(featureDir, "prd.json");
95867
95908
  if (!existsSync17(prdPath)) {
95868
95909
  console.log(source_default.bold(`
95869
95910
  \uD83D\uDCCA ${featureName}
@@ -96009,7 +96050,7 @@ async function displayFeatureStatus(options = {}) {
96009
96050
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
96010
96051
  const projectKey = config2?.name?.trim() || basename7(projectDir);
96011
96052
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
96012
- featureDir = join44(outputDir, "features", options.feature);
96053
+ featureDir = join43(outputDir, "features", options.feature);
96013
96054
  } else {
96014
96055
  const resolved = resolveProject({ feature: options.feature });
96015
96056
  if (!resolved.featureDir) {
@@ -96029,7 +96070,7 @@ init_errors();
96029
96070
  init_logger2();
96030
96071
  init_runtime();
96031
96072
  import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
96032
- import { basename as basename8, join as join45 } from "path";
96073
+ import { basename as basename8, join as join44 } from "path";
96033
96074
  async function resolveOutputDir2(workdir, override) {
96034
96075
  if (override)
96035
96076
  return override;
@@ -96053,7 +96094,7 @@ async function runsListCommand(options) {
96053
96094
  const logger = getLogger();
96054
96095
  const { feature, workdir } = options;
96055
96096
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
96056
- const runsDir = join45(outputDir, "features", feature, "runs");
96097
+ const runsDir = join44(outputDir, "features", feature, "runs");
96057
96098
  if (!existsSync18(runsDir)) {
96058
96099
  logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
96059
96100
  return;
@@ -96065,7 +96106,7 @@ async function runsListCommand(options) {
96065
96106
  }
96066
96107
  logger.info("cli", `Runs for ${feature}`, { count: files.length });
96067
96108
  for (const file3 of files.sort().reverse()) {
96068
- const logPath = join45(runsDir, file3);
96109
+ const logPath = join44(runsDir, file3);
96069
96110
  const entries = await parseRunLog(logPath);
96070
96111
  const startEvent = entries.find((e) => e.message === "run.start");
96071
96112
  const completeEvent = entries.find((e) => e.message === "run.complete");
@@ -96092,7 +96133,7 @@ async function runsShowCommand(options) {
96092
96133
  const logger = getLogger();
96093
96134
  const { runId, feature, workdir } = options;
96094
96135
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
96095
- const logPath = join45(outputDir, "features", feature, "runs", `${runId}.jsonl`);
96136
+ const logPath = join44(outputDir, "features", feature, "runs", `${runId}.jsonl`);
96096
96137
  if (!existsSync18(logPath)) {
96097
96138
  logger.error("cli", "Run not found", { runId, feature, logPath });
96098
96139
  throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
@@ -97438,19 +97479,19 @@ async function detectCommand(options) {
97438
97479
  // src/commands/logs.ts
97439
97480
  init_common();
97440
97481
  import { existsSync as existsSync28 } from "fs";
97441
- import { join as join71 } from "path";
97482
+ import { join as join70 } from "path";
97442
97483
 
97443
97484
  // src/commands/logs-formatter.ts
97444
97485
  init_source();
97445
97486
  init_formatter();
97446
97487
  import { readdirSync as readdirSync7 } from "fs";
97447
- import { join as join70 } from "path";
97488
+ import { join as join69 } from "path";
97448
97489
 
97449
97490
  // src/commands/logs-reader.ts
97450
97491
  init_paths3();
97451
97492
  import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
97452
- import { readdir as readdir4 } from "fs/promises";
97453
- import { join as join69 } from "path";
97493
+ import { readdir as readdir3 } from "fs/promises";
97494
+ import { join as join68 } from "path";
97454
97495
  var _logsReaderDeps = {
97455
97496
  getRunsDir
97456
97497
  };
@@ -97458,13 +97499,13 @@ async function resolveRunFileFromRegistry(runId) {
97458
97499
  const runsDir = _logsReaderDeps.getRunsDir();
97459
97500
  let entries;
97460
97501
  try {
97461
- entries = await readdir4(runsDir);
97502
+ entries = await readdir3(runsDir);
97462
97503
  } catch {
97463
97504
  throw new Error(`Run not found in registry: ${runId}`);
97464
97505
  }
97465
97506
  let matched = null;
97466
97507
  for (const entry of entries) {
97467
- const metaPath = join69(runsDir, entry, "meta.json");
97508
+ const metaPath = join68(runsDir, entry, "meta.json");
97468
97509
  try {
97469
97510
  const meta3 = await Bun.file(metaPath).json();
97470
97511
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -97486,14 +97527,14 @@ async function resolveRunFileFromRegistry(runId) {
97486
97527
  return null;
97487
97528
  }
97488
97529
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
97489
- return join69(matched.eventsDir, specificFile ?? files[0]);
97530
+ return join68(matched.eventsDir, specificFile ?? files[0]);
97490
97531
  }
97491
97532
  async function selectRunFile(runsDir) {
97492
97533
  const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97493
97534
  if (files.length === 0) {
97494
97535
  return null;
97495
97536
  }
97496
- return join69(runsDir, files[0]);
97537
+ return join68(runsDir, files[0]);
97497
97538
  }
97498
97539
  async function extractRunSummary(filePath) {
97499
97540
  const file3 = Bun.file(filePath);
@@ -97579,7 +97620,7 @@ Runs:
97579
97620
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
97580
97621
  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"));
97581
97622
  for (const file3 of files) {
97582
- const filePath = join70(runsDir, file3);
97623
+ const filePath = join69(runsDir, file3);
97583
97624
  const summary = await extractRunSummary(filePath);
97584
97625
  const timestamp = file3.replace(".jsonl", "");
97585
97626
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -97693,7 +97734,7 @@ async function logsCommand(options) {
97693
97734
  return;
97694
97735
  }
97695
97736
  const resolved = resolveProject({ dir: options.dir });
97696
- const naxDir = join71(resolved.projectDir, ".nax");
97737
+ const naxDir = join70(resolved.projectDir, ".nax");
97697
97738
  const configPath = resolved.configPath;
97698
97739
  const configFile = Bun.file(configPath);
97699
97740
  const config2 = await configFile.json();
@@ -97701,8 +97742,8 @@ async function logsCommand(options) {
97701
97742
  if (!featureName) {
97702
97743
  throw new Error("No feature specified in config.json");
97703
97744
  }
97704
- const featureDir = join71(naxDir, "features", featureName);
97705
- const runsDir = join71(featureDir, "runs");
97745
+ const featureDir = join70(naxDir, "features", featureName);
97746
+ const runsDir = join70(featureDir, "runs");
97706
97747
  if (!existsSync28(runsDir)) {
97707
97748
  throw new Error(`No runs directory found for feature: ${featureName}`);
97708
97749
  }
@@ -97728,7 +97769,7 @@ init_prd();
97728
97769
  init_precheck();
97729
97770
  init_common();
97730
97771
  import { existsSync as existsSync29 } from "fs";
97731
- import { join as join72 } from "path";
97772
+ import { join as join71 } from "path";
97732
97773
  async function precheckCommand(options) {
97733
97774
  const resolved = resolveProject({
97734
97775
  dir: options.dir,
@@ -97750,9 +97791,9 @@ async function precheckCommand(options) {
97750
97791
  process.exit(1);
97751
97792
  }
97752
97793
  }
97753
- const naxDir = join72(resolved.projectDir, ".nax");
97754
- const featureDir = join72(naxDir, "features", featureName);
97755
- const prdPath = join72(featureDir, "prd.json");
97794
+ const naxDir = join71(resolved.projectDir, ".nax");
97795
+ const featureDir = join71(naxDir, "features", featureName);
97796
+ const prdPath = join71(featureDir, "prd.json");
97756
97797
  if (!existsSync29(featureDir)) {
97757
97798
  console.error(source_default.red(`Feature not found: ${featureName}`));
97758
97799
  process.exit(1);
@@ -97774,8 +97815,8 @@ async function precheckCommand(options) {
97774
97815
  // src/commands/runs.ts
97775
97816
  init_source();
97776
97817
  init_paths3();
97777
- import { readdir as readdir5 } from "fs/promises";
97778
- import { join as join73 } from "path";
97818
+ import { readdir as readdir4 } from "fs/promises";
97819
+ import { join as join72 } from "path";
97779
97820
  var DEFAULT_LIMIT = 20;
97780
97821
  var _runsCmdDeps = {
97781
97822
  getRunsDir
@@ -97823,14 +97864,14 @@ async function runsCommand(options = {}) {
97823
97864
  const runsDir = _runsCmdDeps.getRunsDir();
97824
97865
  let entries;
97825
97866
  try {
97826
- entries = await readdir5(runsDir);
97867
+ entries = await readdir4(runsDir);
97827
97868
  } catch {
97828
97869
  console.log("No runs found.");
97829
97870
  return;
97830
97871
  }
97831
97872
  const rows = [];
97832
97873
  for (const entry of entries) {
97833
- const metaPath = join73(runsDir, entry, "meta.json");
97874
+ const metaPath = join72(runsDir, entry, "meta.json");
97834
97875
  let meta3;
97835
97876
  try {
97836
97877
  meta3 = await Bun.file(metaPath).json();
@@ -97907,7 +97948,7 @@ async function runsCommand(options = {}) {
97907
97948
 
97908
97949
  // src/commands/unlock.ts
97909
97950
  init_source();
97910
- import { join as join74 } from "path";
97951
+ import { join as join73 } from "path";
97911
97952
  function isProcessAlive2(pid) {
97912
97953
  try {
97913
97954
  process.kill(pid, 0);
@@ -97922,7 +97963,7 @@ function formatLockAge(ageMs) {
97922
97963
  }
97923
97964
  async function unlockCommand(options) {
97924
97965
  const workdir = options.dir ?? process.cwd();
97925
- const lockPath = join74(workdir, "nax.lock");
97966
+ const lockPath = join73(workdir, "nax.lock");
97926
97967
  const lockFile = Bun.file(lockPath);
97927
97968
  const exists = await lockFile.exists();
97928
97969
  if (!exists) {
@@ -98009,7 +98050,7 @@ async function runCompletionPhase(options) {
98009
98050
  } else if (options.config.acceptance.enabled && isComplete(options.prd)) {
98010
98051
  options.statusWriter.setPostRunPhase("acceptance", { status: "running" });
98011
98052
  pipelineEventBus.emit({ type: "postrun:phase:started", phase: "acceptance" });
98012
- const acceptanceTestPaths = options.featureDir ? await Promise.all(groupStoriesByPackage(options.prd, options.workdir, options.feature, options.config.acceptance.testPath, options.config.project?.language).map(async (g) => {
98053
+ const acceptanceTestPaths = options.featureDir ? await Promise.all((await groupStoriesByPackage(options.prd, options.workdir, options.feature, options.config.acceptance.testPath, options.config.project?.language)).map(async (g) => {
98013
98054
  const relativeWorkdir = path19.relative(options.workdir, g.packageDir);
98014
98055
  let groupConfig = options.config;
98015
98056
  if (relativeWorkdir && relativeWorkdir !== ".") {
@@ -106347,7 +106388,7 @@ Next: nax generate --package ${options.package}`));
106347
106388
  }
106348
106389
  return;
106349
106390
  }
106350
- const naxDir = join88(workdir, ".nax");
106391
+ const naxDir = join87(workdir, ".nax");
106351
106392
  if (existsSync35(naxDir) && !options.force) {
106352
106393
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
106353
106394
  return;
@@ -106376,11 +106417,11 @@ Next: nax generate --package ${options.package}`));
106376
106417
  }
106377
106418
  }
106378
106419
  }
106379
- mkdirSync7(join88(naxDir, "features"), { recursive: true });
106380
- mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
106420
+ mkdirSync7(join87(naxDir, "features"), { recursive: true });
106421
+ mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
106381
106422
  const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
106382
- await Bun.write(join88(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106383
- await Bun.write(join88(naxDir, "hooks.json"), JSON.stringify({
106423
+ await Bun.write(join87(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106424
+ await Bun.write(join87(naxDir, "hooks.json"), JSON.stringify({
106384
106425
  hooks: {
106385
106426
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
106386
106427
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -106388,12 +106429,12 @@ Next: nax generate --package ${options.package}`));
106388
106429
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
106389
106430
  }
106390
106431
  }, null, 2));
106391
- await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
106432
+ await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
106392
106433
  *.tmp
106393
106434
  .paused.json
106394
106435
  .nax-verifier-verdict.json
106395
106436
  `);
106396
- await Bun.write(join88(naxDir, "context.md"), `# Project Context
106437
+ await Bun.write(join87(naxDir, "context.md"), `# Project Context
106397
106438
 
106398
106439
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
106399
106440
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -106541,8 +106582,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
106541
106582
  console.error(source_default.red("nax not initialized. Run: nax init"));
106542
106583
  process.exit(1);
106543
106584
  }
106544
- const featureDir = join88(naxDir, "features", options.feature);
106545
- const prdPath = join88(featureDir, "prd.json");
106585
+ const featureDir = join87(naxDir, "features", options.feature);
106586
+ const prdPath = join87(featureDir, "prd.json");
106546
106587
  if (options.plan && options.from) {
106547
106588
  if (existsSync35(prdPath) && !options.force) {
106548
106589
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
@@ -106564,10 +106605,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106564
106605
  }
106565
106606
  }
106566
106607
  try {
106567
- const planLogDir = join88(featureDir, "plan");
106608
+ const planLogDir = join87(featureDir, "plan");
106568
106609
  mkdirSync7(planLogDir, { recursive: true });
106569
106610
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106570
- const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
106611
+ const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
106571
106612
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106572
106613
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106573
106614
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -106613,10 +106654,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106613
106654
  resetLogger();
106614
106655
  const projectKey = config2.name?.trim() || basename16(workdir);
106615
106656
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
106616
- const runsDir = join88(outputDir, "features", options.feature, "runs");
106657
+ const runsDir = join87(outputDir, "features", options.feature, "runs");
106617
106658
  mkdirSync7(runsDir, { recursive: true });
106618
106659
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106619
- const logFilePath = join88(runsDir, `${runId}.jsonl`);
106660
+ const logFilePath = join87(runsDir, `${runId}.jsonl`);
106620
106661
  const isTTY = process.stdout.isTTY ?? false;
106621
106662
  const headlessFlag = options.headless ?? false;
106622
106663
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -106634,7 +106675,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106634
106675
  config2.agent.default = options.agent;
106635
106676
  }
106636
106677
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
106637
- const globalNaxDir = join88(homedir3(), ".nax");
106678
+ const globalNaxDir = join87(homedir3(), ".nax");
106638
106679
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
106639
106680
  const eventEmitter = new PipelineEventEmitter;
106640
106681
  const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
@@ -106654,12 +106695,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
106654
106695
  events: eventEmitter,
106655
106696
  ptyOptions: null,
106656
106697
  agentStreamEvents,
106657
- queueFilePath: join88(workdir, ".queue.txt")
106698
+ queueFilePath: join87(workdir, ".queue.txt")
106658
106699
  });
106659
106700
  } else {
106660
106701
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
106661
106702
  }
106662
- const statusFilePath = join88(outputDir, "status.json");
106703
+ const statusFilePath = join87(outputDir, "status.json");
106663
106704
  let parallel;
106664
106705
  if (options.parallel !== undefined) {
106665
106706
  parallel = Number.parseInt(options.parallel, 10);
@@ -106686,7 +106727,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106686
106727
  skipPrecheck: options.skipPrecheck ?? false,
106687
106728
  agentStreamEvents
106688
106729
  });
106689
- const latestSymlink = join88(runsDir, "latest.jsonl");
106730
+ const latestSymlink = join87(runsDir, "latest.jsonl");
106690
106731
  try {
106691
106732
  if (existsSync35(latestSymlink)) {
106692
106733
  Bun.spawnSync(["rm", latestSymlink]);
@@ -106747,9 +106788,9 @@ features.command("create <name>").description("Create a new feature").option("-d
106747
106788
  console.error(source_default.red("nax not initialized. Run: nax init"));
106748
106789
  process.exit(1);
106749
106790
  }
106750
- const featureDir = join88(naxDir, "features", name);
106791
+ const featureDir = join87(naxDir, "features", name);
106751
106792
  mkdirSync7(featureDir, { recursive: true });
106752
- await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
106793
+ await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
106753
106794
 
106754
106795
  ## Overview
106755
106796
 
@@ -106782,7 +106823,7 @@ features.command("create <name>").description("Create a new feature").option("-d
106782
106823
 
106783
106824
  <!-- What this feature explicitly does NOT cover. -->
106784
106825
  `);
106785
- await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
106826
+ await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
106786
106827
 
106787
106828
  Created: ${new Date().toISOString()}
106788
106829
 
@@ -106808,7 +106849,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106808
106849
  console.error(source_default.red("nax not initialized."));
106809
106850
  process.exit(1);
106810
106851
  }
106811
- const featuresDir = join88(naxDir, "features");
106852
+ const featuresDir = join87(naxDir, "features");
106812
106853
  if (!existsSync35(featuresDir)) {
106813
106854
  console.log(source_default.dim("No features yet."));
106814
106855
  return;
@@ -106823,7 +106864,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106823
106864
  Features:
106824
106865
  `));
106825
106866
  for (const name of entries) {
106826
- const prdPath = join88(featuresDir, name, "prd.json");
106867
+ const prdPath = join87(featuresDir, name, "prd.json");
106827
106868
  if (existsSync35(prdPath)) {
106828
106869
  const prd = await loadPRD(prdPath);
106829
106870
  const c = countStories(prd);
@@ -106858,10 +106899,10 @@ Use: nax plan -f <feature> --from <spec>`));
106858
106899
  cliOverrides.profile = options.profile;
106859
106900
  }
106860
106901
  const config2 = await loadConfig(workdir, cliOverrides);
106861
- const featureLogDir = join88(naxDir, "features", options.feature, "plan");
106902
+ const featureLogDir = join87(naxDir, "features", options.feature, "plan");
106862
106903
  mkdirSync7(featureLogDir, { recursive: true });
106863
106904
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106864
- const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
106905
+ const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
106865
106906
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106866
106907
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106867
106908
  try {