@nathapp/nax 0.69.7 → 0.69.8

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 +309 -275
  3. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -37430,7 +37430,7 @@ var init_write_test = __esm(() => {
37430
37430
  if (!input.beforeRef)
37431
37431
  return parsed;
37432
37432
  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;
37433
+ const testFilePatterns = input.resolvedTestPatterns?.globs;
37434
37434
  const isolation = await verifyTestWriterIsolation(ctx.packageView.packageDir, input.beforeRef, allowedPaths, testFilePatterns, input.lite ? "lite" : "strict");
37435
37435
  return { ...parsed, isolation };
37436
37436
  }
@@ -38414,57 +38414,69 @@ var init_plan_critic_llm = __esm(() => {
38414
38414
  });
38415
38415
 
38416
38416
  // 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"]);
38417
+ async function gitLsFiles2(workdir) {
38422
38418
  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
- }
38419
+ const proc = _greenfieldDeps.spawn(["git", "ls-files"], {
38420
+ cwd: workdir,
38421
+ stdout: "pipe",
38422
+ stderr: "pipe"
38423
+ });
38424
+ const exitCode = await proc.exited;
38425
+ if (exitCode !== 0)
38426
+ return null;
38427
+ const output = await new Response(proc.stdout).text();
38428
+ return output.split(`
38429
+ `).filter(Boolean);
38430
+ } catch {
38431
+ return null;
38432
+ }
38433
+ }
38434
+ async function hasTestFiles(workdir, patterns) {
38435
+ const files = await gitLsFiles2(workdir);
38436
+ if (files !== null) {
38437
+ return files.some((f) => isTestFileByPatterns(f, patterns));
38438
+ }
38439
+ for (const pattern of patterns) {
38440
+ const g = new Bun.Glob(pattern);
38441
+ for await (const path5 of g.scan({ cwd: workdir, onlyFiles: true })) {
38442
+ if (!path5.split("/").some((seg) => IGNORE_DIRS.has(seg))) {
38443
+ return true;
38435
38444
  }
38436
38445
  }
38437
- } catch (error48) {
38438
- if (isRootCall) {
38439
- throw error48;
38440
- }
38441
38446
  }
38442
- return results;
38447
+ return false;
38443
38448
  }
38444
38449
  async function isGreenfieldStory(_story, workdir, patterns) {
38445
38450
  try {
38446
- const regexes = globsToTestRegex(patterns ?? GREENFIELD_FALLBACK_PATTERNS);
38447
- const testFiles = await scanForTestFiles(workdir, regexes);
38448
- return testFiles.length === 0;
38449
- } catch (error48) {
38451
+ return !await hasTestFiles(workdir, patterns ?? DEFAULT_TEST_FILE_PATTERNS);
38452
+ } catch {
38450
38453
  return false;
38451
38454
  }
38452
38455
  }
38453
- var GREENFIELD_FALLBACK_PATTERNS;
38456
+ var _greenfieldDeps, IGNORE_DIRS;
38454
38457
  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"
38458
+ init_test_runners();
38459
+ _greenfieldDeps = {
38460
+ spawn: Bun.spawn
38461
+ };
38462
+ IGNORE_DIRS = new Set([
38463
+ "node_modules",
38464
+ "dist",
38465
+ ".next",
38466
+ ".nuxt",
38467
+ ".cache",
38468
+ "coverage",
38469
+ "vendor",
38470
+ "__pycache__",
38471
+ ".venv",
38472
+ "venv",
38473
+ ".eggs",
38474
+ "target",
38475
+ ".gradle",
38476
+ "out",
38477
+ "tmp",
38478
+ "temp",
38479
+ ".git"
38468
38480
  ]);
38469
38481
  });
38470
38482
 
@@ -38627,13 +38639,13 @@ __export(exports_runners, {
38627
38639
  _regressionRunnerDeps: () => _regressionRunnerDeps
38628
38640
  });
38629
38641
  import { existsSync as existsSync6 } from "fs";
38630
- import { join as join24 } from "path";
38642
+ import { join as join23 } from "path";
38631
38643
  async function verifyAssets(workingDirectory, expectedFiles) {
38632
38644
  if (!expectedFiles || expectedFiles.length === 0)
38633
38645
  return { success: true, missingFiles: [] };
38634
38646
  const missingFiles = [];
38635
38647
  for (const file3 of expectedFiles) {
38636
- if (!existsSync6(join24(workingDirectory, file3)))
38648
+ if (!existsSync6(join23(workingDirectory, file3)))
38637
38649
  missingFiles.push(file3);
38638
38650
  }
38639
38651
  if (missingFiles.length > 0) {
@@ -39080,7 +39092,7 @@ var init_apply_test_edit_declarations = __esm(() => {
39080
39092
  });
39081
39093
 
39082
39094
  // src/operations/validate-mock-structure-files.ts
39083
- import { join as join25 } from "path";
39095
+ import { join as join24 } from "path";
39084
39096
  async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
39085
39097
  const fileExists = deps?.fileExists ?? defaultFileExists;
39086
39098
  const valid = [];
@@ -39093,7 +39105,7 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
39093
39105
  const files = d.files ?? [d.file];
39094
39106
  let allValid = true;
39095
39107
  for (const file3 of files) {
39096
- const absolutePath = join25(packageDir, file3);
39108
+ const absolutePath = join24(packageDir, file3);
39097
39109
  const exists = await fileExists(absolutePath);
39098
39110
  if (!exists) {
39099
39111
  allValid = false;
@@ -40693,7 +40705,7 @@ var init_lint_parsing = __esm(() => {
40693
40705
  });
40694
40706
 
40695
40707
  // src/review/scoped-lint.ts
40696
- import { join as join26, relative as relative10 } from "path";
40708
+ import { join as join25, relative as relative10 } from "path";
40697
40709
  function shellQuotePath4(path5) {
40698
40710
  return `'${path5.replaceAll("'", "'\\''")}'`;
40699
40711
  }
@@ -40741,7 +40753,7 @@ function uniqueFiles(files) {
40741
40753
  async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
40742
40754
  const inScope = [];
40743
40755
  for (const relPath of files) {
40744
- const absPath = join26(workdir, relPath);
40756
+ const absPath = join25(workdir, relPath);
40745
40757
  const exists = await _scopedLintDeps.fileExists(absPath);
40746
40758
  if (!exists)
40747
40759
  continue;
@@ -44115,7 +44127,7 @@ var init_call = __esm(() => {
44115
44127
 
44116
44128
  // src/runtime/cost-aggregator.ts
44117
44129
  import { mkdirSync as mkdirSync2 } from "fs";
44118
- import { join as join27 } from "path";
44130
+ import { join as join26 } from "path";
44119
44131
  function makeCorrelationId() {
44120
44132
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
44121
44133
  }
@@ -44306,7 +44318,7 @@ class CostAggregator {
44306
44318
  if (events.length === 0 && errors3.length === 0)
44307
44319
  return;
44308
44320
  mkdirSync2(this._drainDir, { recursive: true });
44309
- const path5 = join27(this._drainDir, `${this._runId}.jsonl`);
44321
+ const path5 = join26(this._drainDir, `${this._runId}.jsonl`);
44310
44322
  const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
44311
44323
  await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
44312
44324
  `)}
@@ -44346,7 +44358,7 @@ var init_cost_aggregator = __esm(() => {
44346
44358
  // src/runtime/prompt-auditor.ts
44347
44359
  import { appendFileSync } from "fs";
44348
44360
  import { mkdir as mkdir4 } from "fs/promises";
44349
- import { join as join28 } from "path";
44361
+ import { join as join27 } from "path";
44350
44362
  function createNoOpPromptAuditor() {
44351
44363
  return {
44352
44364
  record() {},
@@ -44412,8 +44424,8 @@ class PromptAuditor {
44412
44424
  _jsonlPath;
44413
44425
  _featureDir;
44414
44426
  constructor(runId, flushDir, featureName) {
44415
- this._featureDir = join28(flushDir, featureName);
44416
- this._jsonlPath = join28(this._featureDir, `${runId}.jsonl`);
44427
+ this._featureDir = join27(flushDir, featureName);
44428
+ this._jsonlPath = join27(this._featureDir, `${runId}.jsonl`);
44417
44429
  }
44418
44430
  record(entry) {
44419
44431
  this._enqueue(entry);
@@ -44462,7 +44474,7 @@ class PromptAuditor {
44462
44474
  const auditEntry = entry;
44463
44475
  const filename = deriveTxtFilename(auditEntry);
44464
44476
  try {
44465
- await _promptAuditorDeps.write(join28(this._featureDir, filename), buildTxtContent(auditEntry));
44477
+ await _promptAuditorDeps.write(join27(this._featureDir, filename), buildTxtContent(auditEntry));
44466
44478
  } catch (err) {
44467
44479
  throw tagAuditError(err, "txt");
44468
44480
  }
@@ -44600,6 +44612,7 @@ function createPackageView(config2, packageDir, repoRoot, hasOverride) {
44600
44612
  function createPackageRegistry(loader, repoRoot) {
44601
44613
  const cache = new Map;
44602
44614
  const mergedConfigs = new Map;
44615
+ let hydrated = false;
44603
44616
  function toRelativeKey(packageDir) {
44604
44617
  if (!packageDir)
44605
44618
  return "";
@@ -44618,6 +44631,9 @@ function createPackageRegistry(loader, repoRoot) {
44618
44631
  }
44619
44632
  const overrideConfig = mergedConfigs.get(key);
44620
44633
  const hasOverride = overrideConfig !== undefined;
44634
+ if (!hasOverride && key && !hydrated) {
44635
+ _packagesDeps.getSafeLogger()?.warn("packages", "resolve() called for non-root package before hydrate(); returning root config (per-package overrides not applied)", { packageDir: key });
44636
+ }
44621
44637
  const config2 = overrideConfig ?? loader.current();
44622
44638
  const view = createPackageView(config2, key, repoRoot, hasOverride);
44623
44639
  cache.set(key, view);
@@ -44638,6 +44654,7 @@ function createPackageRegistry(loader, repoRoot) {
44638
44654
  cache.delete(dir);
44639
44655
  }
44640
44656
  }
44657
+ hydrated = true;
44641
44658
  }
44642
44659
  return {
44643
44660
  all() {
@@ -44650,8 +44667,11 @@ function createPackageRegistry(loader, repoRoot) {
44650
44667
  hydrate
44651
44668
  };
44652
44669
  }
44670
+ var _packagesDeps;
44653
44671
  var init_packages = __esm(() => {
44654
44672
  init_config();
44673
+ init_logger2();
44674
+ _packagesDeps = { getSafeLogger };
44655
44675
  });
44656
44676
 
44657
44677
  // src/runtime/agent-stream-events.ts
@@ -45610,7 +45630,7 @@ var init_pid_registry = __esm(() => {
45610
45630
  // src/session/manager-deps.ts
45611
45631
  import { randomUUID as randomUUID3 } from "crypto";
45612
45632
  import { mkdir as mkdir5 } from "fs/promises";
45613
- import { isAbsolute as isAbsolute9, join as join29, relative as relative11, sep as sep2 } from "path";
45633
+ import { isAbsolute as isAbsolute9, join as join28, relative as relative11, sep as sep2 } from "path";
45614
45634
  function resolveProjectDirFromScratchDir(scratchDir) {
45615
45635
  const marker = `${sep2}.nax${sep2}features${sep2}`;
45616
45636
  const markerIdx = scratchDir.lastIndexOf(marker);
@@ -45631,7 +45651,7 @@ var init_manager_deps = __esm(() => {
45631
45651
  now: () => new Date().toISOString(),
45632
45652
  nowMs: () => Date.now(),
45633
45653
  uuid: () => randomUUID3(),
45634
- sessionScratchDir: (projectDir, featureName, sessionId) => join29(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45654
+ sessionScratchDir: (projectDir, featureName, sessionId) => join28(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45635
45655
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
45636
45656
  await mkdir5(scratchDir, { recursive: true });
45637
45657
  const { handle: _handle, ...persistable } = descriptor;
@@ -45642,7 +45662,7 @@ var init_manager_deps = __esm(() => {
45642
45662
  persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
45643
45663
  }
45644
45664
  }
45645
- await Bun.write(join29(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45665
+ await Bun.write(join28(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45646
45666
  }
45647
45667
  };
45648
45668
  });
@@ -46393,7 +46413,7 @@ __export(exports_runtime, {
46393
46413
  CostAggregator: () => CostAggregator,
46394
46414
  AgentStreamEventBus: () => AgentStreamEventBus
46395
46415
  });
46396
- import { basename as basename5, join as join30 } from "path";
46416
+ import { basename as basename5, join as join29 } from "path";
46397
46417
  function createRuntime(config2, workdir, opts) {
46398
46418
  const runId = crypto.randomUUID();
46399
46419
  const controller = new AbortController;
@@ -46409,10 +46429,10 @@ function createRuntime(config2, workdir, opts) {
46409
46429
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
46410
46430
  const globalDir = globalOutputDir();
46411
46431
  const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
46412
- const costDir = join30(outputDir, "cost");
46432
+ const costDir = join29(outputDir, "cost");
46413
46433
  const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
46414
46434
  const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
46415
- const auditDir = config2.agent?.promptAudit?.dir ?? join30(outputDir, "prompt-audit");
46435
+ const auditDir = config2.agent?.promptAudit?.dir ?? join29(outputDir, "prompt-audit");
46416
46436
  let promptAuditor;
46417
46437
  if (opts?.promptAuditor) {
46418
46438
  promptAuditor = opts.promptAuditor;
@@ -46565,9 +46585,9 @@ async function allSettledBounded(tasks, limit) {
46565
46585
 
46566
46586
  // src/context/injector.ts
46567
46587
  import { existsSync as existsSync8 } from "fs";
46568
- import { join as join31 } from "path";
46588
+ import { join as join30 } from "path";
46569
46589
  async function detectNode(workdir) {
46570
- const pkgPath = join31(workdir, "package.json");
46590
+ const pkgPath = join30(workdir, "package.json");
46571
46591
  if (!existsSync8(pkgPath))
46572
46592
  return null;
46573
46593
  try {
@@ -46584,7 +46604,7 @@ async function detectNode(workdir) {
46584
46604
  }
46585
46605
  }
46586
46606
  async function detectGo(workdir) {
46587
- const goMod = join31(workdir, "go.mod");
46607
+ const goMod = join30(workdir, "go.mod");
46588
46608
  if (!existsSync8(goMod))
46589
46609
  return null;
46590
46610
  try {
@@ -46608,7 +46628,7 @@ async function detectGo(workdir) {
46608
46628
  }
46609
46629
  }
46610
46630
  async function detectRust(workdir) {
46611
- const cargoPath = join31(workdir, "Cargo.toml");
46631
+ const cargoPath = join30(workdir, "Cargo.toml");
46612
46632
  if (!existsSync8(cargoPath))
46613
46633
  return null;
46614
46634
  try {
@@ -46624,8 +46644,8 @@ async function detectRust(workdir) {
46624
46644
  }
46625
46645
  }
46626
46646
  async function detectPython(workdir) {
46627
- const pyproject = join31(workdir, "pyproject.toml");
46628
- const requirements = join31(workdir, "requirements.txt");
46647
+ const pyproject = join30(workdir, "pyproject.toml");
46648
+ const requirements = join30(workdir, "requirements.txt");
46629
46649
  if (!existsSync8(pyproject) && !existsSync8(requirements))
46630
46650
  return null;
46631
46651
  try {
@@ -46644,7 +46664,7 @@ async function detectPython(workdir) {
46644
46664
  }
46645
46665
  }
46646
46666
  async function detectPhp(workdir) {
46647
- const composerPath = join31(workdir, "composer.json");
46667
+ const composerPath = join30(workdir, "composer.json");
46648
46668
  if (!existsSync8(composerPath))
46649
46669
  return null;
46650
46670
  try {
@@ -46657,7 +46677,7 @@ async function detectPhp(workdir) {
46657
46677
  }
46658
46678
  }
46659
46679
  async function detectRuby(workdir) {
46660
- const gemfile = join31(workdir, "Gemfile");
46680
+ const gemfile = join30(workdir, "Gemfile");
46661
46681
  if (!existsSync8(gemfile))
46662
46682
  return null;
46663
46683
  try {
@@ -46669,9 +46689,9 @@ async function detectRuby(workdir) {
46669
46689
  }
46670
46690
  }
46671
46691
  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");
46692
+ const pom = join30(workdir, "pom.xml");
46693
+ const gradle = join30(workdir, "build.gradle");
46694
+ const gradleKts = join30(workdir, "build.gradle.kts");
46675
46695
  if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
46676
46696
  return null;
46677
46697
  try {
@@ -46679,7 +46699,7 @@ async function detectJvm(workdir) {
46679
46699
  const content2 = await Bun.file(pom).text();
46680
46700
  const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
46681
46701
  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";
46702
+ const lang2 = existsSync8(join30(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46683
46703
  return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
46684
46704
  }
46685
46705
  const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
@@ -46933,7 +46953,7 @@ var init_windsurf = __esm(() => {
46933
46953
 
46934
46954
  // src/context/generator.ts
46935
46955
  import { existsSync as existsSync9 } from "fs";
46936
- import { join as join32, relative as relative12 } from "path";
46956
+ import { join as join31, relative as relative12 } from "path";
46937
46957
  async function loadContextContent(options, config2) {
46938
46958
  if (!_generatorDeps.existsSync(options.contextPath)) {
46939
46959
  throw new Error(`Context file not found: ${options.contextPath}`);
@@ -46951,7 +46971,7 @@ async function generateFor(agent, options, config2) {
46951
46971
  try {
46952
46972
  const context = await loadContextContent(options, config2);
46953
46973
  const content = generator.generate(context);
46954
- const outputPath = join32(options.outputDir, generator.outputFile);
46974
+ const outputPath = join31(options.outputDir, generator.outputFile);
46955
46975
  validateFilePath(outputPath, options.outputDir);
46956
46976
  if (!options.dryRun) {
46957
46977
  await _generatorDeps.writeFile(outputPath, content);
@@ -46969,7 +46989,7 @@ async function generateAll(options, config2, agentFilter) {
46969
46989
  for (const [agentKey, generator] of entries) {
46970
46990
  try {
46971
46991
  const content = generator.generate(context);
46972
- const outputPath = join32(options.outputDir, generator.outputFile);
46992
+ const outputPath = join31(options.outputDir, generator.outputFile);
46973
46993
  validateFilePath(outputPath, options.outputDir);
46974
46994
  if (!options.dryRun) {
46975
46995
  await _generatorDeps.writeFile(outputPath, content);
@@ -46989,7 +47009,7 @@ async function discoverPackages(repoRoot) {
46989
47009
  const glob = new Bun.Glob(pattern);
46990
47010
  for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
46991
47011
  const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
46992
- const pkgAbsolute = join32(repoRoot, pkgRelative);
47012
+ const pkgAbsolute = join31(repoRoot, pkgRelative);
46993
47013
  if (!seen.has(pkgAbsolute)) {
46994
47014
  seen.add(pkgAbsolute);
46995
47015
  packages.push(pkgAbsolute);
@@ -47021,14 +47041,14 @@ async function discoverWorkspacePackages2(repoRoot) {
47021
47041
  }
47022
47042
  }
47023
47043
  }
47024
- const turboPath = join32(repoRoot, "turbo.json");
47044
+ const turboPath = join31(repoRoot, "turbo.json");
47025
47045
  try {
47026
47046
  const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
47027
47047
  if (Array.isArray(turbo.packages)) {
47028
47048
  await resolveGlobs(turbo.packages);
47029
47049
  }
47030
47050
  } catch {}
47031
- const pkgPath = join32(repoRoot, "package.json");
47051
+ const pkgPath = join31(repoRoot, "package.json");
47032
47052
  try {
47033
47053
  const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
47034
47054
  const ws = pkg.workspaces;
@@ -47036,7 +47056,7 @@ async function discoverWorkspacePackages2(repoRoot) {
47036
47056
  if (patterns.length > 0)
47037
47057
  await resolveGlobs(patterns);
47038
47058
  } catch {}
47039
- const pnpmPath = join32(repoRoot, "pnpm-workspace.yaml");
47059
+ const pnpmPath = join31(repoRoot, "pnpm-workspace.yaml");
47040
47060
  try {
47041
47061
  const raw = await _generatorDeps.readTextFile(pnpmPath);
47042
47062
  const lines = raw.split(`
@@ -47062,7 +47082,7 @@ async function discoverWorkspacePackages2(repoRoot) {
47062
47082
  async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
47063
47083
  const resolvedRepoRoot = repoRoot ?? packageDir;
47064
47084
  const relativePkgPath = relative12(resolvedRepoRoot, packageDir);
47065
- const contextPath = join32(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
47085
+ const contextPath = join31(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
47066
47086
  if (!_generatorDeps.existsSync(contextPath)) {
47067
47087
  return [
47068
47088
  {
@@ -47130,7 +47150,7 @@ var init_generator2 = __esm(() => {
47130
47150
  });
47131
47151
 
47132
47152
  // src/analyze/scanner.ts
47133
- import { join as join33 } from "path";
47153
+ import { join as join32 } from "path";
47134
47154
  function resolveFrameworkAndRunner(language, pkg) {
47135
47155
  if (language === "go")
47136
47156
  return { framework: "", testRunner: "go-test" };
@@ -47152,7 +47172,7 @@ async function scanSourceRoots(workdir) {
47152
47172
  });
47153
47173
  try {
47154
47174
  const language = await deps.detectLanguage(workdir);
47155
- const pkg = await deps.readPackageJson(join33(workdir, "package.json"));
47175
+ const pkg = await deps.readPackageJson(join32(workdir, "package.json"));
47156
47176
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
47157
47177
  return [{ path: ".", language, framework, testRunner }];
47158
47178
  } catch {
@@ -47170,9 +47190,9 @@ async function scanSourceRoots(workdir) {
47170
47190
  packages = packages.slice(0, MAX_SOURCE_ROOTS);
47171
47191
  }
47172
47192
  return Promise.all(packages.map(async (pkgPath) => {
47173
- const pkgDir = pkgPath === "." ? workdir : join33(workdir, pkgPath);
47193
+ const pkgDir = pkgPath === "." ? workdir : join32(workdir, pkgPath);
47174
47194
  const language = await deps.detectLanguage(pkgDir);
47175
- const pkg = await deps.readPackageJson(join33(pkgDir, "package.json"));
47195
+ const pkg = await deps.readPackageJson(join32(pkgDir, "package.json"));
47176
47196
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
47177
47197
  return { path: pkgPath, language, framework, testRunner };
47178
47198
  }));
@@ -47205,7 +47225,7 @@ var init_analyze = __esm(() => {
47205
47225
  });
47206
47226
 
47207
47227
  // src/debate/pre-phase/grounder.ts
47208
- import { join as join34 } from "path";
47228
+ import { join as join33 } from "path";
47209
47229
  async function buildCodebaseContext(workdir) {
47210
47230
  const roots = await _grounderDeps.scanSourceRoots(workdir);
47211
47231
  return buildSourceRootsSection(normalizeRoots(workdir, roots));
@@ -47217,7 +47237,7 @@ function normalizeRoots(workdir, roots) {
47217
47237
  }));
47218
47238
  }
47219
47239
  async function writeManifestArtifact(ctx, manifest) {
47220
- const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47240
+ const manifestPath = join33(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47221
47241
  await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
47222
47242
  }
47223
47243
  var _grounderDeps, grounderStrategy = async (ctx) => {
@@ -47560,7 +47580,7 @@ function formatSpecDeltas(blockers, manifest) {
47560
47580
 
47561
47581
  // src/debate/verifiers/checks.ts
47562
47582
  import { existsSync as defaultExistsSync } from "fs";
47563
- import { join as join35 } from "path";
47583
+ import { join as join34 } from "path";
47564
47584
  function checkFilesExist(prd, workdir, deps) {
47565
47585
  const existsSync10 = deps?.existsSync ?? defaultExistsSync;
47566
47586
  const findings = [];
@@ -47570,7 +47590,7 @@ function checkFilesExist(prd, workdir, deps) {
47570
47590
  for (const entry of story.contextFiles) {
47571
47591
  const filePath = typeof entry === "string" ? entry : entry.path;
47572
47592
  const factId = typeof entry === "string" ? undefined : entry.factId;
47573
- const absPath = join35(workdir, filePath);
47593
+ const absPath = join34(workdir, filePath);
47574
47594
  if (existsSync10(absPath))
47575
47595
  continue;
47576
47596
  if (factId) {
@@ -47681,7 +47701,7 @@ var init_checks3 = () => {};
47681
47701
 
47682
47702
  // src/debate/verifiers/plan-checklist.ts
47683
47703
  import { existsSync as existsSync10 } from "fs";
47684
- import { join as join36 } from "path";
47704
+ import { join as join35 } from "path";
47685
47705
  function parsePrd(output) {
47686
47706
  if (!output)
47687
47707
  return null;
@@ -47692,7 +47712,7 @@ function parsePrd(output) {
47692
47712
  }
47693
47713
  }
47694
47714
  async function loadManifest(ctx) {
47695
- const manifestPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47715
+ const manifestPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47696
47716
  const raw = await _planChecklistDeps.readFile(manifestPath);
47697
47717
  if (!raw)
47698
47718
  return null;
@@ -47705,7 +47725,7 @@ async function loadManifest(ctx) {
47705
47725
  }
47706
47726
  }
47707
47727
  async function emitSpecDeltas(ctx, blockers, manifest) {
47708
- const artifactPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47728
+ const artifactPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47709
47729
  const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
47710
47730
  await _planChecklistDeps.write(artifactPath, content);
47711
47731
  return artifactPath;
@@ -48051,7 +48071,7 @@ var init_runner_plan_helpers = __esm(() => {
48051
48071
  });
48052
48072
 
48053
48073
  // src/debate/runner-plan.ts
48054
- import { join as join37 } from "path";
48074
+ import { join as join36 } from "path";
48055
48075
  async function runPlan(ctx, taskContext, outputFormat, opts) {
48056
48076
  const logger = _debateSessionDeps.getSafeLogger();
48057
48077
  const config2 = ctx.stageConfig;
@@ -48110,7 +48130,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
48110
48130
  sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
48111
48131
  proposers: ctx.stageConfig.proposers
48112
48132
  });
48113
- const outputPaths = resolved.map((_, i) => join37(opts.outputDir, `prd-debate-${i}.json`));
48133
+ const outputPaths = resolved.map((_, i) => join36(opts.outputDir, `prd-debate-${i}.json`));
48114
48134
  const successful = [];
48115
48135
  let rebuttalList;
48116
48136
  if (selectorKind === "verifier-pick") {
@@ -50353,9 +50373,9 @@ function validateFeatureName(feature) {
50353
50373
 
50354
50374
  // src/plan/critic.ts
50355
50375
  import { mkdir as mkdir6 } from "fs/promises";
50356
- import { dirname as dirname7, join as join40 } from "path";
50376
+ import { dirname as dirname7, join as join39 } from "path";
50357
50377
  async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
50358
- const path7 = join40(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50378
+ const path7 = join39(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50359
50379
  await mkdir6(dirname7(path7), { recursive: true });
50360
50380
  await Bun.write(path7, formatSpecDeltas(findings, manifest));
50361
50381
  return path7;
@@ -51568,9 +51588,9 @@ __export(exports_plan_decompose, {
51568
51588
  runReplanLoop: () => runReplanLoop,
51569
51589
  planDecomposeCommand: () => planDecomposeCommand
51570
51590
  });
51571
- import { join as join41 } from "path";
51591
+ import { join as join40 } from "path";
51572
51592
  async function planDecomposeCommand(workdir, config2, options) {
51573
- const prdPath = join41(workdir, ".nax", "features", options.feature, "prd.json");
51593
+ const prdPath = join40(workdir, ".nax", "features", options.feature, "prd.json");
51574
51594
  if (!_planDeps.existsSync(prdPath)) {
51575
51595
  throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
51576
51596
  stage: "decompose",
@@ -51744,7 +51764,7 @@ var init_plan_decompose = __esm(() => {
51744
51764
 
51745
51765
  // src/cli/plan-runtime.ts
51746
51766
  import { existsSync as existsSync15 } from "fs";
51747
- import { join as join42 } from "path";
51767
+ import { join as join41 } from "path";
51748
51768
  function isRuntimeWithAgentManager(value) {
51749
51769
  return typeof value === "object" && value !== null && "agentManager" in value;
51750
51770
  }
@@ -51796,7 +51816,7 @@ var init_plan_runtime = __esm(() => {
51796
51816
  writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
51797
51817
  scanSourceRoots: (workdir) => scanSourceRoots(workdir),
51798
51818
  createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
51799
- readPackageJson: (workdir) => Bun.file(join42(workdir, "package.json")).json().catch(() => null),
51819
+ readPackageJson: (workdir) => Bun.file(join41(workdir, "package.json")).json().catch(() => null),
51800
51820
  spawnSync: (cmd, opts) => {
51801
51821
  const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
51802
51822
  return { stdout: result.stdout, exitCode: result.exitCode };
@@ -52181,7 +52201,7 @@ var init_metrics = __esm(() => {
52181
52201
 
52182
52202
  // src/commands/common.ts
52183
52203
  import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
52184
- import { join as join43, resolve as resolve13 } from "path";
52204
+ import { join as join42, resolve as resolve13 } from "path";
52185
52205
  function resolveProject(options = {}) {
52186
52206
  const { dir, feature } = options;
52187
52207
  let projectRoot;
@@ -52189,12 +52209,12 @@ function resolveProject(options = {}) {
52189
52209
  let configPath;
52190
52210
  if (dir) {
52191
52211
  projectRoot = realpathSync3(resolve13(dir));
52192
- naxDir = join43(projectRoot, ".nax");
52212
+ naxDir = join42(projectRoot, ".nax");
52193
52213
  if (!existsSync16(naxDir)) {
52194
52214
  throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
52195
52215
  Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
52196
52216
  }
52197
- configPath = join43(naxDir, "config.json");
52217
+ configPath = join42(naxDir, "config.json");
52198
52218
  if (!existsSync16(configPath)) {
52199
52219
  throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
52200
52220
  Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
@@ -52202,17 +52222,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
52202
52222
  } else {
52203
52223
  const found = findProjectRoot(process.cwd());
52204
52224
  if (!found) {
52205
- const cwdNaxDir = join43(process.cwd(), ".nax");
52225
+ const cwdNaxDir = join42(process.cwd(), ".nax");
52206
52226
  if (existsSync16(cwdNaxDir)) {
52207
- const cwdConfigPath = join43(cwdNaxDir, "config.json");
52227
+ const cwdConfigPath = join42(cwdNaxDir, "config.json");
52208
52228
  throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
52209
52229
  Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
52210
52230
  }
52211
52231
  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
52232
  }
52213
52233
  projectRoot = found;
52214
- naxDir = join43(projectRoot, ".nax");
52215
- configPath = join43(naxDir, "config.json");
52234
+ naxDir = join42(projectRoot, ".nax");
52235
+ configPath = join42(naxDir, "config.json");
52216
52236
  }
52217
52237
  let featureDir;
52218
52238
  if (feature) {
@@ -52221,8 +52241,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
52221
52241
  } catch (error48) {
52222
52242
  throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
52223
52243
  }
52224
- const featuresDir = join43(naxDir, "features");
52225
- featureDir = join43(featuresDir, feature);
52244
+ const featuresDir = join42(naxDir, "features");
52245
+ featureDir = join42(featuresDir, feature);
52226
52246
  if (!existsSync16(featureDir)) {
52227
52247
  const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
52228
52248
  const availableMsg = availableFeatures.length > 0 ? `
@@ -52255,7 +52275,7 @@ async function resolveProjectAsync(options = {}) {
52255
52275
  }
52256
52276
  const isPlainName = !dir.includes("/") && !dir.includes("\\");
52257
52277
  if (isPlainName) {
52258
- const registryIdentityPath = join43(globalConfigDir(), dir, ".identity");
52278
+ const registryIdentityPath = join42(globalConfigDir(), dir, ".identity");
52259
52279
  const identityFile = Bun.file(registryIdentityPath);
52260
52280
  if (await identityFile.exists()) {
52261
52281
  try {
@@ -52287,12 +52307,12 @@ function findProjectRoot(startDir) {
52287
52307
  let current = resolve13(startDir);
52288
52308
  let depth = 0;
52289
52309
  while (depth < MAX_DIRECTORY_DEPTH) {
52290
- const naxDir = join43(current, ".nax");
52291
- const configPath = join43(naxDir, "config.json");
52310
+ const naxDir = join42(current, ".nax");
52311
+ const configPath = join42(naxDir, "config.json");
52292
52312
  if (existsSync16(configPath)) {
52293
52313
  return realpathSync3(current);
52294
52314
  }
52295
- const parent = join43(current, "..");
52315
+ const parent = join42(current, "..");
52296
52316
  if (parent === current) {
52297
52317
  break;
52298
52318
  }
@@ -52619,8 +52639,8 @@ var init_semantic_verdict = __esm(() => {
52619
52639
  await Bun.write(filePath, content);
52620
52640
  },
52621
52641
  readdir: async (dir) => {
52622
- const { readdir: readdir3 } = await import("fs/promises");
52623
- return readdir3(dir);
52642
+ const { readdir: readdir2 } = await import("fs/promises");
52643
+ return readdir2(dir);
52624
52644
  },
52625
52645
  readFile: async (filePath) => {
52626
52646
  return Bun.file(filePath).text();
@@ -53071,10 +53091,10 @@ var init_acceptance_setup = __esm(() => {
53071
53091
  },
53072
53092
  deleteSemanticVerdicts: async (featureDir) => {
53073
53093
  const dir = `${featureDir}/semantic-verdicts`;
53074
- const { readdir: readdir3, unlink: unlink2 } = await import("fs/promises");
53094
+ const { readdir: readdir2, unlink: unlink2 } = await import("fs/promises");
53075
53095
  let files;
53076
53096
  try {
53077
- files = await readdir3(dir);
53097
+ files = await readdir2(dir);
53078
53098
  } catch (err) {
53079
53099
  if (err.code === "ENOENT")
53080
53100
  return;
@@ -53451,10 +53471,10 @@ var init_effectiveness = __esm(() => {
53451
53471
 
53452
53472
  // src/execution/progress.ts
53453
53473
  import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
53454
- import { join as join46 } from "path";
53474
+ import { join as join45 } from "path";
53455
53475
  async function appendProgress(featureDir, storyId, status, message) {
53456
53476
  await mkdir7(featureDir, { recursive: true });
53457
- const progressPath = join46(featureDir, "progress.txt");
53477
+ const progressPath = join45(featureDir, "progress.txt");
53458
53478
  const timestamp = new Date().toISOString();
53459
53479
  const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
53460
53480
  `;
@@ -53648,7 +53668,7 @@ var init_completion = __esm(() => {
53648
53668
 
53649
53669
  // src/constitution/loader.ts
53650
53670
  import { existsSync as existsSync19 } from "fs";
53651
- import { join as join47 } from "path";
53671
+ import { join as join46 } from "path";
53652
53672
  function truncateToTokens(text, maxTokens) {
53653
53673
  const maxChars = maxTokens * 3;
53654
53674
  if (text.length <= maxChars) {
@@ -53670,7 +53690,7 @@ async function loadConstitution(projectDir, config2) {
53670
53690
  }
53671
53691
  let combinedContent = "";
53672
53692
  if (!config2.skipGlobal) {
53673
- const globalPath = join47(globalConfigDir(), config2.path);
53693
+ const globalPath = join46(globalConfigDir(), config2.path);
53674
53694
  if (existsSync19(globalPath)) {
53675
53695
  const validatedPath = validateFilePath(globalPath, globalConfigDir());
53676
53696
  const globalFile = Bun.file(validatedPath);
@@ -53680,7 +53700,7 @@ async function loadConstitution(projectDir, config2) {
53680
53700
  }
53681
53701
  }
53682
53702
  }
53683
- const projectPath = join47(projectDir, config2.path);
53703
+ const projectPath = join46(projectDir, config2.path);
53684
53704
  if (existsSync19(projectPath)) {
53685
53705
  const validatedPath = validateFilePath(projectPath, projectDir);
53686
53706
  const projectFile = Bun.file(validatedPath);
@@ -55114,7 +55134,7 @@ var init_story_orchestrator = __esm(() => {
55114
55134
  });
55115
55135
 
55116
55136
  // src/execution/build-plan-for-strategy.ts
55117
- import { join as join48 } from "path";
55137
+ import { join as join47 } from "path";
55118
55138
  function requiresInitialRefCapture(strategy) {
55119
55139
  return isThreeSessionStrategy(strategy);
55120
55140
  }
@@ -55160,7 +55180,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
55160
55180
  }
55161
55181
  if (shouldRunRectification(config2) && inputs.rectification) {
55162
55182
  const sink = makeDeclarationSink();
55163
- const packageDir = join48(ctx.packageDir, story.workdir ?? "");
55183
+ const packageDir = join47(ctx.packageDir, story.workdir ?? "");
55164
55184
  const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
55165
55185
  const strategies = [];
55166
55186
  const pkgQuality = ctx.packageView.select(qualityConfigSelector).quality;
@@ -55230,8 +55250,27 @@ var init_build_plan_for_strategy = __esm(() => {
55230
55250
  init_story_orchestrator();
55231
55251
  });
55232
55252
 
55253
+ // src/utils/paths.ts
55254
+ import { join as join48, relative as relative13, sep as sep4 } from "path";
55255
+ function packageDirRelative(projectDir, workdir) {
55256
+ if (!projectDir || !workdir || workdir === projectDir)
55257
+ return;
55258
+ const rel = relative13(projectDir, workdir);
55259
+ if (rel === ".." || rel.startsWith(`..${sep4}`))
55260
+ return;
55261
+ return rel && rel !== "." ? rel : undefined;
55262
+ }
55263
+ function getRunsDir() {
55264
+ return process.env.NAX_RUNS_DIR ?? join48(globalConfigDir(), "runs");
55265
+ }
55266
+ function getEventsRootDir() {
55267
+ return join48(globalConfigDir(), "events");
55268
+ }
55269
+ var init_paths3 = __esm(() => {
55270
+ init_paths();
55271
+ });
55272
+
55233
55273
  // src/execution/plan-inputs.ts
55234
- import { relative as relative13, sep as sep4 } from "path";
55235
55274
  function validatePlanInputs(story, config2) {
55236
55275
  if (!story.id || story.id.trim() === "") {
55237
55276
  throw new NaxError("Story ID is required and must be non-empty", "STORY_ID_INVALID", {
@@ -55307,15 +55346,8 @@ async function assemblePlanInputsFromCtx(ctx) {
55307
55346
  if (!_isTdd && !ctx.prompt?.trim()) {
55308
55347
  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
55348
  }
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);
55349
+ const packageDirRel = packageDirRelative(ctx.projectDir, ctx.workdir);
55350
+ const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.projectDir, packageDirRel);
55319
55351
  const [testWriterPrompt, implementerPrompt, verifierPrompt] = _isTdd ? await Promise.all([
55320
55352
  _isFreshRun ? buildThreeSessionPrompt("test-writer", ctx, isLite) : Promise.resolve(""),
55321
55353
  buildThreeSessionPrompt("implementer", ctx, isLite),
@@ -55326,7 +55358,8 @@ async function assemblePlanInputsFromCtx(ctx) {
55326
55358
  promptMarkdown: testWriterPrompt,
55327
55359
  featureContextMarkdown: ctx.featureContextMarkdown,
55328
55360
  constitution: ctx.constitution?.content,
55329
- lite: isLite
55361
+ lite: isLite,
55362
+ resolvedTestPatterns
55330
55363
  } : undefined;
55331
55364
  const greenfieldGateInput = _isTdd && _isFreshRun && resolvedTestPatterns ? { story, workdir: ctx.workdir, resolvedTestPatterns } : undefined;
55332
55365
  const implementerInput = {
@@ -55351,7 +55384,7 @@ async function assemblePlanInputsFromCtx(ctx) {
55351
55384
  naxIgnoreIndex: ctx.naxIgnoreIndex,
55352
55385
  regressionMode: toVerifyScopedMode(ctx.config.execution?.regressionGate?.mode),
55353
55386
  repoRoot: ctx.projectDir,
55354
- packagePrefix: packageDirRelative,
55387
+ packagePrefix: packageDirRel,
55355
55388
  resolvedTestPatterns
55356
55389
  } : undefined;
55357
55390
  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 +55489,7 @@ var init_plan_inputs = __esm(() => {
55456
55489
  init_prompts();
55457
55490
  init_review();
55458
55491
  init_resolver();
55492
+ init_paths3();
55459
55493
  });
55460
55494
 
55461
55495
  // src/pipeline/stages/execution-helpers.ts
@@ -56521,6 +56555,8 @@ var init_queue_check = __esm(() => {
56521
56555
  // src/pipeline/stages/routing.ts
56522
56556
  var routingStage, _routingDeps;
56523
56557
  var init_routing2 = __esm(() => {
56558
+ init_test_runners();
56559
+ init_paths3();
56524
56560
  init_greenfield();
56525
56561
  init_logger2();
56526
56562
  init_prd();
@@ -56564,7 +56600,16 @@ var init_routing2 = __esm(() => {
56564
56600
  const greenfieldDetectionEnabled = ctx.config.tdd.greenfieldDetection ?? true;
56565
56601
  if (greenfieldDetectionEnabled && routing.testStrategy.startsWith("three-session-tdd")) {
56566
56602
  const greenfieldScanDir = ctx.workdir;
56567
- const isGreenfield = await _routingDeps.isGreenfieldStory(ctx.story, greenfieldScanDir);
56603
+ const root = ctx.projectDir ?? ctx.workdir;
56604
+ const packageDir = packageDirRelative(root, ctx.workdir);
56605
+ const resolved = await _routingDeps.resolveTestFilePatterns(ctx.config, root, packageDir, { storyId: ctx.story.id }).catch((err) => {
56606
+ logger.debug("routing", "Test-pattern resolution failed; using default greenfield patterns", {
56607
+ storyId: ctx.story.id,
56608
+ error: errorMessage(err)
56609
+ });
56610
+ return;
56611
+ });
56612
+ const isGreenfield = await _routingDeps.isGreenfieldStory(ctx.story, greenfieldScanDir, resolved?.globs);
56568
56613
  if (isGreenfield) {
56569
56614
  logger.info("routing", "Greenfield detected \u2014 forcing test-after strategy", {
56570
56615
  storyId: ctx.story.id,
@@ -56592,6 +56637,7 @@ var init_routing2 = __esm(() => {
56592
56637
  resolveRouting,
56593
56638
  complexityToModelTier,
56594
56639
  isGreenfieldStory,
56640
+ resolveTestFilePatterns,
56595
56641
  clearCache,
56596
56642
  savePRD
56597
56643
  };
@@ -59508,18 +59554,6 @@ var init_loader4 = __esm(() => {
59508
59554
  FALLBACK_TEST_FILE_RE = /\.(test|spec)\.(ts|js|tsx|jsx|mjs)$/;
59509
59555
  });
59510
59556
 
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
59557
  // src/utils/command-argv.ts
59524
59558
  function parseCommandToArgv(command) {
59525
59559
  const safeEnv = buildAllowedEnv();
@@ -59574,7 +59608,7 @@ var init_command_argv = __esm(() => {
59574
59608
  });
59575
59609
 
59576
59610
  // src/hooks/runner.ts
59577
- import { join as join75 } from "path";
59611
+ import { join as join74 } from "path";
59578
59612
  function createDrainDeadline2(deadlineMs) {
59579
59613
  let timeoutId;
59580
59614
  const promise2 = new Promise((resolve16) => {
@@ -59593,14 +59627,14 @@ async function loadHooksConfig(projectDir, globalDir) {
59593
59627
  let globalHooks = { hooks: {} };
59594
59628
  let projectHooks = { hooks: {} };
59595
59629
  let skipGlobal = false;
59596
- const projectPath = join75(projectDir, "hooks.json");
59630
+ const projectPath = join74(projectDir, "hooks.json");
59597
59631
  const projectData = await loadJsonFile(projectPath, "hooks");
59598
59632
  if (projectData) {
59599
59633
  projectHooks = projectData;
59600
59634
  skipGlobal = projectData.skipGlobal ?? false;
59601
59635
  }
59602
59636
  if (!skipGlobal && globalDir) {
59603
- const globalPath = join75(globalDir, "hooks.json");
59637
+ const globalPath = join74(globalDir, "hooks.json");
59604
59638
  const globalData = await loadJsonFile(globalPath, "hooks");
59605
59639
  if (globalData) {
59606
59640
  globalHooks = globalData;
@@ -59770,7 +59804,7 @@ var package_default;
59770
59804
  var init_package = __esm(() => {
59771
59805
  package_default = {
59772
59806
  name: "@nathapp/nax",
59773
- version: "0.69.7",
59807
+ version: "0.69.8",
59774
59808
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
59775
59809
  type: "module",
59776
59810
  bin: {
@@ -59865,8 +59899,8 @@ var init_version = __esm(() => {
59865
59899
  NAX_VERSION = package_default.version;
59866
59900
  NAX_COMMIT = (() => {
59867
59901
  try {
59868
- if (/^[0-9a-f]{6,10}$/.test("d524317e"))
59869
- return "d524317e";
59902
+ if (/^[0-9a-f]{6,10}$/.test("0b7fc5bf"))
59903
+ return "0b7fc5bf";
59870
59904
  } catch {}
59871
59905
  try {
59872
59906
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -60743,15 +60777,15 @@ var init_acceptance_loop = __esm(() => {
60743
60777
 
60744
60778
  // src/session/scratch-purge.ts
60745
60779
  import { mkdir as mkdir12, rename, rm } from "fs/promises";
60746
- import { dirname as dirname12, join as join76 } from "path";
60780
+ import { dirname as dirname12, join as join75 } from "path";
60747
60781
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
60748
- const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
60782
+ const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
60749
60783
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
60750
60784
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
60751
60785
  let purged = 0;
60752
60786
  for (const sessionId of sessionIds) {
60753
- const sessionDir = join76(sessionsDir, sessionId);
60754
- const descriptorPath = join76(sessionDir, "descriptor.json");
60787
+ const sessionDir = join75(sessionsDir, sessionId);
60788
+ const descriptorPath = join75(sessionDir, "descriptor.json");
60755
60789
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
60756
60790
  continue;
60757
60791
  let lastActivityAt;
@@ -60767,7 +60801,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
60767
60801
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
60768
60802
  continue;
60769
60803
  if (archiveInsteadOfDelete) {
60770
- const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60804
+ const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60771
60805
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
60772
60806
  } else {
60773
60807
  await _scratchPurgeDeps.remove(sessionDir);
@@ -61505,12 +61539,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
61505
61539
 
61506
61540
  // src/pipeline/subscribers/events-writer.ts
61507
61541
  import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
61508
- import { basename as basename13, join as join77 } from "path";
61542
+ import { basename as basename13, join as join76 } from "path";
61509
61543
  function wireEventsWriter(bus, feature, runId, workdir) {
61510
61544
  const logger = getSafeLogger();
61511
61545
  const project = basename13(workdir);
61512
- const eventsDir = join77(getEventsRootDir(), project);
61513
- const eventsFile = join77(eventsDir, "events.jsonl");
61546
+ const eventsDir = join76(getEventsRootDir(), project);
61547
+ const eventsFile = join76(eventsDir, "events.jsonl");
61514
61548
  let dirReady = false;
61515
61549
  const write = (line) => {
61516
61550
  return (async () => {
@@ -61691,12 +61725,12 @@ var init_interaction2 = __esm(() => {
61691
61725
 
61692
61726
  // src/pipeline/subscribers/registry.ts
61693
61727
  import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
61694
- import { basename as basename14, join as join78 } from "path";
61728
+ import { basename as basename14, join as join77 } from "path";
61695
61729
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
61696
61730
  const logger = getSafeLogger();
61697
61731
  const project = basename14(workdir);
61698
- const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
61699
- const metaFile = join78(runDir, "meta.json");
61732
+ const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
61733
+ const metaFile = join77(runDir, "meta.json");
61700
61734
  const unsub = bus.on("run:started", (_ev) => {
61701
61735
  return (async () => {
61702
61736
  try {
@@ -61706,8 +61740,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
61706
61740
  project,
61707
61741
  feature,
61708
61742
  workdir,
61709
- statusPath: join78(outputDir, "features", feature, "status.json"),
61710
- eventsDir: join78(outputDir, "features", feature, "runs"),
61743
+ statusPath: join77(outputDir, "features", feature, "status.json"),
61744
+ eventsDir: join77(outputDir, "features", feature, "runs"),
61711
61745
  registeredAt: new Date().toISOString()
61712
61746
  };
61713
61747
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -61953,7 +61987,7 @@ var init_types9 = __esm(() => {
61953
61987
 
61954
61988
  // src/worktree/dependencies.ts
61955
61989
  import { existsSync as existsSync30 } from "fs";
61956
- import { join as join79 } from "path";
61990
+ import { join as join78 } from "path";
61957
61991
  async function prepareWorktreeDependencies(options) {
61958
61992
  const mode = options.config.execution.worktreeDependencies.mode;
61959
61993
  const resolvedCwd = resolveDependencyCwd(options);
@@ -61967,7 +62001,7 @@ async function prepareWorktreeDependencies(options) {
61967
62001
  }
61968
62002
  }
61969
62003
  function resolveDependencyCwd(options) {
61970
- return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
62004
+ return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
61971
62005
  }
61972
62006
  function resolveInheritedDependencies(options, resolvedCwd) {
61973
62007
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -61977,7 +62011,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
61977
62011
  }
61978
62012
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
61979
62013
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
61980
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
62014
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join78(directory, filename))));
61981
62015
  }
61982
62016
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
61983
62017
  const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
@@ -62041,13 +62075,13 @@ __export(exports_manager, {
62041
62075
  });
62042
62076
  import { existsSync as existsSync31, symlinkSync } from "fs";
62043
62077
  import { mkdir as mkdir15 } from "fs/promises";
62044
- import { join as join80 } from "path";
62078
+ import { join as join79 } from "path";
62045
62079
 
62046
62080
  class WorktreeManager {
62047
62081
  async ensureGitExcludes(projectRoot) {
62048
62082
  const logger = getSafeLogger();
62049
- const infoDir = join80(projectRoot, ".git", "info");
62050
- const excludePath = join80(infoDir, "exclude");
62083
+ const infoDir = join79(projectRoot, ".git", "info");
62084
+ const excludePath = join79(infoDir, "exclude");
62051
62085
  try {
62052
62086
  await mkdir15(infoDir, { recursive: true });
62053
62087
  let existing = "";
@@ -62074,7 +62108,7 @@ ${missing.join(`
62074
62108
  }
62075
62109
  async create(projectRoot, storyId) {
62076
62110
  validateStoryId(storyId);
62077
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62111
+ const worktreePath = join79(projectRoot, ".nax-wt", storyId);
62078
62112
  const branchName = `nax/${storyId}`;
62079
62113
  try {
62080
62114
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -62115,9 +62149,9 @@ ${missing.join(`
62115
62149
  }
62116
62150
  throw new Error(`Failed to create worktree: ${String(error48)}`);
62117
62151
  }
62118
- const envSource = join80(projectRoot, ".env");
62152
+ const envSource = join79(projectRoot, ".env");
62119
62153
  if (existsSync31(envSource)) {
62120
- const envTarget = join80(worktreePath, ".env");
62154
+ const envTarget = join79(worktreePath, ".env");
62121
62155
  try {
62122
62156
  symlinkSync(envSource, envTarget, "file");
62123
62157
  } catch (error48) {
@@ -62128,7 +62162,7 @@ ${missing.join(`
62128
62162
  }
62129
62163
  async remove(projectRoot, storyId) {
62130
62164
  validateStoryId(storyId);
62131
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62165
+ const worktreePath = join79(projectRoot, ".nax-wt", storyId);
62132
62166
  const branchName = `nax/${storyId}`;
62133
62167
  try {
62134
62168
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -62940,10 +62974,10 @@ var init_merge_conflict_rectify = __esm(() => {
62940
62974
  });
62941
62975
 
62942
62976
  // src/execution/pipeline-result-handler.ts
62943
- import { join as join81 } from "path";
62977
+ import { join as join80 } from "path";
62944
62978
  async function removeWorktreeDirectory(projectRoot, storyId) {
62945
62979
  const logger = getSafeLogger();
62946
- const worktreePath = join81(projectRoot, ".nax-wt", storyId);
62980
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62947
62981
  try {
62948
62982
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
62949
62983
  cwd: projectRoot,
@@ -63160,7 +63194,7 @@ var init_pipeline_result_handler = __esm(() => {
63160
63194
 
63161
63195
  // src/execution/iteration-runner.ts
63162
63196
  import { existsSync as existsSync32 } from "fs";
63163
- import { join as join82 } from "path";
63197
+ import { join as join81 } from "path";
63164
63198
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
63165
63199
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
63166
63200
  if (ctx.dryRun) {
@@ -63185,7 +63219,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63185
63219
  const storyStartTime = Date.now();
63186
63220
  let effectiveWorkdir = ctx.workdir;
63187
63221
  if (ctx.config.execution.storyIsolation === "worktree") {
63188
- const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
63222
+ const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
63189
63223
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
63190
63224
  if (!worktreeExists) {
63191
63225
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -63205,7 +63239,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63205
63239
  }
63206
63240
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
63207
63241
  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;
63242
+ const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join81(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
63209
63243
  let dependencyContext;
63210
63244
  if (ctx.config.execution.storyIsolation === "worktree") {
63211
63245
  try {
@@ -63232,7 +63266,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63232
63266
  };
63233
63267
  }
63234
63268
  }
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;
63269
+ 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
63270
  const pipelineContext = {
63237
63271
  config: effectiveConfig,
63238
63272
  rootConfig: ctx.config,
@@ -63434,7 +63468,7 @@ __export(exports_parallel_worker, {
63434
63468
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
63435
63469
  _parallelWorkerDeps: () => _parallelWorkerDeps
63436
63470
  });
63437
- import { join as join83 } from "path";
63471
+ import { join as join82 } from "path";
63438
63472
  function buildWorktreePipelineContext(base, _story) {
63439
63473
  return { ...base, prd: structuredClone(base.prd) };
63440
63474
  }
@@ -63457,7 +63491,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
63457
63491
  story,
63458
63492
  stories: [story],
63459
63493
  projectDir: context.projectDir,
63460
- workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
63494
+ workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
63461
63495
  worktreeDependencyContext: dependencyContext,
63462
63496
  routing,
63463
63497
  storyGitRef: storyGitRef ?? undefined
@@ -64344,7 +64378,7 @@ async function writeStatusFile(filePath, status) {
64344
64378
  var init_status_file = () => {};
64345
64379
 
64346
64380
  // src/execution/status-writer.ts
64347
- import { join as join84 } from "path";
64381
+ import { join as join83 } from "path";
64348
64382
 
64349
64383
  class StatusWriter {
64350
64384
  statusFile;
@@ -64463,7 +64497,7 @@ class StatusWriter {
64463
64497
  if (!this._prd)
64464
64498
  return;
64465
64499
  const safeLogger = getSafeLogger();
64466
- const featureStatusPath = join84(featureDir, "status.json");
64500
+ const featureStatusPath = join83(featureDir, "status.json");
64467
64501
  const write = async () => {
64468
64502
  try {
64469
64503
  const base = this.getSnapshot(totalCost, iterations);
@@ -64495,7 +64529,7 @@ __export(exports_migrate, {
64495
64529
  detectGeneratedContent: () => detectGeneratedContent
64496
64530
  });
64497
64531
  import { existsSync as existsSync33 } from "fs";
64498
- import { mkdir as mkdir16, readdir as readdir6, rename as rename3 } from "fs/promises";
64532
+ import { mkdir as mkdir16, readdir as readdir5, rename as rename3 } from "fs/promises";
64499
64533
  import path22 from "path";
64500
64534
  async function detectGeneratedContent(naxDir) {
64501
64535
  if (!existsSync33(naxDir))
@@ -64503,7 +64537,7 @@ async function detectGeneratedContent(naxDir) {
64503
64537
  const candidates = [];
64504
64538
  let entries = [];
64505
64539
  try {
64506
- entries = await readdir6(naxDir);
64540
+ entries = await readdir5(naxDir);
64507
64541
  } catch {
64508
64542
  return [];
64509
64543
  }
@@ -64516,13 +64550,13 @@ async function detectGeneratedContent(naxDir) {
64516
64550
  if (existsSync33(featuresDir)) {
64517
64551
  let featureDirs = [];
64518
64552
  try {
64519
- featureDirs = await readdir6(featuresDir);
64553
+ featureDirs = await readdir5(featuresDir);
64520
64554
  } catch {}
64521
64555
  for (const fid of featureDirs) {
64522
64556
  const featureDir = path22.join(featuresDir, fid);
64523
64557
  let subEntries = [];
64524
64558
  try {
64525
- subEntries = await readdir6(featureDir);
64559
+ subEntries = await readdir5(featureDir);
64526
64560
  } catch {
64527
64561
  continue;
64528
64562
  }
@@ -64537,7 +64571,7 @@ async function detectGeneratedContent(naxDir) {
64537
64571
  const storiesDir = path22.join(featureDir, "stories");
64538
64572
  let storyDirs = [];
64539
64573
  try {
64540
- storyDirs = await readdir6(storiesDir);
64574
+ storyDirs = await readdir5(storiesDir);
64541
64575
  } catch {
64542
64576
  continue;
64543
64577
  }
@@ -64545,7 +64579,7 @@ async function detectGeneratedContent(naxDir) {
64545
64579
  const storyDir = path22.join(storiesDir, sid);
64546
64580
  let storyEntries = [];
64547
64581
  try {
64548
- storyEntries = await readdir6(storyDir);
64582
+ storyEntries = await readdir5(storyDir);
64549
64583
  } catch {
64550
64584
  continue;
64551
64585
  }
@@ -64897,7 +64931,7 @@ __export(exports_run_initialization, {
64897
64931
  initializeRun: () => initializeRun,
64898
64932
  _reconcileDeps: () => _reconcileDeps
64899
64933
  });
64900
- import { join as join85 } from "path";
64934
+ import { join as join84 } from "path";
64901
64935
  async function reconcileState(prd, prdPath, workdir, config2) {
64902
64936
  const logger = getSafeLogger();
64903
64937
  let reconciledCount = 0;
@@ -64914,7 +64948,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
64914
64948
  });
64915
64949
  continue;
64916
64950
  }
64917
- const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
64951
+ const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
64918
64952
  try {
64919
64953
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
64920
64954
  if (!reviewResult.success) {
@@ -94718,7 +94752,7 @@ __export(exports_curator, {
94718
94752
  });
94719
94753
  import { readdirSync as readdirSync8 } from "fs";
94720
94754
  import { unlink as unlink4 } from "fs/promises";
94721
- import { basename as basename15, join as join87 } from "path";
94755
+ import { basename as basename15, join as join86 } from "path";
94722
94756
  function getProjectKey(config2, projectDir) {
94723
94757
  return config2.name?.trim() || basename15(projectDir);
94724
94758
  }
@@ -94801,7 +94835,7 @@ async function curatorStatus(options) {
94801
94835
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94802
94836
  const projectKey = getProjectKey(config2, resolved.projectDir);
94803
94837
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94804
- const runsDir = join87(outputDir, "runs");
94838
+ const runsDir = join86(outputDir, "runs");
94805
94839
  const runIds = listRunIds(runsDir);
94806
94840
  let runId;
94807
94841
  if (options.run) {
@@ -94818,8 +94852,8 @@ async function curatorStatus(options) {
94818
94852
  runId = runIds[runIds.length - 1];
94819
94853
  }
94820
94854
  console.log(`Run: ${runId}`);
94821
- const runDir = join87(runsDir, runId);
94822
- const observationsPath = join87(runDir, "observations.jsonl");
94855
+ const runDir = join86(runsDir, runId);
94856
+ const observationsPath = join86(runDir, "observations.jsonl");
94823
94857
  const observations = await parseObservations(observationsPath);
94824
94858
  const counts = new Map;
94825
94859
  for (const obs of observations) {
@@ -94829,7 +94863,7 @@ async function curatorStatus(options) {
94829
94863
  for (const [kind, count] of counts.entries()) {
94830
94864
  console.log(` ${kind}: ${count}`);
94831
94865
  }
94832
- const proposalsPath = join87(runDir, "curator-proposals.md");
94866
+ const proposalsPath = join86(runDir, "curator-proposals.md");
94833
94867
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94834
94868
  if (proposalText !== null) {
94835
94869
  console.log("");
@@ -94843,8 +94877,8 @@ async function curatorCommit(options) {
94843
94877
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94844
94878
  const projectKey = getProjectKey(config2, resolved.projectDir);
94845
94879
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94846
- const runDir = join87(outputDir, "runs", options.runId);
94847
- const proposalsPath = join87(runDir, "curator-proposals.md");
94880
+ const runDir = join86(outputDir, "runs", options.runId);
94881
+ const proposalsPath = join86(runDir, "curator-proposals.md");
94848
94882
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94849
94883
  if (proposalText === null) {
94850
94884
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -94860,7 +94894,7 @@ async function curatorCommit(options) {
94860
94894
  const dropFileState = new Map;
94861
94895
  const skippedDrops = new Set;
94862
94896
  for (const drop2 of drops) {
94863
- const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94897
+ const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94864
94898
  if (!dropFileState.has(targetPath)) {
94865
94899
  const fileExists2 = await Bun.file(targetPath).exists();
94866
94900
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -94894,7 +94928,7 @@ async function curatorCommit(options) {
94894
94928
  if (skippedDrops.has(drop2)) {
94895
94929
  continue;
94896
94930
  }
94897
- const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94931
+ const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94898
94932
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
94899
94933
  const filtered = filterDropContent(existing, drop2.description);
94900
94934
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -94903,7 +94937,7 @@ async function curatorCommit(options) {
94903
94937
  }
94904
94938
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
94905
94939
  for (const add2 of adds) {
94906
- const targetPath = join87(resolved.projectDir, add2.canonicalFile);
94940
+ const targetPath = join86(resolved.projectDir, add2.canonicalFile);
94907
94941
  const content = buildAddContent(add2);
94908
94942
  await _curatorCmdDeps.appendFile(targetPath, content);
94909
94943
  modifiedFiles.add(targetPath);
@@ -94940,7 +94974,7 @@ async function curatorDryrun(options) {
94940
94974
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94941
94975
  const projectKey = getProjectKey(config2, resolved.projectDir);
94942
94976
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94943
- const runsDir = join87(outputDir, "runs");
94977
+ const runsDir = join86(outputDir, "runs");
94944
94978
  const runIds = listRunIds(runsDir);
94945
94979
  if (runIds.length === 0) {
94946
94980
  console.log("No runs found.");
@@ -94951,7 +94985,7 @@ async function curatorDryrun(options) {
94951
94985
  console.log(`Run ${options.run} not found in ${runsDir}.`);
94952
94986
  return;
94953
94987
  }
94954
- const observationsPath = join87(runsDir, runId, "observations.jsonl");
94988
+ const observationsPath = join86(runsDir, runId, "observations.jsonl");
94955
94989
  const observations = await parseObservations(observationsPath);
94956
94990
  const thresholds = getThresholds(config2);
94957
94991
  const proposals = runHeuristics(observations, thresholds);
@@ -94992,12 +95026,12 @@ async function curatorGc(options) {
94992
95026
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
94993
95027
  const projectKey = getProjectKey(config2, resolved.projectDir);
94994
95028
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94995
- const perRunsDir = join87(outputDir, "runs");
95029
+ const perRunsDir = join86(outputDir, "runs");
94996
95030
  for (const runId of uniqueRunIds) {
94997
95031
  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"));
95032
+ const runDir = join86(perRunsDir, runId);
95033
+ await _curatorCmdDeps.removeFile(join86(runDir, "observations.jsonl"));
95034
+ await _curatorCmdDeps.removeFile(join86(runDir, "curator-proposals.md"));
95001
95035
  }
95002
95036
  }
95003
95037
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -95042,7 +95076,7 @@ var init_curator2 = __esm(() => {
95042
95076
  init_source();
95043
95077
  import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
95044
95078
  import { homedir as homedir3 } from "os";
95045
- import { basename as basename16, join as join88 } from "path";
95079
+ import { basename as basename16, join as join87 } from "path";
95046
95080
 
95047
95081
  // node_modules/commander/esm.mjs
95048
95082
  var import__ = __toESM(require_commander(), 1);
@@ -95066,12 +95100,12 @@ init_errors();
95066
95100
  init_operations();
95067
95101
 
95068
95102
  // src/plan/strategies/context-builder.ts
95069
- import { join as join39 } from "path";
95103
+ import { join as join38 } from "path";
95070
95104
  init_config();
95071
95105
  init_errors();
95072
95106
  init_interaction();
95073
95107
  async function buildPlanModeContext(workdir, fullConfig, options, deps) {
95074
- const naxDir = join39(workdir, ".nax");
95108
+ const naxDir = join38(workdir, ".nax");
95075
95109
  if (!deps.existsSync(naxDir)) {
95076
95110
  throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
95077
95111
  stage: "plan",
@@ -95079,8 +95113,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
95079
95113
  });
95080
95114
  }
95081
95115
  validateFeatureName(options.feature);
95082
- const outputDir = join39(naxDir, "features", options.feature);
95083
- const outputPath = join39(outputDir, "prd.json");
95116
+ const outputDir = join38(naxDir, "features", options.feature);
95117
+ const outputPath = join38(outputDir, "prd.json");
95084
95118
  const [specContent, sourceRoots, pkg] = await Promise.all([
95085
95119
  deps.readFile(options.from),
95086
95120
  deps.scanSourceRoots(workdir),
@@ -95095,7 +95129,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
95095
95129
  ...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
95096
95130
  ];
95097
95131
  const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
95098
- const packageJson = await deps.readPackageJsonAt(join39(workdir, relativePath, "package.json"));
95132
+ const packageJson = await deps.readPackageJsonAt(join38(workdir, relativePath, "package.json"));
95099
95133
  return buildPackageSummary(relativePath, packageJson);
95100
95134
  }));
95101
95135
  const projectName = detectProjectName(workdir, pkg);
@@ -95698,7 +95732,7 @@ init_interaction();
95698
95732
  init_prd();
95699
95733
  init_runtime();
95700
95734
  import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
95701
- import { basename as basename7, join as join44, resolve as resolve14 } from "path";
95735
+ import { basename as basename7, join as join43, resolve as resolve14 } from "path";
95702
95736
  var _statusFeaturesDeps = {
95703
95737
  projectOutputDir,
95704
95738
  loadConfig
@@ -95712,7 +95746,7 @@ function isPidAlive(pid) {
95712
95746
  }
95713
95747
  }
95714
95748
  async function loadStatusFile(featureDir) {
95715
- const statusPath = join44(featureDir, "status.json");
95749
+ const statusPath = join43(featureDir, "status.json");
95716
95750
  if (!existsSync17(statusPath)) {
95717
95751
  return null;
95718
95752
  }
@@ -95727,7 +95761,7 @@ async function loadProjectStatusFile(projectDir) {
95727
95761
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95728
95762
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95729
95763
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95730
- const statusPath = join44(outputDir, "status.json");
95764
+ const statusPath = join43(outputDir, "status.json");
95731
95765
  if (!existsSync17(statusPath)) {
95732
95766
  return null;
95733
95767
  }
@@ -95739,7 +95773,7 @@ async function loadProjectStatusFile(projectDir) {
95739
95773
  }
95740
95774
  }
95741
95775
  async function getFeatureSummary(featureName, featureDir) {
95742
- const prdPath = join44(featureDir, "prd.json");
95776
+ const prdPath = join43(featureDir, "prd.json");
95743
95777
  if (!existsSync17(prdPath)) {
95744
95778
  return {
95745
95779
  name: featureName,
@@ -95782,7 +95816,7 @@ async function getFeatureSummary(featureName, featureDir) {
95782
95816
  };
95783
95817
  }
95784
95818
  }
95785
- const runsDir = join44(featureDir, "runs");
95819
+ const runsDir = join43(featureDir, "runs");
95786
95820
  if (existsSync17(runsDir)) {
95787
95821
  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
95822
  if (runs.length > 0) {
@@ -95796,7 +95830,7 @@ async function displayAllFeatures(projectDir) {
95796
95830
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95797
95831
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95798
95832
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95799
- const featuresDir = join44(outputDir, "features");
95833
+ const featuresDir = join43(outputDir, "features");
95800
95834
  if (!existsSync17(featuresDir)) {
95801
95835
  console.log(source_default.dim("No features found."));
95802
95836
  return;
@@ -95837,7 +95871,7 @@ async function displayAllFeatures(projectDir) {
95837
95871
  console.log();
95838
95872
  }
95839
95873
  }
95840
- const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join44(featuresDir, name))));
95874
+ const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join43(featuresDir, name))));
95841
95875
  console.log(source_default.bold(`\uD83D\uDCCA Features
95842
95876
  `));
95843
95877
  const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
@@ -95863,7 +95897,7 @@ async function displayAllFeatures(projectDir) {
95863
95897
  console.log();
95864
95898
  }
95865
95899
  async function displayFeatureDetails(featureName, featureDir) {
95866
- const prdPath = join44(featureDir, "prd.json");
95900
+ const prdPath = join43(featureDir, "prd.json");
95867
95901
  if (!existsSync17(prdPath)) {
95868
95902
  console.log(source_default.bold(`
95869
95903
  \uD83D\uDCCA ${featureName}
@@ -96009,7 +96043,7 @@ async function displayFeatureStatus(options = {}) {
96009
96043
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
96010
96044
  const projectKey = config2?.name?.trim() || basename7(projectDir);
96011
96045
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
96012
- featureDir = join44(outputDir, "features", options.feature);
96046
+ featureDir = join43(outputDir, "features", options.feature);
96013
96047
  } else {
96014
96048
  const resolved = resolveProject({ feature: options.feature });
96015
96049
  if (!resolved.featureDir) {
@@ -96029,7 +96063,7 @@ init_errors();
96029
96063
  init_logger2();
96030
96064
  init_runtime();
96031
96065
  import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
96032
- import { basename as basename8, join as join45 } from "path";
96066
+ import { basename as basename8, join as join44 } from "path";
96033
96067
  async function resolveOutputDir2(workdir, override) {
96034
96068
  if (override)
96035
96069
  return override;
@@ -96053,7 +96087,7 @@ async function runsListCommand(options) {
96053
96087
  const logger = getLogger();
96054
96088
  const { feature, workdir } = options;
96055
96089
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
96056
- const runsDir = join45(outputDir, "features", feature, "runs");
96090
+ const runsDir = join44(outputDir, "features", feature, "runs");
96057
96091
  if (!existsSync18(runsDir)) {
96058
96092
  logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
96059
96093
  return;
@@ -96065,7 +96099,7 @@ async function runsListCommand(options) {
96065
96099
  }
96066
96100
  logger.info("cli", `Runs for ${feature}`, { count: files.length });
96067
96101
  for (const file3 of files.sort().reverse()) {
96068
- const logPath = join45(runsDir, file3);
96102
+ const logPath = join44(runsDir, file3);
96069
96103
  const entries = await parseRunLog(logPath);
96070
96104
  const startEvent = entries.find((e) => e.message === "run.start");
96071
96105
  const completeEvent = entries.find((e) => e.message === "run.complete");
@@ -96092,7 +96126,7 @@ async function runsShowCommand(options) {
96092
96126
  const logger = getLogger();
96093
96127
  const { runId, feature, workdir } = options;
96094
96128
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
96095
- const logPath = join45(outputDir, "features", feature, "runs", `${runId}.jsonl`);
96129
+ const logPath = join44(outputDir, "features", feature, "runs", `${runId}.jsonl`);
96096
96130
  if (!existsSync18(logPath)) {
96097
96131
  logger.error("cli", "Run not found", { runId, feature, logPath });
96098
96132
  throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
@@ -97438,19 +97472,19 @@ async function detectCommand(options) {
97438
97472
  // src/commands/logs.ts
97439
97473
  init_common();
97440
97474
  import { existsSync as existsSync28 } from "fs";
97441
- import { join as join71 } from "path";
97475
+ import { join as join70 } from "path";
97442
97476
 
97443
97477
  // src/commands/logs-formatter.ts
97444
97478
  init_source();
97445
97479
  init_formatter();
97446
97480
  import { readdirSync as readdirSync7 } from "fs";
97447
- import { join as join70 } from "path";
97481
+ import { join as join69 } from "path";
97448
97482
 
97449
97483
  // src/commands/logs-reader.ts
97450
97484
  init_paths3();
97451
97485
  import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
97452
- import { readdir as readdir4 } from "fs/promises";
97453
- import { join as join69 } from "path";
97486
+ import { readdir as readdir3 } from "fs/promises";
97487
+ import { join as join68 } from "path";
97454
97488
  var _logsReaderDeps = {
97455
97489
  getRunsDir
97456
97490
  };
@@ -97458,13 +97492,13 @@ async function resolveRunFileFromRegistry(runId) {
97458
97492
  const runsDir = _logsReaderDeps.getRunsDir();
97459
97493
  let entries;
97460
97494
  try {
97461
- entries = await readdir4(runsDir);
97495
+ entries = await readdir3(runsDir);
97462
97496
  } catch {
97463
97497
  throw new Error(`Run not found in registry: ${runId}`);
97464
97498
  }
97465
97499
  let matched = null;
97466
97500
  for (const entry of entries) {
97467
- const metaPath = join69(runsDir, entry, "meta.json");
97501
+ const metaPath = join68(runsDir, entry, "meta.json");
97468
97502
  try {
97469
97503
  const meta3 = await Bun.file(metaPath).json();
97470
97504
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -97486,14 +97520,14 @@ async function resolveRunFileFromRegistry(runId) {
97486
97520
  return null;
97487
97521
  }
97488
97522
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
97489
- return join69(matched.eventsDir, specificFile ?? files[0]);
97523
+ return join68(matched.eventsDir, specificFile ?? files[0]);
97490
97524
  }
97491
97525
  async function selectRunFile(runsDir) {
97492
97526
  const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97493
97527
  if (files.length === 0) {
97494
97528
  return null;
97495
97529
  }
97496
- return join69(runsDir, files[0]);
97530
+ return join68(runsDir, files[0]);
97497
97531
  }
97498
97532
  async function extractRunSummary(filePath) {
97499
97533
  const file3 = Bun.file(filePath);
@@ -97579,7 +97613,7 @@ Runs:
97579
97613
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
97580
97614
  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
97615
  for (const file3 of files) {
97582
- const filePath = join70(runsDir, file3);
97616
+ const filePath = join69(runsDir, file3);
97583
97617
  const summary = await extractRunSummary(filePath);
97584
97618
  const timestamp = file3.replace(".jsonl", "");
97585
97619
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -97693,7 +97727,7 @@ async function logsCommand(options) {
97693
97727
  return;
97694
97728
  }
97695
97729
  const resolved = resolveProject({ dir: options.dir });
97696
- const naxDir = join71(resolved.projectDir, ".nax");
97730
+ const naxDir = join70(resolved.projectDir, ".nax");
97697
97731
  const configPath = resolved.configPath;
97698
97732
  const configFile = Bun.file(configPath);
97699
97733
  const config2 = await configFile.json();
@@ -97701,8 +97735,8 @@ async function logsCommand(options) {
97701
97735
  if (!featureName) {
97702
97736
  throw new Error("No feature specified in config.json");
97703
97737
  }
97704
- const featureDir = join71(naxDir, "features", featureName);
97705
- const runsDir = join71(featureDir, "runs");
97738
+ const featureDir = join70(naxDir, "features", featureName);
97739
+ const runsDir = join70(featureDir, "runs");
97706
97740
  if (!existsSync28(runsDir)) {
97707
97741
  throw new Error(`No runs directory found for feature: ${featureName}`);
97708
97742
  }
@@ -97728,7 +97762,7 @@ init_prd();
97728
97762
  init_precheck();
97729
97763
  init_common();
97730
97764
  import { existsSync as existsSync29 } from "fs";
97731
- import { join as join72 } from "path";
97765
+ import { join as join71 } from "path";
97732
97766
  async function precheckCommand(options) {
97733
97767
  const resolved = resolveProject({
97734
97768
  dir: options.dir,
@@ -97750,9 +97784,9 @@ async function precheckCommand(options) {
97750
97784
  process.exit(1);
97751
97785
  }
97752
97786
  }
97753
- const naxDir = join72(resolved.projectDir, ".nax");
97754
- const featureDir = join72(naxDir, "features", featureName);
97755
- const prdPath = join72(featureDir, "prd.json");
97787
+ const naxDir = join71(resolved.projectDir, ".nax");
97788
+ const featureDir = join71(naxDir, "features", featureName);
97789
+ const prdPath = join71(featureDir, "prd.json");
97756
97790
  if (!existsSync29(featureDir)) {
97757
97791
  console.error(source_default.red(`Feature not found: ${featureName}`));
97758
97792
  process.exit(1);
@@ -97774,8 +97808,8 @@ async function precheckCommand(options) {
97774
97808
  // src/commands/runs.ts
97775
97809
  init_source();
97776
97810
  init_paths3();
97777
- import { readdir as readdir5 } from "fs/promises";
97778
- import { join as join73 } from "path";
97811
+ import { readdir as readdir4 } from "fs/promises";
97812
+ import { join as join72 } from "path";
97779
97813
  var DEFAULT_LIMIT = 20;
97780
97814
  var _runsCmdDeps = {
97781
97815
  getRunsDir
@@ -97823,14 +97857,14 @@ async function runsCommand(options = {}) {
97823
97857
  const runsDir = _runsCmdDeps.getRunsDir();
97824
97858
  let entries;
97825
97859
  try {
97826
- entries = await readdir5(runsDir);
97860
+ entries = await readdir4(runsDir);
97827
97861
  } catch {
97828
97862
  console.log("No runs found.");
97829
97863
  return;
97830
97864
  }
97831
97865
  const rows = [];
97832
97866
  for (const entry of entries) {
97833
- const metaPath = join73(runsDir, entry, "meta.json");
97867
+ const metaPath = join72(runsDir, entry, "meta.json");
97834
97868
  let meta3;
97835
97869
  try {
97836
97870
  meta3 = await Bun.file(metaPath).json();
@@ -97907,7 +97941,7 @@ async function runsCommand(options = {}) {
97907
97941
 
97908
97942
  // src/commands/unlock.ts
97909
97943
  init_source();
97910
- import { join as join74 } from "path";
97944
+ import { join as join73 } from "path";
97911
97945
  function isProcessAlive2(pid) {
97912
97946
  try {
97913
97947
  process.kill(pid, 0);
@@ -97922,7 +97956,7 @@ function formatLockAge(ageMs) {
97922
97956
  }
97923
97957
  async function unlockCommand(options) {
97924
97958
  const workdir = options.dir ?? process.cwd();
97925
- const lockPath = join74(workdir, "nax.lock");
97959
+ const lockPath = join73(workdir, "nax.lock");
97926
97960
  const lockFile = Bun.file(lockPath);
97927
97961
  const exists = await lockFile.exists();
97928
97962
  if (!exists) {
@@ -106347,7 +106381,7 @@ Next: nax generate --package ${options.package}`));
106347
106381
  }
106348
106382
  return;
106349
106383
  }
106350
- const naxDir = join88(workdir, ".nax");
106384
+ const naxDir = join87(workdir, ".nax");
106351
106385
  if (existsSync35(naxDir) && !options.force) {
106352
106386
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
106353
106387
  return;
@@ -106376,11 +106410,11 @@ Next: nax generate --package ${options.package}`));
106376
106410
  }
106377
106411
  }
106378
106412
  }
106379
- mkdirSync7(join88(naxDir, "features"), { recursive: true });
106380
- mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
106413
+ mkdirSync7(join87(naxDir, "features"), { recursive: true });
106414
+ mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
106381
106415
  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({
106416
+ await Bun.write(join87(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106417
+ await Bun.write(join87(naxDir, "hooks.json"), JSON.stringify({
106384
106418
  hooks: {
106385
106419
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
106386
106420
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -106388,12 +106422,12 @@ Next: nax generate --package ${options.package}`));
106388
106422
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
106389
106423
  }
106390
106424
  }, null, 2));
106391
- await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
106425
+ await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
106392
106426
  *.tmp
106393
106427
  .paused.json
106394
106428
  .nax-verifier-verdict.json
106395
106429
  `);
106396
- await Bun.write(join88(naxDir, "context.md"), `# Project Context
106430
+ await Bun.write(join87(naxDir, "context.md"), `# Project Context
106397
106431
 
106398
106432
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
106399
106433
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -106541,8 +106575,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
106541
106575
  console.error(source_default.red("nax not initialized. Run: nax init"));
106542
106576
  process.exit(1);
106543
106577
  }
106544
- const featureDir = join88(naxDir, "features", options.feature);
106545
- const prdPath = join88(featureDir, "prd.json");
106578
+ const featureDir = join87(naxDir, "features", options.feature);
106579
+ const prdPath = join87(featureDir, "prd.json");
106546
106580
  if (options.plan && options.from) {
106547
106581
  if (existsSync35(prdPath) && !options.force) {
106548
106582
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
@@ -106564,10 +106598,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106564
106598
  }
106565
106599
  }
106566
106600
  try {
106567
- const planLogDir = join88(featureDir, "plan");
106601
+ const planLogDir = join87(featureDir, "plan");
106568
106602
  mkdirSync7(planLogDir, { recursive: true });
106569
106603
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106570
- const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
106604
+ const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
106571
106605
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106572
106606
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106573
106607
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -106613,10 +106647,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106613
106647
  resetLogger();
106614
106648
  const projectKey = config2.name?.trim() || basename16(workdir);
106615
106649
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
106616
- const runsDir = join88(outputDir, "features", options.feature, "runs");
106650
+ const runsDir = join87(outputDir, "features", options.feature, "runs");
106617
106651
  mkdirSync7(runsDir, { recursive: true });
106618
106652
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106619
- const logFilePath = join88(runsDir, `${runId}.jsonl`);
106653
+ const logFilePath = join87(runsDir, `${runId}.jsonl`);
106620
106654
  const isTTY = process.stdout.isTTY ?? false;
106621
106655
  const headlessFlag = options.headless ?? false;
106622
106656
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -106634,7 +106668,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106634
106668
  config2.agent.default = options.agent;
106635
106669
  }
106636
106670
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
106637
- const globalNaxDir = join88(homedir3(), ".nax");
106671
+ const globalNaxDir = join87(homedir3(), ".nax");
106638
106672
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
106639
106673
  const eventEmitter = new PipelineEventEmitter;
106640
106674
  const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
@@ -106654,12 +106688,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
106654
106688
  events: eventEmitter,
106655
106689
  ptyOptions: null,
106656
106690
  agentStreamEvents,
106657
- queueFilePath: join88(workdir, ".queue.txt")
106691
+ queueFilePath: join87(workdir, ".queue.txt")
106658
106692
  });
106659
106693
  } else {
106660
106694
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
106661
106695
  }
106662
- const statusFilePath = join88(outputDir, "status.json");
106696
+ const statusFilePath = join87(outputDir, "status.json");
106663
106697
  let parallel;
106664
106698
  if (options.parallel !== undefined) {
106665
106699
  parallel = Number.parseInt(options.parallel, 10);
@@ -106686,7 +106720,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106686
106720
  skipPrecheck: options.skipPrecheck ?? false,
106687
106721
  agentStreamEvents
106688
106722
  });
106689
- const latestSymlink = join88(runsDir, "latest.jsonl");
106723
+ const latestSymlink = join87(runsDir, "latest.jsonl");
106690
106724
  try {
106691
106725
  if (existsSync35(latestSymlink)) {
106692
106726
  Bun.spawnSync(["rm", latestSymlink]);
@@ -106747,9 +106781,9 @@ features.command("create <name>").description("Create a new feature").option("-d
106747
106781
  console.error(source_default.red("nax not initialized. Run: nax init"));
106748
106782
  process.exit(1);
106749
106783
  }
106750
- const featureDir = join88(naxDir, "features", name);
106784
+ const featureDir = join87(naxDir, "features", name);
106751
106785
  mkdirSync7(featureDir, { recursive: true });
106752
- await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
106786
+ await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
106753
106787
 
106754
106788
  ## Overview
106755
106789
 
@@ -106782,7 +106816,7 @@ features.command("create <name>").description("Create a new feature").option("-d
106782
106816
 
106783
106817
  <!-- What this feature explicitly does NOT cover. -->
106784
106818
  `);
106785
- await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
106819
+ await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
106786
106820
 
106787
106821
  Created: ${new Date().toISOString()}
106788
106822
 
@@ -106808,7 +106842,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106808
106842
  console.error(source_default.red("nax not initialized."));
106809
106843
  process.exit(1);
106810
106844
  }
106811
- const featuresDir = join88(naxDir, "features");
106845
+ const featuresDir = join87(naxDir, "features");
106812
106846
  if (!existsSync35(featuresDir)) {
106813
106847
  console.log(source_default.dim("No features yet."));
106814
106848
  return;
@@ -106823,7 +106857,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106823
106857
  Features:
106824
106858
  `));
106825
106859
  for (const name of entries) {
106826
- const prdPath = join88(featuresDir, name, "prd.json");
106860
+ const prdPath = join87(featuresDir, name, "prd.json");
106827
106861
  if (existsSync35(prdPath)) {
106828
106862
  const prd = await loadPRD(prdPath);
106829
106863
  const c = countStories(prd);
@@ -106858,10 +106892,10 @@ Use: nax plan -f <feature> --from <spec>`));
106858
106892
  cliOverrides.profile = options.profile;
106859
106893
  }
106860
106894
  const config2 = await loadConfig(workdir, cliOverrides);
106861
- const featureLogDir = join88(naxDir, "features", options.feature, "plan");
106895
+ const featureLogDir = join87(naxDir, "features", options.feature, "plan");
106862
106896
  mkdirSync7(featureLogDir, { recursive: true });
106863
106897
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106864
- const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
106898
+ const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
106865
106899
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106866
106900
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106867
106901
  try {