@nathapp/nax 0.69.1 → 0.69.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +588 -464
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -16983,10 +16983,10 @@ var init_schemas_execution = __esm(() => {
16983
16983
  autoApproveVerifier: exports_external.boolean(),
16984
16984
  strategy: exports_external.enum(["auto", "strict", "lite", "off"]).default("auto"),
16985
16985
  sessionTiers: exports_external.object({
16986
- testWriter: exports_external.string().optional(),
16987
- implementer: exports_external.string().optional(),
16988
- verifier: exports_external.string().optional()
16989
- }).optional(),
16986
+ testWriter: ConfiguredModelSchema.default("fast"),
16987
+ verifier: ConfiguredModelSchema.default("fast"),
16988
+ implementer: ConfiguredModelSchema.optional()
16989
+ }).default({ testWriter: "fast", verifier: "fast" }),
16990
16990
  testWriterAllowedPaths: exports_external.array(exports_external.string()).optional(),
16991
16991
  rollbackOnFailure: exports_external.boolean().optional(),
16992
16992
  greenfieldDetection: exports_external.boolean().optional()
@@ -17413,7 +17413,7 @@ var init_schemas3 = __esm(() => {
17413
17413
  autoApproveVerifier: true,
17414
17414
  strategy: "auto",
17415
17415
  sessionTiers: {
17416
- testWriter: "balanced",
17416
+ testWriter: "fast",
17417
17417
  verifier: "fast"
17418
17418
  },
17419
17419
  testWriterAllowedPaths: ["src/index.ts", "src/**/index.ts"],
@@ -18946,6 +18946,14 @@ ${errors3.join(`
18946
18946
  }
18947
18947
  return result.data;
18948
18948
  }
18949
+ async function loadPackageOverride(repoRoot, packageDir) {
18950
+ const packageConfigPath = join3(repoRoot, PROJECT_NAX_DIR, "mono", packageDir, "config.json");
18951
+ const override = await loadJsonFile(packageConfigPath, "config");
18952
+ if (!override)
18953
+ return null;
18954
+ const { profile: _profile, ...fields } = override;
18955
+ return fields;
18956
+ }
18949
18957
  async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
18950
18958
  const logger = getLogger();
18951
18959
  const resolvedRootConfigPath = resolve3(rootConfigPath);
@@ -19416,8 +19424,10 @@ __export(exports_config, {
19416
19424
  precheckConfigSelector: () => precheckConfigSelector,
19417
19425
  planConfigSelector: () => planConfigSelector,
19418
19426
  pickSelector: () => pickSelector,
19427
+ mergePackageConfig: () => mergePackageConfig,
19419
19428
  loadProfileEnv: () => loadProfileEnv,
19420
19429
  loadProfile: () => loadProfile,
19430
+ loadPackageOverride: () => loadPackageOverride,
19421
19431
  loadConfigForWorkdir: () => loadConfigForWorkdir,
19422
19432
  loadConfig: () => loadConfig,
19423
19433
  llmRoutingConfigSelector: () => llmRoutingConfigSelector,
@@ -19442,6 +19452,7 @@ __export(exports_config, {
19442
19452
  acceptanceFixConfigSelector: () => acceptanceFixConfigSelector,
19443
19453
  acceptanceConfigSelector: () => acceptanceConfigSelector,
19444
19454
  VALID_TEST_STRATEGIES: () => VALID_TEST_STRATEGIES,
19455
+ TddConfigSchema: () => TddConfigSchema,
19445
19456
  THREE_SESSION_STRATEGIES: () => THREE_SESSION_STRATEGIES,
19446
19457
  TEST_STRATEGY_GUIDE: () => TEST_STRATEGY_GUIDE,
19447
19458
  SPEC_ANCHOR_RULES: () => SPEC_ANCHOR_RULES,
@@ -19463,6 +19474,7 @@ var init_config = __esm(() => {
19463
19474
  init_schema();
19464
19475
  init_schemas_model();
19465
19476
  init_schemas_debate();
19477
+ init_schemas_execution();
19466
19478
  init_loader();
19467
19479
  init_path_security();
19468
19480
  init_paths();
@@ -23959,6 +23971,7 @@ var init_detect = __esm(() => {
23959
23971
  });
23960
23972
 
23961
23973
  // src/test-runners/detect/workspace.ts
23974
+ import { join as join6 } from "path";
23962
23975
  async function expandWorkspaceGlob(workdir, pattern) {
23963
23976
  const dirs = [];
23964
23977
  try {
@@ -24032,15 +24045,31 @@ async function detectTurboOrNx(workdir) {
24032
24045
  }
24033
24046
  async function detectNaxMonoLayout(workdir) {
24034
24047
  const dirs = [];
24035
- try {
24036
- const glob = new Bun.Glob(".nax/mono/*/config.json");
24037
- for await (const entry of glob.scan({ cwd: workdir })) {
24038
- const parts = entry.split("/");
24039
- if (parts.length >= 4) {
24040
- const pkgDir = parts.slice(2, -1).join("/");
24041
- dirs.push(pkgDir);
24048
+ const monoRoot = join6(workdir, ".nax", "mono");
24049
+ async function walk(currentDir, relativeParts) {
24050
+ const entries = [];
24051
+ for await (const entry of _workspaceDeps.glob("*", currentDir)) {
24052
+ entries.push(entry);
24053
+ }
24054
+ let hasConfig = false;
24055
+ for (const entry of entries) {
24056
+ const entryPath = join6(currentDir, entry);
24057
+ const stat = await _workspaceDeps.stat(entryPath);
24058
+ const isDirectory = (stat.mode & 61440) === 16384;
24059
+ if (!isDirectory && entry === "config.json") {
24060
+ hasConfig = true;
24061
+ continue;
24062
+ }
24063
+ if (isDirectory) {
24064
+ await walk(entryPath, [...relativeParts, entry]);
24042
24065
  }
24043
24066
  }
24067
+ if (hasConfig && relativeParts.length > 0) {
24068
+ dirs.push(relativeParts.join("/"));
24069
+ }
24070
+ }
24071
+ try {
24072
+ await walk(monoRoot, []);
24044
24073
  } catch {}
24045
24074
  return dirs;
24046
24075
  }
@@ -24084,7 +24113,8 @@ var init_workspace = __esm(() => {
24084
24113
  return f.text();
24085
24114
  },
24086
24115
  spawn: Bun.spawn,
24087
- glob: (pattern, cwd) => new Bun.Glob(pattern).scan({ cwd, onlyFiles: false })
24116
+ glob: (pattern, cwd) => new Bun.Glob(pattern).scan({ cwd, onlyFiles: false }),
24117
+ stat: (path) => Bun.file(path).stat()
24088
24118
  };
24089
24119
  _workspaceCache = new Map;
24090
24120
  });
@@ -24158,7 +24188,7 @@ var init_detector2 = __esm(() => {
24158
24188
  });
24159
24189
 
24160
24190
  // src/test-runners/resolver.ts
24161
- import { dirname as dirname2, isAbsolute as isAbsolute3, join as join6, relative, resolve as resolve4 } from "path";
24191
+ import { dirname as dirname2, isAbsolute as isAbsolute3, join as join7, relative, resolve as resolve4 } from "path";
24162
24192
  function buildResolved(globs, resolution) {
24163
24193
  return {
24164
24194
  globs,
@@ -24210,7 +24240,7 @@ async function resolveTestFilePatterns(config2, workdir, packageDir, options) {
24210
24240
  validateGlobs(rootPatterns, "resolver");
24211
24241
  return buildResolved(rootPatterns, "root-config");
24212
24242
  }
24213
- const detectionWorkdir = packageDir ? join6(workdir, packageDir) : workdir;
24243
+ const detectionWorkdir = packageDir ? join7(workdir, packageDir) : workdir;
24214
24244
  const detected = await _resolverDeps.detectTestFilePatterns(detectionWorkdir);
24215
24245
  if (detected.confidence !== "empty" && detected.patterns.length > 0) {
24216
24246
  getSafeLogger()?.info("resolver", "Test patterns auto-detected", {
@@ -24789,7 +24819,7 @@ var init_git = __esm(() => {
24789
24819
  });
24790
24820
 
24791
24821
  // src/utils/path-filters.ts
24792
- import { join as join7, relative as relative2 } from "path";
24822
+ import { join as join8, relative as relative2 } from "path";
24793
24823
  function basename3(path) {
24794
24824
  const stripped = path.startsWith("./") ? path.slice(2) : path;
24795
24825
  const idx = stripped.lastIndexOf("/");
@@ -24879,8 +24909,8 @@ async function resolveNaxIgnorePatterns(repoRoot, packageDir) {
24879
24909
  const normalizedRepoRoot = normalizePath(repoRoot);
24880
24910
  const normalizedPackageDir = packageDir ? normalizePath(packageDir) : normalizedRepoRoot;
24881
24911
  const packagePrefix = normalizedPackageDir !== normalizedRepoRoot ? normalizePath(relative2(repoRoot, packageDir ?? repoRoot)) : null;
24882
- const rootFile = join7(repoRoot, NAX_IGNORE_FILENAME);
24883
- const packageFile = join7(packageDir ?? repoRoot, NAX_IGNORE_FILENAME);
24912
+ const rootFile = join8(repoRoot, NAX_IGNORE_FILENAME);
24913
+ const packageFile = join8(packageDir ?? repoRoot, NAX_IGNORE_FILENAME);
24884
24914
  const rootPatterns = await readIgnorePatterns(rootFile);
24885
24915
  const packagePatterns = packageDir && packageDir !== repoRoot ? await readIgnorePatterns(packageFile) : [];
24886
24916
  return [
@@ -24949,7 +24979,7 @@ var init_path_filters = __esm(() => {
24949
24979
  });
24950
24980
 
24951
24981
  // src/verification/smart-runner.ts
24952
- import { join as join8, relative as relative3 } from "path";
24982
+ import { join as join9, relative as relative3 } from "path";
24953
24983
  function clearGitRootCache() {
24954
24984
  _gitRootCache.clear();
24955
24985
  }
@@ -25067,7 +25097,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
25067
25097
  const lines = stdout.trim().split(`
25068
25098
  `).filter(Boolean);
25069
25099
  const effectiveRepoRoot = repoRoot ?? workdir;
25070
- const packageDir = packagePrefix ? join8(effectiveRepoRoot, packagePrefix) : undefined;
25100
+ const packageDir = packagePrefix ? join9(effectiveRepoRoot, packagePrefix) : undefined;
25071
25101
  const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(effectiveRepoRoot, packageDir);
25072
25102
  let effectivePrefix = packagePrefix;
25073
25103
  if (packagePrefix && repoRoot) {
@@ -25096,7 +25126,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
25096
25126
  return [];
25097
25127
  const lines = stdout.trim().split(`
25098
25128
  `).filter(Boolean);
25099
- const packageDir = packagePrefix ? join8(repoRoot, packagePrefix) : undefined;
25129
+ const packageDir = packagePrefix ? join9(repoRoot, packagePrefix) : undefined;
25100
25130
  const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
25101
25131
  const gitRoot = await getGitRootMemo(workdir);
25102
25132
  const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative3(gitRoot, repoRoot) : "";
@@ -25104,7 +25134,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
25104
25134
  const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
25105
25135
  const scoped = filterNaxInternalPaths(scopedRaw, ignoreMatchers);
25106
25136
  const stripped = extraPrefix ? scoped.map((f) => f.slice(`${extraPrefix}/`.length)) : scoped;
25107
- return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) => join8(repoRoot, f));
25137
+ return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) => join9(repoRoot, f));
25108
25138
  } catch {
25109
25139
  return [];
25110
25140
  }
@@ -25236,6 +25266,7 @@ __export(exports_test_runners, {
25236
25266
  formatFailureSummary: () => formatFailureSummary,
25237
25267
  findPackageDir: () => findPackageDir,
25238
25268
  extractTestDirs: () => extractTestDirs,
25269
+ discoverWorkspacePackages: () => discoverWorkspacePackages,
25239
25270
  detectTestFilePatterns: () => detectTestFilePatterns,
25240
25271
  detectManifestFrameworksFromPackageJson: () => detectManifestFrameworksFromPackageJson,
25241
25272
  detectFramework: () => detectFramework,
@@ -25257,6 +25288,7 @@ var init_test_runners = __esm(() => {
25257
25288
  init_detector2();
25258
25289
  init_resolver();
25259
25290
  init_parser();
25291
+ init_workspace();
25260
25292
  init_ac_parser();
25261
25293
  init_scoped_selection();
25262
25294
  });
@@ -25308,7 +25340,7 @@ var init_project = __esm(() => {
25308
25340
 
25309
25341
  // src/utils/path-security.ts
25310
25342
  import { realpathSync as realpathSync2 } from "fs";
25311
- import { dirname as dirname3, isAbsolute as isAbsolute4, join as join9, normalize as normalize3, resolve as resolve5 } from "path";
25343
+ import { dirname as dirname3, isAbsolute as isAbsolute4, join as join10, normalize as normalize3, resolve as resolve5 } from "path";
25312
25344
  function safeRealpathForComparison(p) {
25313
25345
  try {
25314
25346
  return realpathSync2(p);
@@ -25317,7 +25349,7 @@ function safeRealpathForComparison(p) {
25317
25349
  if (parent === p)
25318
25350
  return normalize3(p);
25319
25351
  const resolvedParent = safeRealpathForComparison(parent);
25320
- return join9(resolvedParent, p.split("/").pop() ?? "");
25352
+ return join10(resolvedParent, p.split("/").pop() ?? "");
25321
25353
  }
25322
25354
  }
25323
25355
  function isRelativeAndSafe(filePath) {
@@ -25345,7 +25377,7 @@ function validateModulePath(modulePath, allowedRoots) {
25345
25377
  } else {
25346
25378
  for (let i = 0;i < allowedRoots.length; i++) {
25347
25379
  const originalRoot = resolve5(allowedRoots[i]);
25348
- const absoluteInput = resolve5(join9(originalRoot, modulePath));
25380
+ const absoluteInput = resolve5(join10(originalRoot, modulePath));
25349
25381
  const resolved = safeRealpathForComparison(absoluteInput);
25350
25382
  const resolvedRoot = resolvedRoots[i];
25351
25383
  if (resolved.startsWith(`${resolvedRoot}/`) || resolved === resolvedRoot) {
@@ -25362,7 +25394,7 @@ var init_path_security2 = () => {};
25362
25394
 
25363
25395
  // src/context/engine/providers/code-neighbor.ts
25364
25396
  import { createHash as createHash3 } from "crypto";
25365
- import { join as join10, relative as relative4, resolve as resolve6 } from "path";
25397
+ import { join as join11, relative as relative4, resolve as resolve6 } from "path";
25366
25398
  function isExcludedPath(file3, ignoreMatchers) {
25367
25399
  for (const prefix of EXCLUDED_DIR_PREFIXES2) {
25368
25400
  if (file3.startsWith(prefix) || file3.includes(`/${prefix}`))
@@ -25500,7 +25532,7 @@ async function readCached(absolutePath, cache) {
25500
25532
  async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, siblingTestContext) {
25501
25533
  const neighbors = new Set;
25502
25534
  let anyTruncated = false;
25503
- const ownAbsPath = join10(workdir, filePath);
25535
+ const ownAbsPath = join11(workdir, filePath);
25504
25536
  if (await _codeNeighborDeps.fileExists(ownAbsPath)) {
25505
25537
  const ownContent = await readCached(ownAbsPath, contentCache);
25506
25538
  if (ownContent !== null && ownContent.length > 0) {
@@ -25522,7 +25554,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
25522
25554
  break outer;
25523
25555
  if (srcFile === filePath)
25524
25556
  continue;
25525
- const content = await readCached(join10(scanWorkdir, srcFile), contentCache);
25557
+ const content = await readCached(join11(scanWorkdir, srcFile), contentCache);
25526
25558
  if (content?.includes(fileBaseName)) {
25527
25559
  for (const spec of parseImportSpecifiers(content)) {
25528
25560
  const resolved = resolveImport(spec, srcFile, scanWorkdir);
@@ -25538,7 +25570,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
25538
25570
  const candidates = deriveSiblingTestCandidates(filePath, siblingTestContext.globs);
25539
25571
  let chosen = null;
25540
25572
  for (const candidate of candidates) {
25541
- if (await _codeNeighborDeps.fileExists(join10(workdir, candidate))) {
25573
+ if (await _codeNeighborDeps.fileExists(join11(workdir, candidate))) {
25542
25574
  chosen = candidate;
25543
25575
  break;
25544
25576
  }
@@ -25562,7 +25594,7 @@ async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRo
25562
25594
  const relPkgDirs = await _codeNeighborDeps.discoverWorkspacePackages(repoRoot);
25563
25595
  if (relPkgDirs.length === 0)
25564
25596
  return [repoRoot];
25565
- return relPkgDirs.map((rel) => join10(repoRoot, rel)).filter((abs) => abs !== packageDir);
25597
+ return relPkgDirs.map((rel) => join11(repoRoot, rel)).filter((abs) => abs !== packageDir);
25566
25598
  } catch {
25567
25599
  return [repoRoot];
25568
25600
  }
@@ -26859,7 +26891,7 @@ var init_orchestrator = __esm(() => {
26859
26891
  });
26860
26892
 
26861
26893
  // src/context/rules/canonical-loader.ts
26862
- import { basename as basename4, join as join11 } from "path";
26894
+ import { basename as basename4, join as join12 } from "path";
26863
26895
  function parseRuleAllowMarker(line) {
26864
26896
  const allowed = new Set;
26865
26897
  RULE_ALLOW_MARKER.lastIndex = 0;
@@ -26994,7 +27026,7 @@ function applyCanonicalRulesBudget(rules, budgetTokens) {
26994
27026
  }
26995
27027
  async function loadCanonicalRules(workdir, options = {}) {
26996
27028
  const logger = _canonicalLoaderDeps.getLogger();
26997
- const rulesDir = join11(workdir, CANONICAL_RULES_DIR);
27029
+ const rulesDir = join12(workdir, CANONICAL_RULES_DIR);
26998
27030
  const allFilePaths = _canonicalLoaderDeps.globInDir(rulesDir);
26999
27031
  const filePaths = allFilePaths.filter((filePath) => {
27000
27032
  const normalized = filePath.replaceAll("\\", "/");
@@ -27095,7 +27127,7 @@ var init_canonical_loader = __esm(() => {
27095
27127
  for (const rel of files) {
27096
27128
  const depth = rel.split("/").length - 1;
27097
27129
  if (depth <= 1) {
27098
- kept.push(join11(dir, rel));
27130
+ kept.push(join12(dir, rel));
27099
27131
  } else {
27100
27132
  ignored.push(rel);
27101
27133
  }
@@ -27377,7 +27409,7 @@ var init_session_scratch = __esm(() => {
27377
27409
 
27378
27410
  // src/context/engine/providers/static-rules.ts
27379
27411
  import { createHash as createHash8 } from "crypto";
27380
- import { join as join12, relative as relative5 } from "path";
27412
+ import { join as join13, relative as relative5 } from "path";
27381
27413
  function contentHash85(content) {
27382
27414
  return createHash8("sha256").update(content).digest("hex").slice(0, 8);
27383
27415
  }
@@ -27585,7 +27617,7 @@ ${rule.content}`,
27585
27617
  const existingCandidates = [];
27586
27618
  for (const fileName of LEGACY_CANDIDATE_FILES) {
27587
27619
  try {
27588
- if (await _staticRulesDeps.fileExists(join12(rootDir, fileName))) {
27620
+ if (await _staticRulesDeps.fileExists(join13(rootDir, fileName))) {
27589
27621
  existingCandidates.push(fileName);
27590
27622
  }
27591
27623
  } catch {}
@@ -27601,11 +27633,11 @@ ${rule.content}`,
27601
27633
  for (const fileName of LEGACY_CANDIDATE_FILES) {
27602
27634
  legacySources.push({
27603
27635
  sourceId: fileName,
27604
- filePath: join12(rootDir, fileName),
27636
+ filePath: join13(rootDir, fileName),
27605
27637
  heading: fileName
27606
27638
  });
27607
27639
  }
27608
- const rulesDir = join12(rootDir, LEGACY_RULES_DIR);
27640
+ const rulesDir = join13(rootDir, LEGACY_RULES_DIR);
27609
27641
  const nestedRulePaths = _staticRulesDeps.globInDir(rulesDir);
27610
27642
  for (const filePath of nestedRulePaths) {
27611
27643
  const normalized = normalizePath2(filePath);
@@ -27664,7 +27696,7 @@ var init_static_rules = __esm(() => {
27664
27696
  fileExists: async (path) => Bun.file(path).exists(),
27665
27697
  globInDir: (dir) => {
27666
27698
  try {
27667
- return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) => join12(dir, f));
27699
+ return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) => join13(dir, f));
27668
27700
  } catch {
27669
27701
  return [];
27670
27702
  }
@@ -28003,7 +28035,7 @@ function validateStory(raw, index, allIds) {
28003
28035
  routing: {
28004
28036
  complexity,
28005
28037
  testStrategy,
28006
- reasoning: "validated from LLM output",
28038
+ reasoning: typeof routing.reasoning === "string" && routing.reasoning.trim().length > 0 ? routing.reasoning.trim() : "validated from LLM output",
28007
28039
  ...noTestJustification !== undefined ? { noTestJustification } : {}
28008
28040
  },
28009
28041
  ...workdir !== undefined ? { workdir } : {},
@@ -28554,7 +28586,7 @@ var init_orchestrator_factory = __esm(() => {
28554
28586
  });
28555
28587
 
28556
28588
  // src/context/engine/providers/plugin-loader.ts
28557
- import { isAbsolute as isAbsolute5, join as join13, resolve as resolve7 } from "path";
28589
+ import { isAbsolute as isAbsolute5, join as join14, resolve as resolve7 } from "path";
28558
28590
  function isInitialisable(p) {
28559
28591
  return typeof p.init === "function";
28560
28592
  }
@@ -28582,7 +28614,7 @@ function resolveModuleSpecifier(specifier, workdir) {
28582
28614
  }
28583
28615
  if (specifier.startsWith("./") || specifier.startsWith("../")) {
28584
28616
  const resolvedWorkdir = resolve7(workdir);
28585
- const resolved = resolve7(join13(workdir, specifier));
28617
+ const resolved = resolve7(join14(workdir, specifier));
28586
28618
  if (resolved !== resolvedWorkdir && !resolved.startsWith(`${resolvedWorkdir}/`)) {
28587
28619
  throw new Error(`Plugin module path escapes project workdir: "${specifier}" resolves to "${resolved}" (workdir: "${resolvedWorkdir}")`);
28588
28620
  }
@@ -28728,15 +28760,15 @@ var init_available_budget = __esm(() => {
28728
28760
 
28729
28761
  // src/context/engine/manifest-store.ts
28730
28762
  import { mkdir as mkdir2 } from "fs/promises";
28731
- import { dirname as dirname5, isAbsolute as isAbsolute6, join as join14, relative as relative7, resolve as resolve8 } from "path";
28763
+ import { dirname as dirname5, isAbsolute as isAbsolute6, join as join15, relative as relative7, resolve as resolve8 } from "path";
28732
28764
  function contextStoryDir(projectDir, featureId, storyId) {
28733
- return join14(projectDir, ".nax", "features", featureId, "stories", storyId);
28765
+ return join15(projectDir, ".nax", "features", featureId, "stories", storyId);
28734
28766
  }
28735
28767
  function contextManifestPath(projectDir, featureId, storyId, stage) {
28736
- return join14(contextStoryDir(projectDir, featureId, storyId), `context-manifest-${stage}.json`);
28768
+ return join15(contextStoryDir(projectDir, featureId, storyId), `context-manifest-${stage}.json`);
28737
28769
  }
28738
28770
  function rebuildManifestPath(projectDir, featureId, storyId) {
28739
- return join14(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
28771
+ return join15(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
28740
28772
  }
28741
28773
  function toStoredPath(projectDir, pathValue) {
28742
28774
  const relativePath = isAbsolute6(pathValue) ? relative7(projectDir, pathValue) : pathValue;
@@ -28792,7 +28824,7 @@ async function loadContextManifests(projectDir, storyId, featureId) {
28792
28824
  const storyDir = contextStoryDir(projectDir, feature, storyId);
28793
28825
  const manifestFiles = await _manifestStoreDeps.listManifestFiles(storyDir);
28794
28826
  for (const fileName of manifestFiles) {
28795
- const fullPath = join14(storyDir, fileName);
28827
+ const fullPath = join15(storyDir, fileName);
28796
28828
  if (!await _manifestStoreDeps.fileExists(fullPath))
28797
28829
  continue;
28798
28830
  try {
@@ -28817,7 +28849,7 @@ var init_manifest_store = __esm(() => {
28817
28849
  fileExists: (path2) => Bun.file(path2).exists(),
28818
28850
  readFile: (path2) => Bun.file(path2).text(),
28819
28851
  listFeatureDirs: async (projectDir) => {
28820
- const baseDir = join14(projectDir, ".nax", "features");
28852
+ const baseDir = join15(projectDir, ".nax", "features");
28821
28853
  try {
28822
28854
  const dirs = [];
28823
28855
  for await (const entry of new Bun.Glob("*").scan({ cwd: baseDir, absolute: false })) {
@@ -28844,7 +28876,7 @@ var init_manifest_store = __esm(() => {
28844
28876
 
28845
28877
  // src/context/engine/stage-assembler.ts
28846
28878
  import { readdir } from "fs/promises";
28847
- import { isAbsolute as isAbsolute7, join as join15, resolve as resolve9 } from "path";
28879
+ import { isAbsolute as isAbsolute7, join as join16, resolve as resolve9 } from "path";
28848
28880
  function dedupeScratchDirs(dirs) {
28849
28881
  return [...new Set(dirs.filter((dir) => Boolean(dir)))];
28850
28882
  }
@@ -28853,7 +28885,7 @@ function toAbsolutePath2(projectDir, pathValue) {
28853
28885
  }
28854
28886
  async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId, ttlMs) {
28855
28887
  const logger = getLogger();
28856
- const sessionsRoot = join15(projectDir, ".nax", "features", featureName, "sessions");
28888
+ const sessionsRoot = join16(projectDir, ".nax", "features", featureName, "sessions");
28857
28889
  let entries;
28858
28890
  try {
28859
28891
  entries = await _stageAssemblerDeps.readdir(sessionsRoot);
@@ -28863,7 +28895,7 @@ async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId
28863
28895
  const cutoff = _stageAssemblerDeps.now() - ttlMs;
28864
28896
  const found = [];
28865
28897
  for (const entry of entries) {
28866
- const descriptorPath = join15(sessionsRoot, entry, "descriptor.json");
28898
+ const descriptorPath = join16(sessionsRoot, entry, "descriptor.json");
28867
28899
  try {
28868
28900
  const parsed = await _stageAssemblerDeps.readDescriptor(descriptorPath);
28869
28901
  if (!parsed || parsed.storyId !== storyId || !parsed.scratchDir)
@@ -30528,13 +30560,13 @@ var exports_loader = {};
30528
30560
  __export(exports_loader, {
30529
30561
  loadOverride: () => loadOverride
30530
30562
  });
30531
- import { join as join16 } from "path";
30563
+ import { join as join17 } from "path";
30532
30564
  async function loadOverride(role, workdir, config2) {
30533
30565
  const overridePath = config2.prompts?.overrides?.[role];
30534
30566
  if (!overridePath) {
30535
30567
  return null;
30536
30568
  }
30537
- const absolutePath = join16(workdir, overridePath);
30569
+ const absolutePath = join17(workdir, overridePath);
30538
30570
  const file3 = Bun.file(absolutePath);
30539
30571
  if (!await file3.exists()) {
30540
30572
  return null;
@@ -33201,11 +33233,11 @@ var init_prepare_inputs = __esm(() => {
33201
33233
  });
33202
33234
 
33203
33235
  // src/utils/nax-project-root.ts
33204
- import { dirname as dirname6, join as join17, resolve as resolve10 } from "path";
33236
+ import { dirname as dirname6, join as join18, resolve as resolve10 } from "path";
33205
33237
  async function findNaxProjectRoot(startDir) {
33206
33238
  let dir = resolve10(startDir);
33207
33239
  for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
33208
- if (await _naxProjectRootDeps.exists(join17(dir, ".nax", "config.json"))) {
33240
+ if (await _naxProjectRootDeps.exists(join18(dir, ".nax", "config.json"))) {
33209
33241
  return dir;
33210
33242
  }
33211
33243
  const parent = dirname6(dir);
@@ -33226,7 +33258,7 @@ var init_nax_project_root = __esm(() => {
33226
33258
 
33227
33259
  // src/review/review-audit.ts
33228
33260
  import { mkdir as mkdir3 } from "fs/promises";
33229
- import { join as join18 } from "path";
33261
+ import { join as join19 } from "path";
33230
33262
  function auditKey(reviewer, storyId) {
33231
33263
  return `${reviewer}:${storyId ?? "_feature"}`;
33232
33264
  }
@@ -33259,15 +33291,15 @@ function toPersistedEntry(entry, epochMs) {
33259
33291
  async function persistReviewAudit(entry) {
33260
33292
  let resolvedDir;
33261
33293
  if (entry.outputDir) {
33262
- resolvedDir = join18(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
33294
+ resolvedDir = join19(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
33263
33295
  } else {
33264
33296
  const projectRoot = entry.projectDir ?? await _reviewAuditDeps.findNaxProjectRoot(entry.workdir);
33265
- resolvedDir = join18(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
33297
+ resolvedDir = join19(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
33266
33298
  }
33267
33299
  await _reviewAuditDeps.mkdir(resolvedDir);
33268
33300
  const epochMs = _reviewAuditDeps.now();
33269
33301
  const filename = `${epochMs}-${entry.sessionName}.json`;
33270
- await _reviewAuditDeps.writeFile(join18(resolvedDir, filename), toPersistedEntry(entry, epochMs));
33302
+ await _reviewAuditDeps.writeFile(join19(resolvedDir, filename), toPersistedEntry(entry, epochMs));
33271
33303
  }
33272
33304
  function createNoOpReviewAuditor() {
33273
33305
  return {
@@ -33868,6 +33900,17 @@ function createDrainDeadline(deadlineMs) {
33868
33900
  }
33869
33901
  async function runQualityCommand(opts) {
33870
33902
  const { commandName, command, workdir, storyId, timeoutMs = DEFAULT_TIMEOUT_MS, env: env2, stripEnvVars } = opts;
33903
+ if (!command || command.trim() === "") {
33904
+ return {
33905
+ commandName,
33906
+ command,
33907
+ success: false,
33908
+ exitCode: -1,
33909
+ output: `[nax] ${commandName} skipped: empty command`,
33910
+ durationMs: 0,
33911
+ timedOut: false
33912
+ };
33913
+ }
33871
33914
  const startTime = Date.now();
33872
33915
  const logger = getSafeLogger();
33873
33916
  logger?.info("quality", `Running ${commandName}`, { storyId, commandName, command, workdir });
@@ -33982,7 +34025,7 @@ __export(exports_command_resolver, {
33982
34025
  resolveQualityTestCommands: () => resolveQualityTestCommands,
33983
34026
  _commandResolverDeps: () => _commandResolverDeps
33984
34027
  });
33985
- import { join as join19 } from "path";
34028
+ import { join as join20 } from "path";
33986
34029
  async function resolveQualityTestCommands(config2, workdir, storyWorkdir) {
33987
34030
  const rawTestCommand = config2.review?.commands?.test ?? config2.quality?.commands?.test;
33988
34031
  const rawScopedTemplate = config2.quality?.commands?.testScoped;
@@ -34009,7 +34052,7 @@ var init_command_resolver = __esm(() => {
34009
34052
  _commandResolverDeps = {
34010
34053
  readPackageName: async (dir) => {
34011
34054
  try {
34012
- const content = await Bun.file(join19(dir, "package.json")).json();
34055
+ const content = await Bun.file(join20(dir, "package.json")).json();
34013
34056
  return typeof content.name === "string" ? content.name : null;
34014
34057
  } catch {
34015
34058
  return null;
@@ -34458,7 +34501,7 @@ ${outputFormat}`, overridable: false }
34458
34501
  });
34459
34502
 
34460
34503
  // src/operations/plan-refine.ts
34461
- import { join as join20 } from "path";
34504
+ import { join as join21 } from "path";
34462
34505
  function hasToken(text, tokens) {
34463
34506
  const lower = text.toLowerCase();
34464
34507
  return tokens.some((token) => lower.includes(token));
@@ -34550,7 +34593,7 @@ async function normalizeStoryFiles(story, workdir, fileExists, upstreamProduced)
34550
34593
  for (const entry of contextFiles) {
34551
34594
  const filePath = typeof entry === "string" ? entry : entry.path;
34552
34595
  const factId = typeof entry === "string" ? undefined : entry.factId;
34553
- if (expected.has(filePath) || await fileExists(join20(workdir, filePath))) {
34596
+ if (expected.has(filePath) || await fileExists(join21(workdir, filePath))) {
34554
34597
  kept.push(entry);
34555
34598
  continue;
34556
34599
  }
@@ -35702,11 +35745,11 @@ function extractTestCode(output) {
35702
35745
 
35703
35746
  // src/acceptance/generator.ts
35704
35747
  import { existsSync as existsSync5 } from "fs";
35705
- import { join as join21 } from "path";
35748
+ import { join as join22 } from "path";
35706
35749
  function resolvePytestBin(packageDir) {
35707
35750
  if (packageDir) {
35708
35751
  for (const venvDir of [".venv", "venv", "env"]) {
35709
- const candidate = join21(packageDir, venvDir, "bin", "pytest");
35752
+ const candidate = join22(packageDir, venvDir, "bin", "pytest");
35710
35753
  if (existsSync5(candidate))
35711
35754
  return candidate;
35712
35755
  }
@@ -37292,6 +37335,7 @@ var init_write_test = __esm(() => {
37292
37335
  stage: "run",
37293
37336
  session: { role: "test-writer", lifetime: "warm" },
37294
37337
  config: tddConfigSelector,
37338
+ model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.testWriter,
37295
37339
  keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "test-writer"),
37296
37340
  build(input, _ctx) {
37297
37341
  if (input.promptMarkdown?.trim()) {
@@ -37352,6 +37396,7 @@ var init_implement = __esm(() => {
37352
37396
  stage: "run",
37353
37397
  session: { role: "implementer", lifetime: "warm" },
37354
37398
  config: tddConfigSelector,
37399
+ model: (input) => input.story.routing?.modelTier,
37355
37400
  keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "implementer"),
37356
37401
  build(input, _ctx) {
37357
37402
  if (input.promptMarkdown?.trim()) {
@@ -37749,6 +37794,7 @@ var init_verify = __esm(() => {
37749
37794
  stage: "verify",
37750
37795
  session: { role: "verifier", lifetime: "fresh" },
37751
37796
  config: tddConfigSelector,
37797
+ model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.verifier,
37752
37798
  retry: makeParseRetryStrategy({
37753
37799
  validate: (parsed) => {
37754
37800
  if (!parsed || typeof parsed !== "object")
@@ -38315,14 +38361,14 @@ var init_plan_critic_llm = __esm(() => {
38315
38361
 
38316
38362
  // src/context/greenfield.ts
38317
38363
  import { readdir as readdir2 } from "fs/promises";
38318
- import { join as join22 } from "path";
38364
+ import { join as join23 } from "path";
38319
38365
  async function scanForTestFiles(dir, testPatterns, isRootCall = true) {
38320
38366
  const results = [];
38321
38367
  const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
38322
38368
  try {
38323
38369
  const entries = await readdir2(dir, { withFileTypes: true });
38324
38370
  for (const entry of entries) {
38325
- const fullPath = join22(dir, entry.name);
38371
+ const fullPath = join23(dir, entry.name);
38326
38372
  if (entry.isDirectory()) {
38327
38373
  if (ignoreDirs.has(entry.name))
38328
38374
  continue;
@@ -38527,13 +38573,13 @@ __export(exports_runners, {
38527
38573
  _regressionRunnerDeps: () => _regressionRunnerDeps
38528
38574
  });
38529
38575
  import { existsSync as existsSync6 } from "fs";
38530
- import { join as join23 } from "path";
38576
+ import { join as join24 } from "path";
38531
38577
  async function verifyAssets(workingDirectory, expectedFiles) {
38532
38578
  if (!expectedFiles || expectedFiles.length === 0)
38533
38579
  return { success: true, missingFiles: [] };
38534
38580
  const missingFiles = [];
38535
38581
  for (const file3 of expectedFiles) {
38536
- if (!existsSync6(join23(workingDirectory, file3)))
38582
+ if (!existsSync6(join24(workingDirectory, file3)))
38537
38583
  missingFiles.push(file3);
38538
38584
  }
38539
38585
  if (missingFiles.length > 0) {
@@ -38659,7 +38705,7 @@ var init_full_suite_gate = __esm(() => {
38659
38705
  _fullSuiteGateDeps = {
38660
38706
  resolveGateContext: async (input, ctx) => {
38661
38707
  const { resolveQualityTestCommands: resolveQualityTestCommands2 } = await Promise.resolve().then(() => (init_command_resolver(), exports_command_resolver));
38662
- const config2 = ctx.runtime.configLoader.current();
38708
+ const config2 = ctx.packageView.config;
38663
38709
  const fullSuiteTimeout = config2.execution?.regressionGate?.timeoutSeconds ?? config2.execution?.rectification?.fullSuiteTimeoutSeconds ?? 300;
38664
38710
  const { testCommand: resolvedTestCmd } = await resolveQualityTestCommands2(config2, input.workdir, input.story.workdir);
38665
38711
  if (!resolvedTestCmd) {
@@ -38671,13 +38717,14 @@ var init_full_suite_gate = __esm(() => {
38671
38717
  workdir: input.workdir
38672
38718
  });
38673
38719
  }
38674
- return { config: config2, testCmd: resolvedTestCmd, fullSuiteTimeout };
38720
+ const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
38721
+ return { config: config2, testCmd: resolvedTestCmd, fullSuiteTimeout, cmdWorkdir };
38675
38722
  },
38676
- runTests: async (input, gateCtx) => {
38723
+ runTests: async (_input, gateCtx) => {
38677
38724
  const { regression: regression2 } = await Promise.resolve().then(() => (init_runners(), exports_runners));
38678
38725
  const { parseTestOutput: parseTestOutput2 } = await Promise.resolve().then(() => (init_test_runners(), exports_test_runners));
38679
38726
  const result = await regression2({
38680
- workdir: input.workdir,
38727
+ workdir: gateCtx.cmdWorkdir,
38681
38728
  command: gateCtx.testCmd,
38682
38729
  timeoutSeconds: gateCtx.fullSuiteTimeout,
38683
38730
  acceptOnTimeout: false,
@@ -38708,7 +38755,7 @@ var init_full_suite_gate = __esm(() => {
38708
38755
  config: fullSuiteGateConfigSelector,
38709
38756
  async execute(input, ctx, deps = _fullSuiteGateDeps) {
38710
38757
  const logger = getLogger();
38711
- const ctxConfig = ctx.config;
38758
+ const ctxConfig = ctx.packageView.config;
38712
38759
  const enabled = ctxConfig?.execution?.regressionGate?.enabled ?? true;
38713
38760
  if (!enabled) {
38714
38761
  logger.info("verify[regression]", "Regression gate disabled \u2014 skipping full-suite run", {
@@ -38727,7 +38774,7 @@ var init_full_suite_gate = __esm(() => {
38727
38774
  logger.info("verify[regression]", "Running full-suite gate", {
38728
38775
  storyId: input.story.id,
38729
38776
  packageDir: input.story.workdir,
38730
- cwd: input.workdir,
38777
+ cwd: gateCtx.cmdWorkdir,
38731
38778
  command: gateCtx.testCmd,
38732
38779
  timeoutSeconds: gateCtx.fullSuiteTimeout
38733
38780
  });
@@ -38979,7 +39026,7 @@ var init_apply_test_edit_declarations = __esm(() => {
38979
39026
  });
38980
39027
 
38981
39028
  // src/operations/validate-mock-structure-files.ts
38982
- import { join as join24 } from "path";
39029
+ import { join as join25 } from "path";
38983
39030
  async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
38984
39031
  const fileExists = deps?.fileExists ?? defaultFileExists;
38985
39032
  const valid = [];
@@ -38992,7 +39039,7 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
38992
39039
  const files = d.files ?? [d.file];
38993
39040
  let allValid = true;
38994
39041
  for (const file3 of files) {
38995
- const absolutePath = join24(packageDir, file3);
39042
+ const absolutePath = join25(packageDir, file3);
38996
39043
  const exists = await fileExists(absolutePath);
38997
39044
  if (!exists) {
38998
39045
  allValid = false;
@@ -39214,9 +39261,9 @@ var init_mechanical_lintfix_strategy = __esm(() => {
39214
39261
  stage: "rectification",
39215
39262
  config: qualityConfigSelector,
39216
39263
  async execute(input, ctx, deps = _mechanicalLintFixDeps) {
39217
- const ctxConfig = ctx.config;
39218
- const broad = ctxConfig?.quality?.commands?.lintFix;
39219
- const scoped2 = ctxConfig?.quality?.commands?.lintFixScoped;
39264
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39265
+ const broad = quality?.commands?.lintFix;
39266
+ const scoped2 = quality?.commands?.lintFixScoped;
39220
39267
  const command = buildCommand(broad, scoped2, input.scopeFiles);
39221
39268
  if (!command)
39222
39269
  return { applied: true, exitCode: 0 };
@@ -39225,7 +39272,7 @@ var init_mechanical_lintfix_strategy = __esm(() => {
39225
39272
  command,
39226
39273
  workdir: input.workdir,
39227
39274
  storyId: input.storyId,
39228
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39275
+ stripEnvVars: quality?.stripEnvVars ?? []
39229
39276
  });
39230
39277
  return { applied: true, exitCode: result.exitCode };
39231
39278
  }
@@ -39273,9 +39320,9 @@ var init_mechanical_formatfix_strategy = __esm(() => {
39273
39320
  stage: "rectification",
39274
39321
  config: qualityConfigSelector,
39275
39322
  async execute(input, ctx, deps = _mechanicalFormatFixDeps) {
39276
- const ctxConfig = ctx.config;
39277
- const broad = ctxConfig?.quality?.commands?.formatFix;
39278
- const scoped2 = ctxConfig?.quality?.commands?.formatFixScoped;
39323
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39324
+ const broad = quality?.commands?.formatFix;
39325
+ const scoped2 = quality?.commands?.formatFixScoped;
39279
39326
  const command = buildCommand2(broad, scoped2, input.scopeFiles);
39280
39327
  if (!command)
39281
39328
  return { applied: true, exitCode: 0 };
@@ -39284,7 +39331,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
39284
39331
  command,
39285
39332
  workdir: input.workdir,
39286
39333
  storyId: input.storyId,
39287
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39334
+ stripEnvVars: quality?.stripEnvVars ?? []
39288
39335
  });
39289
39336
  return { applied: true, exitCode: result.exitCode };
39290
39337
  }
@@ -39295,6 +39342,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
39295
39342
  var _lintCheckDeps, lintCheckOp;
39296
39343
  var init_lint_check = __esm(() => {
39297
39344
  init_config();
39345
+ init_logger2();
39298
39346
  init_runner();
39299
39347
  init_lint_parsing();
39300
39348
  _lintCheckDeps = {
@@ -39307,21 +39355,26 @@ var init_lint_check = __esm(() => {
39307
39355
  stage: "review",
39308
39356
  config: qualityConfigSelector,
39309
39357
  async execute(input, ctx, deps = _lintCheckDeps) {
39310
- const ctxConfig = ctx.config;
39311
- const command = ctxConfig?.quality?.commands?.lint;
39312
- if (ctxConfig !== undefined && !command) {
39313
- return { success: true, findings: [], durationMs: 0 };
39358
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39359
+ const command = quality?.commands?.lint;
39360
+ if (!command) {
39361
+ getSafeLogger()?.warn("quality", "No lint command configured \u2014 skipping lint gate", {
39362
+ storyId: input.storyId,
39363
+ packageDir: ctx.packageView.packageDir
39364
+ });
39365
+ return { success: true, status: "skipped", findings: [], durationMs: 0 };
39314
39366
  }
39367
+ const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
39315
39368
  const start = Date.now();
39316
39369
  const result = await deps.runQualityCommand({
39317
39370
  commandName: "lint",
39318
- command: command ?? "",
39319
- workdir: input.workdir,
39371
+ command,
39372
+ workdir: cmdWorkdir,
39320
39373
  storyId: input.storyId,
39321
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39374
+ stripEnvVars: quality?.stripEnvVars ?? []
39322
39375
  });
39323
39376
  if (result.exitCode === 0) {
39324
- return { success: true, findings: [], durationMs: Date.now() - start };
39377
+ return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39325
39378
  }
39326
39379
  const parsed = deps.parseLintOutput(result.output, "auto", { workdir: input.workdir });
39327
39380
  return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
@@ -39545,6 +39598,7 @@ var init_typecheck_parsing = __esm(() => {
39545
39598
  var _typecheckCheckDeps, typecheckCheckOp;
39546
39599
  var init_typecheck_check = __esm(() => {
39547
39600
  init_config();
39601
+ init_logger2();
39548
39602
  init_runner();
39549
39603
  init_typecheck_parsing();
39550
39604
  _typecheckCheckDeps = {
@@ -39557,21 +39611,26 @@ var init_typecheck_check = __esm(() => {
39557
39611
  stage: "review",
39558
39612
  config: qualityConfigSelector,
39559
39613
  async execute(input, ctx, deps = _typecheckCheckDeps) {
39560
- const ctxConfig = ctx.config;
39561
- const command = ctxConfig?.quality?.commands?.typecheck;
39562
- if (ctxConfig !== undefined && !command) {
39563
- return { success: true, findings: [], durationMs: 0 };
39614
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39615
+ const command = quality?.commands?.typecheck;
39616
+ if (!command) {
39617
+ getSafeLogger()?.warn("quality", "No typecheck command configured \u2014 skipping typecheck gate", {
39618
+ storyId: input.storyId,
39619
+ packageDir: ctx.packageView.packageDir
39620
+ });
39621
+ return { success: true, status: "skipped", findings: [], durationMs: 0 };
39564
39622
  }
39623
+ const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
39565
39624
  const start = Date.now();
39566
39625
  const result = await deps.runQualityCommand({
39567
39626
  commandName: "typecheck",
39568
- command: command ?? "",
39569
- workdir: input.workdir,
39627
+ command,
39628
+ workdir: cmdWorkdir,
39570
39629
  storyId: input.storyId,
39571
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39630
+ stripEnvVars: quality?.stripEnvVars ?? []
39572
39631
  });
39573
39632
  if (result.exitCode === 0) {
39574
- return { success: true, findings: [], durationMs: Date.now() - start };
39633
+ return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39575
39634
  }
39576
39635
  const parsed = deps.parseTypecheckOutput(result.output, "auto", { workdir: input.workdir });
39577
39636
  return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
@@ -39600,12 +39659,16 @@ var init_verify_scoped = __esm(() => {
39600
39659
  config: qualityConfigSelector,
39601
39660
  async execute(input, ctx, deps = _verifyScopedDeps) {
39602
39661
  const logger = getLogger();
39603
- const ctxConfig = ctx.config;
39604
- const baseCommand = ctxConfig?.quality?.commands?.test;
39605
- if (!ctxConfig || !baseCommand) {
39662
+ const quality = ctx.packageView.select(qualityConfigSelector);
39663
+ const baseCommand = quality.quality?.commands?.test;
39664
+ if (!baseCommand) {
39665
+ logger.warn("quality", "No test command configured \u2014 skipping scoped verify", {
39666
+ storyId: input.storyId,
39667
+ packageDir: ctx.packageView.packageDir
39668
+ });
39606
39669
  return {
39607
39670
  success: true,
39608
- status: "passed",
39671
+ status: "skipped",
39609
39672
  findings: [],
39610
39673
  durationMs: 0,
39611
39674
  passCount: 0,
@@ -39617,11 +39680,11 @@ var init_verify_scoped = __esm(() => {
39617
39680
  workdir: input.workdir,
39618
39681
  storyId: input.storyId,
39619
39682
  storyGitRef: input.storyGitRef,
39620
- testCommand: baseCommand ?? "",
39621
- testScopedTemplate: ctxConfig.quality?.commands?.testScoped,
39622
- smartRunnerConfig: ctxConfig.execution?.smartTestRunner,
39623
- scopeTestThreshold: ctxConfig.quality?.scopeTestThreshold,
39624
- fallbackFullSuiteCommand: ctxConfig.quality?.commands?.test,
39683
+ testCommand: baseCommand,
39684
+ testScopedTemplate: quality.quality?.commands?.testScoped,
39685
+ smartRunnerConfig: quality.execution?.smartTestRunner,
39686
+ scopeTestThreshold: quality.quality?.scopeTestThreshold,
39687
+ fallbackFullSuiteCommand: quality.quality?.commands?.test,
39625
39688
  naxIgnoreIndex: input.naxIgnoreIndex
39626
39689
  });
39627
39690
  if (selection.isFullSuite && regressionMode === "deferred" && !selection.isMonorepoOrchestrator && !selection.thresholdFallback) {
@@ -39648,27 +39711,28 @@ var init_verify_scoped = __esm(() => {
39648
39711
  command: selection.effectiveCommand
39649
39712
  });
39650
39713
  }
39651
- const scopedTimeout = ctxConfig.execution?.regressionGate?.timeoutSeconds ?? 600;
39714
+ const scopedTimeout = quality.execution?.regressionGate?.timeoutSeconds ?? 600;
39715
+ const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
39652
39716
  logger.info("verify[scoped]", "Running scoped tests", {
39653
39717
  storyId: input.storyId,
39654
39718
  packageDir: input.packageDir,
39655
- cwd: input.workdir,
39719
+ cwd: cmdWorkdir,
39656
39720
  command: selection.effectiveCommand,
39657
39721
  timeoutSeconds: scopedTimeout,
39658
39722
  isFullSuite: selection.isFullSuite
39659
39723
  });
39660
39724
  const start = Date.now();
39661
39725
  const result = await deps.regression({
39662
- workdir: input.workdir,
39726
+ workdir: cmdWorkdir,
39663
39727
  command: selection.effectiveCommand,
39664
- timeoutSeconds: ctxConfig.execution?.regressionGate?.timeoutSeconds ?? 600,
39665
- forceExit: ctxConfig.quality?.forceExit,
39666
- detectOpenHandles: ctxConfig.quality?.detectOpenHandles,
39667
- detectOpenHandlesRetries: ctxConfig.quality?.detectOpenHandlesRetries,
39668
- gracePeriodMs: ctxConfig.quality?.gracePeriodMs,
39669
- drainTimeoutMs: ctxConfig.quality?.drainTimeoutMs,
39670
- shell: ctxConfig.quality?.shell,
39671
- stripEnvVars: ctxConfig.quality?.stripEnvVars
39728
+ timeoutSeconds: quality.execution?.regressionGate?.timeoutSeconds ?? 600,
39729
+ forceExit: quality.quality?.forceExit,
39730
+ detectOpenHandles: quality.quality?.detectOpenHandles,
39731
+ detectOpenHandlesRetries: quality.quality?.detectOpenHandlesRetries,
39732
+ gracePeriodMs: quality.quality?.gracePeriodMs,
39733
+ drainTimeoutMs: quality.quality?.drainTimeoutMs,
39734
+ shell: quality.quality?.shell,
39735
+ stripEnvVars: quality.quality?.stripEnvVars
39672
39736
  });
39673
39737
  const durationMs = Date.now() - start;
39674
39738
  const parsed = result.output ? deps.parseTestOutput(result.output) : { passed: 0, failed: 0, failures: [] };
@@ -40440,7 +40504,7 @@ var init_lint_parsing = __esm(() => {
40440
40504
  });
40441
40505
 
40442
40506
  // src/review/scoped-lint.ts
40443
- import { join as join25, relative as relative10 } from "path";
40507
+ import { join as join26, relative as relative10 } from "path";
40444
40508
  function shellQuotePath4(path5) {
40445
40509
  return `'${path5.replaceAll("'", "'\\''")}'`;
40446
40510
  }
@@ -40488,7 +40552,7 @@ function uniqueFiles(files) {
40488
40552
  async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
40489
40553
  const inScope = [];
40490
40554
  for (const relPath of files) {
40491
- const absPath = join25(workdir, relPath);
40555
+ const absPath = join26(workdir, relPath);
40492
40556
  const exists = await _scopedLintDeps.fileExists(absPath);
40493
40557
  if (!exists)
40494
40558
  continue;
@@ -43862,7 +43926,7 @@ var init_call = __esm(() => {
43862
43926
 
43863
43927
  // src/runtime/cost-aggregator.ts
43864
43928
  import { mkdirSync as mkdirSync2 } from "fs";
43865
- import { join as join26 } from "path";
43929
+ import { join as join27 } from "path";
43866
43930
  function makeCorrelationId() {
43867
43931
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
43868
43932
  }
@@ -44053,7 +44117,7 @@ class CostAggregator {
44053
44117
  if (events.length === 0 && errors3.length === 0)
44054
44118
  return;
44055
44119
  mkdirSync2(this._drainDir, { recursive: true });
44056
- const path5 = join26(this._drainDir, `${this._runId}.jsonl`);
44120
+ const path5 = join27(this._drainDir, `${this._runId}.jsonl`);
44057
44121
  const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
44058
44122
  await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
44059
44123
  `)}
@@ -44093,7 +44157,7 @@ var init_cost_aggregator = __esm(() => {
44093
44157
  // src/runtime/prompt-auditor.ts
44094
44158
  import { appendFileSync } from "fs";
44095
44159
  import { mkdir as mkdir4 } from "fs/promises";
44096
- import { join as join27 } from "path";
44160
+ import { join as join28 } from "path";
44097
44161
  function createNoOpPromptAuditor() {
44098
44162
  return {
44099
44163
  record() {},
@@ -44159,8 +44223,8 @@ class PromptAuditor {
44159
44223
  _jsonlPath;
44160
44224
  _featureDir;
44161
44225
  constructor(runId, flushDir, featureName) {
44162
- this._featureDir = join27(flushDir, featureName);
44163
- this._jsonlPath = join27(this._featureDir, `${runId}.jsonl`);
44226
+ this._featureDir = join28(flushDir, featureName);
44227
+ this._jsonlPath = join28(this._featureDir, `${runId}.jsonl`);
44164
44228
  }
44165
44229
  record(entry) {
44166
44230
  this._enqueue(entry);
@@ -44209,7 +44273,7 @@ class PromptAuditor {
44209
44273
  const auditEntry = entry;
44210
44274
  const filename = deriveTxtFilename(auditEntry);
44211
44275
  try {
44212
- await _promptAuditorDeps.write(join27(this._featureDir, filename), buildTxtContent(auditEntry));
44276
+ await _promptAuditorDeps.write(join28(this._featureDir, filename), buildTxtContent(auditEntry));
44213
44277
  } catch (err) {
44214
44278
  throw tagAuditError(err, "txt");
44215
44279
  }
@@ -44325,12 +44389,14 @@ var init_paths2 = __esm(() => {
44325
44389
  });
44326
44390
 
44327
44391
  // src/runtime/packages.ts
44328
- function createPackageView(config2, packageDir, repoRoot) {
44392
+ function createPackageView(config2, packageDir, repoRoot, hasOverride) {
44329
44393
  const memo = new Map;
44330
44394
  const relativeFromRoot = packageDir ? packageDir.startsWith(repoRoot) ? packageDir.slice(repoRoot.length).replace(/^\//, "") : packageDir : "";
44331
44395
  return {
44332
44396
  packageDir,
44333
44397
  relativeFromRoot,
44398
+ repoRoot,
44399
+ hasOverride,
44334
44400
  config: config2,
44335
44401
  select(selector) {
44336
44402
  if (memo.has(selector.name)) {
@@ -44344,17 +44410,46 @@ function createPackageView(config2, packageDir, repoRoot) {
44344
44410
  }
44345
44411
  function createPackageRegistry(loader, repoRoot) {
44346
44412
  const cache = new Map;
44413
+ const mergedConfigs = new Map;
44414
+ function toRelativeKey(packageDir) {
44415
+ if (!packageDir)
44416
+ return "";
44417
+ const prefix = repoRoot.endsWith("/") ? repoRoot : `${repoRoot}/`;
44418
+ if (packageDir.startsWith(prefix))
44419
+ return packageDir.slice(prefix.length);
44420
+ if (packageDir === repoRoot)
44421
+ return "";
44422
+ return packageDir;
44423
+ }
44347
44424
  function resolve12(packageDir) {
44348
- const key = packageDir ?? "";
44425
+ const key = toRelativeKey(packageDir);
44349
44426
  const cached2 = cache.get(key);
44350
44427
  if (cached2 !== undefined) {
44351
44428
  return cached2;
44352
44429
  }
44353
- const config2 = loader.current();
44354
- const view = createPackageView(config2, key, repoRoot);
44430
+ const overrideConfig = mergedConfigs.get(key);
44431
+ const hasOverride = overrideConfig !== undefined;
44432
+ const config2 = overrideConfig ?? loader.current();
44433
+ const view = createPackageView(config2, key, repoRoot, hasOverride);
44355
44434
  cache.set(key, view);
44356
44435
  return view;
44357
44436
  }
44437
+ async function hydrate(packageDirs, loadOverride2) {
44438
+ const load = loadOverride2 ?? (await Promise.resolve().then(() => (init_config(), exports_config))).loadPackageOverride;
44439
+ for (const dir of packageDirs) {
44440
+ if (!dir) {
44441
+ continue;
44442
+ }
44443
+ if (mergedConfigs.has(dir)) {
44444
+ continue;
44445
+ }
44446
+ const override = await load(repoRoot, dir);
44447
+ if (override !== null) {
44448
+ mergedConfigs.set(dir, mergePackageConfig(loader.current(), override));
44449
+ cache.delete(dir);
44450
+ }
44451
+ }
44452
+ }
44358
44453
  return {
44359
44454
  all() {
44360
44455
  return [...cache.values()];
@@ -44362,9 +44457,13 @@ function createPackageRegistry(loader, repoRoot) {
44362
44457
  resolve: resolve12,
44363
44458
  repo() {
44364
44459
  return resolve12(undefined);
44365
- }
44460
+ },
44461
+ hydrate
44366
44462
  };
44367
44463
  }
44464
+ var init_packages = __esm(() => {
44465
+ init_config();
44466
+ });
44368
44467
 
44369
44468
  // src/runtime/agent-stream-events.ts
44370
44469
  class AgentStreamEventBus {
@@ -45322,7 +45421,7 @@ var init_pid_registry = __esm(() => {
45322
45421
  // src/session/manager-deps.ts
45323
45422
  import { randomUUID as randomUUID3 } from "crypto";
45324
45423
  import { mkdir as mkdir5 } from "fs/promises";
45325
- import { isAbsolute as isAbsolute9, join as join28, relative as relative11, sep as sep2 } from "path";
45424
+ import { isAbsolute as isAbsolute9, join as join29, relative as relative11, sep as sep2 } from "path";
45326
45425
  function resolveProjectDirFromScratchDir(scratchDir) {
45327
45426
  const marker = `${sep2}.nax${sep2}features${sep2}`;
45328
45427
  const markerIdx = scratchDir.lastIndexOf(marker);
@@ -45343,7 +45442,7 @@ var init_manager_deps = __esm(() => {
45343
45442
  now: () => new Date().toISOString(),
45344
45443
  nowMs: () => Date.now(),
45345
45444
  uuid: () => randomUUID3(),
45346
- sessionScratchDir: (projectDir, featureName, sessionId) => join28(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45445
+ sessionScratchDir: (projectDir, featureName, sessionId) => join29(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45347
45446
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
45348
45447
  await mkdir5(scratchDir, { recursive: true });
45349
45448
  const { handle: _handle, ...persistable } = descriptor;
@@ -45354,7 +45453,7 @@ var init_manager_deps = __esm(() => {
45354
45453
  persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
45355
45454
  }
45356
45455
  }
45357
- await Bun.write(join28(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45456
+ await Bun.write(join29(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45358
45457
  }
45359
45458
  };
45360
45459
  });
@@ -46105,7 +46204,7 @@ __export(exports_runtime, {
46105
46204
  CostAggregator: () => CostAggregator,
46106
46205
  AgentStreamEventBus: () => AgentStreamEventBus
46107
46206
  });
46108
- import { basename as basename5, join as join29 } from "path";
46207
+ import { basename as basename5, join as join30 } from "path";
46109
46208
  function createRuntime(config2, workdir, opts) {
46110
46209
  const runId = crypto.randomUUID();
46111
46210
  const controller = new AbortController;
@@ -46121,10 +46220,10 @@ function createRuntime(config2, workdir, opts) {
46121
46220
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
46122
46221
  const globalDir = globalOutputDir();
46123
46222
  const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
46124
- const costDir = join29(outputDir, "cost");
46223
+ const costDir = join30(outputDir, "cost");
46125
46224
  const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
46126
46225
  const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
46127
- const auditDir = config2.agent?.promptAudit?.dir ?? join29(outputDir, "prompt-audit");
46226
+ const auditDir = config2.agent?.promptAudit?.dir ?? join30(outputDir, "prompt-audit");
46128
46227
  let promptAuditor;
46129
46228
  if (opts?.promptAuditor) {
46130
46229
  promptAuditor = opts.promptAuditor;
@@ -46230,6 +46329,7 @@ var init_runtime = __esm(() => {
46230
46329
  init_prompt_auditor();
46231
46330
  init_review_audit();
46232
46331
  init_paths2();
46332
+ init_packages();
46233
46333
  init_dispatch_events();
46234
46334
  init_agent_stream_events();
46235
46335
  init_middleware();
@@ -46247,6 +46347,7 @@ var init_runtime = __esm(() => {
46247
46347
  init_cost_aggregator();
46248
46348
  init_dispatch_events();
46249
46349
  init_middleware();
46350
+ init_packages();
46250
46351
  init_paths2();
46251
46352
  init_prompt_auditor();
46252
46353
  init_session_run_hop();
@@ -46275,9 +46376,9 @@ async function allSettledBounded(tasks, limit) {
46275
46376
 
46276
46377
  // src/context/injector.ts
46277
46378
  import { existsSync as existsSync8 } from "fs";
46278
- import { join as join30 } from "path";
46379
+ import { join as join31 } from "path";
46279
46380
  async function detectNode(workdir) {
46280
- const pkgPath = join30(workdir, "package.json");
46381
+ const pkgPath = join31(workdir, "package.json");
46281
46382
  if (!existsSync8(pkgPath))
46282
46383
  return null;
46283
46384
  try {
@@ -46294,7 +46395,7 @@ async function detectNode(workdir) {
46294
46395
  }
46295
46396
  }
46296
46397
  async function detectGo(workdir) {
46297
- const goMod = join30(workdir, "go.mod");
46398
+ const goMod = join31(workdir, "go.mod");
46298
46399
  if (!existsSync8(goMod))
46299
46400
  return null;
46300
46401
  try {
@@ -46318,7 +46419,7 @@ async function detectGo(workdir) {
46318
46419
  }
46319
46420
  }
46320
46421
  async function detectRust(workdir) {
46321
- const cargoPath = join30(workdir, "Cargo.toml");
46422
+ const cargoPath = join31(workdir, "Cargo.toml");
46322
46423
  if (!existsSync8(cargoPath))
46323
46424
  return null;
46324
46425
  try {
@@ -46334,8 +46435,8 @@ async function detectRust(workdir) {
46334
46435
  }
46335
46436
  }
46336
46437
  async function detectPython(workdir) {
46337
- const pyproject = join30(workdir, "pyproject.toml");
46338
- const requirements = join30(workdir, "requirements.txt");
46438
+ const pyproject = join31(workdir, "pyproject.toml");
46439
+ const requirements = join31(workdir, "requirements.txt");
46339
46440
  if (!existsSync8(pyproject) && !existsSync8(requirements))
46340
46441
  return null;
46341
46442
  try {
@@ -46354,7 +46455,7 @@ async function detectPython(workdir) {
46354
46455
  }
46355
46456
  }
46356
46457
  async function detectPhp(workdir) {
46357
- const composerPath = join30(workdir, "composer.json");
46458
+ const composerPath = join31(workdir, "composer.json");
46358
46459
  if (!existsSync8(composerPath))
46359
46460
  return null;
46360
46461
  try {
@@ -46367,7 +46468,7 @@ async function detectPhp(workdir) {
46367
46468
  }
46368
46469
  }
46369
46470
  async function detectRuby(workdir) {
46370
- const gemfile = join30(workdir, "Gemfile");
46471
+ const gemfile = join31(workdir, "Gemfile");
46371
46472
  if (!existsSync8(gemfile))
46372
46473
  return null;
46373
46474
  try {
@@ -46379,9 +46480,9 @@ async function detectRuby(workdir) {
46379
46480
  }
46380
46481
  }
46381
46482
  async function detectJvm(workdir) {
46382
- const pom = join30(workdir, "pom.xml");
46383
- const gradle = join30(workdir, "build.gradle");
46384
- const gradleKts = join30(workdir, "build.gradle.kts");
46483
+ const pom = join31(workdir, "pom.xml");
46484
+ const gradle = join31(workdir, "build.gradle");
46485
+ const gradleKts = join31(workdir, "build.gradle.kts");
46385
46486
  if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
46386
46487
  return null;
46387
46488
  try {
@@ -46389,7 +46490,7 @@ async function detectJvm(workdir) {
46389
46490
  const content2 = await Bun.file(pom).text();
46390
46491
  const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
46391
46492
  const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
46392
- const lang2 = existsSync8(join30(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46493
+ const lang2 = existsSync8(join31(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46393
46494
  return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
46394
46495
  }
46395
46496
  const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
@@ -46643,7 +46744,7 @@ var init_windsurf = __esm(() => {
46643
46744
 
46644
46745
  // src/context/generator.ts
46645
46746
  import { existsSync as existsSync9 } from "fs";
46646
- import { join as join31, relative as relative12 } from "path";
46747
+ import { join as join32, relative as relative12 } from "path";
46647
46748
  async function loadContextContent(options, config2) {
46648
46749
  if (!_generatorDeps.existsSync(options.contextPath)) {
46649
46750
  throw new Error(`Context file not found: ${options.contextPath}`);
@@ -46661,7 +46762,7 @@ async function generateFor(agent, options, config2) {
46661
46762
  try {
46662
46763
  const context = await loadContextContent(options, config2);
46663
46764
  const content = generator.generate(context);
46664
- const outputPath = join31(options.outputDir, generator.outputFile);
46765
+ const outputPath = join32(options.outputDir, generator.outputFile);
46665
46766
  validateFilePath(outputPath, options.outputDir);
46666
46767
  if (!options.dryRun) {
46667
46768
  await _generatorDeps.writeFile(outputPath, content);
@@ -46679,7 +46780,7 @@ async function generateAll(options, config2, agentFilter) {
46679
46780
  for (const [agentKey, generator] of entries) {
46680
46781
  try {
46681
46782
  const content = generator.generate(context);
46682
- const outputPath = join31(options.outputDir, generator.outputFile);
46783
+ const outputPath = join32(options.outputDir, generator.outputFile);
46683
46784
  validateFilePath(outputPath, options.outputDir);
46684
46785
  if (!options.dryRun) {
46685
46786
  await _generatorDeps.writeFile(outputPath, content);
@@ -46699,7 +46800,7 @@ async function discoverPackages(repoRoot) {
46699
46800
  const glob = new Bun.Glob(pattern);
46700
46801
  for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
46701
46802
  const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
46702
- const pkgAbsolute = join31(repoRoot, pkgRelative);
46803
+ const pkgAbsolute = join32(repoRoot, pkgRelative);
46703
46804
  if (!seen.has(pkgAbsolute)) {
46704
46805
  seen.add(pkgAbsolute);
46705
46806
  packages.push(pkgAbsolute);
@@ -46731,14 +46832,14 @@ async function discoverWorkspacePackages2(repoRoot) {
46731
46832
  }
46732
46833
  }
46733
46834
  }
46734
- const turboPath = join31(repoRoot, "turbo.json");
46835
+ const turboPath = join32(repoRoot, "turbo.json");
46735
46836
  try {
46736
46837
  const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
46737
46838
  if (Array.isArray(turbo.packages)) {
46738
46839
  await resolveGlobs(turbo.packages);
46739
46840
  }
46740
46841
  } catch {}
46741
- const pkgPath = join31(repoRoot, "package.json");
46842
+ const pkgPath = join32(repoRoot, "package.json");
46742
46843
  try {
46743
46844
  const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
46744
46845
  const ws = pkg.workspaces;
@@ -46746,7 +46847,7 @@ async function discoverWorkspacePackages2(repoRoot) {
46746
46847
  if (patterns.length > 0)
46747
46848
  await resolveGlobs(patterns);
46748
46849
  } catch {}
46749
- const pnpmPath = join31(repoRoot, "pnpm-workspace.yaml");
46850
+ const pnpmPath = join32(repoRoot, "pnpm-workspace.yaml");
46750
46851
  try {
46751
46852
  const raw = await _generatorDeps.readTextFile(pnpmPath);
46752
46853
  const lines = raw.split(`
@@ -46772,7 +46873,7 @@ async function discoverWorkspacePackages2(repoRoot) {
46772
46873
  async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
46773
46874
  const resolvedRepoRoot = repoRoot ?? packageDir;
46774
46875
  const relativePkgPath = relative12(resolvedRepoRoot, packageDir);
46775
- const contextPath = join31(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
46876
+ const contextPath = join32(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
46776
46877
  if (!_generatorDeps.existsSync(contextPath)) {
46777
46878
  return [
46778
46879
  {
@@ -46840,7 +46941,7 @@ var init_generator2 = __esm(() => {
46840
46941
  });
46841
46942
 
46842
46943
  // src/analyze/scanner.ts
46843
- import { join as join32 } from "path";
46944
+ import { join as join33 } from "path";
46844
46945
  function resolveFrameworkAndRunner(language, pkg) {
46845
46946
  if (language === "go")
46846
46947
  return { framework: "", testRunner: "go-test" };
@@ -46862,7 +46963,7 @@ async function scanSourceRoots(workdir) {
46862
46963
  });
46863
46964
  try {
46864
46965
  const language = await deps.detectLanguage(workdir);
46865
- const pkg = await deps.readPackageJson(join32(workdir, "package.json"));
46966
+ const pkg = await deps.readPackageJson(join33(workdir, "package.json"));
46866
46967
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
46867
46968
  return [{ path: ".", language, framework, testRunner }];
46868
46969
  } catch {
@@ -46880,9 +46981,9 @@ async function scanSourceRoots(workdir) {
46880
46981
  packages = packages.slice(0, MAX_SOURCE_ROOTS);
46881
46982
  }
46882
46983
  return Promise.all(packages.map(async (pkgPath) => {
46883
- const pkgDir = pkgPath === "." ? workdir : join32(workdir, pkgPath);
46984
+ const pkgDir = pkgPath === "." ? workdir : join33(workdir, pkgPath);
46884
46985
  const language = await deps.detectLanguage(pkgDir);
46885
- const pkg = await deps.readPackageJson(join32(pkgDir, "package.json"));
46986
+ const pkg = await deps.readPackageJson(join33(pkgDir, "package.json"));
46886
46987
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
46887
46988
  return { path: pkgPath, language, framework, testRunner };
46888
46989
  }));
@@ -46915,7 +47016,7 @@ var init_analyze = __esm(() => {
46915
47016
  });
46916
47017
 
46917
47018
  // src/debate/pre-phase/grounder.ts
46918
- import { join as join33 } from "path";
47019
+ import { join as join34 } from "path";
46919
47020
  async function buildCodebaseContext(workdir) {
46920
47021
  const roots = await _grounderDeps.scanSourceRoots(workdir);
46921
47022
  return buildSourceRootsSection(normalizeRoots(workdir, roots));
@@ -46927,7 +47028,7 @@ function normalizeRoots(workdir, roots) {
46927
47028
  }));
46928
47029
  }
46929
47030
  async function writeManifestArtifact(ctx, manifest) {
46930
- const manifestPath = join33(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47031
+ const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
46931
47032
  await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
46932
47033
  }
46933
47034
  var _grounderDeps, grounderStrategy = async (ctx) => {
@@ -47270,7 +47371,7 @@ function formatSpecDeltas(blockers, manifest) {
47270
47371
 
47271
47372
  // src/debate/verifiers/checks.ts
47272
47373
  import { existsSync as defaultExistsSync } from "fs";
47273
- import { join as join34 } from "path";
47374
+ import { join as join35 } from "path";
47274
47375
  function checkFilesExist(prd, workdir, deps) {
47275
47376
  const existsSync10 = deps?.existsSync ?? defaultExistsSync;
47276
47377
  const findings = [];
@@ -47280,7 +47381,7 @@ function checkFilesExist(prd, workdir, deps) {
47280
47381
  for (const entry of story.contextFiles) {
47281
47382
  const filePath = typeof entry === "string" ? entry : entry.path;
47282
47383
  const factId = typeof entry === "string" ? undefined : entry.factId;
47283
- const absPath = join34(workdir, filePath);
47384
+ const absPath = join35(workdir, filePath);
47284
47385
  if (existsSync10(absPath))
47285
47386
  continue;
47286
47387
  if (factId) {
@@ -47391,7 +47492,7 @@ var init_checks3 = () => {};
47391
47492
 
47392
47493
  // src/debate/verifiers/plan-checklist.ts
47393
47494
  import { existsSync as existsSync10 } from "fs";
47394
- import { join as join35 } from "path";
47495
+ import { join as join36 } from "path";
47395
47496
  function parsePrd(output) {
47396
47497
  if (!output)
47397
47498
  return null;
@@ -47402,7 +47503,7 @@ function parsePrd(output) {
47402
47503
  }
47403
47504
  }
47404
47505
  async function loadManifest(ctx) {
47405
- const manifestPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47506
+ const manifestPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47406
47507
  const raw = await _planChecklistDeps.readFile(manifestPath);
47407
47508
  if (!raw)
47408
47509
  return null;
@@ -47415,7 +47516,7 @@ async function loadManifest(ctx) {
47415
47516
  }
47416
47517
  }
47417
47518
  async function emitSpecDeltas(ctx, blockers, manifest) {
47418
- const artifactPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47519
+ const artifactPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47419
47520
  const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
47420
47521
  await _planChecklistDeps.write(artifactPath, content);
47421
47522
  return artifactPath;
@@ -47761,7 +47862,7 @@ var init_runner_plan_helpers = __esm(() => {
47761
47862
  });
47762
47863
 
47763
47864
  // src/debate/runner-plan.ts
47764
- import { join as join36 } from "path";
47865
+ import { join as join37 } from "path";
47765
47866
  async function runPlan(ctx, taskContext, outputFormat, opts) {
47766
47867
  const logger = _debateSessionDeps.getSafeLogger();
47767
47868
  const config2 = ctx.stageConfig;
@@ -47820,7 +47921,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
47820
47921
  sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
47821
47922
  proposers: ctx.stageConfig.proposers
47822
47923
  });
47823
- const outputPaths = resolved.map((_, i) => join36(opts.outputDir, `prd-debate-${i}.json`));
47924
+ const outputPaths = resolved.map((_, i) => join37(opts.outputDir, `prd-debate-${i}.json`));
47824
47925
  const successful = [];
47825
47926
  let rebuttalList;
47826
47927
  if (selectorKind === "verifier-pick") {
@@ -50063,9 +50164,9 @@ function validateFeatureName(feature) {
50063
50164
 
50064
50165
  // src/plan/critic.ts
50065
50166
  import { mkdir as mkdir6 } from "fs/promises";
50066
- import { dirname as dirname7, join as join39 } from "path";
50167
+ import { dirname as dirname7, join as join40 } from "path";
50067
50168
  async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
50068
- const path7 = join39(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50169
+ const path7 = join40(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50069
50170
  await mkdir6(dirname7(path7), { recursive: true });
50070
50171
  await Bun.write(path7, formatSpecDeltas(findings, manifest));
50071
50172
  return path7;
@@ -51278,9 +51379,9 @@ __export(exports_plan_decompose, {
51278
51379
  runReplanLoop: () => runReplanLoop,
51279
51380
  planDecomposeCommand: () => planDecomposeCommand
51280
51381
  });
51281
- import { join as join40 } from "path";
51382
+ import { join as join41 } from "path";
51282
51383
  async function planDecomposeCommand(workdir, config2, options) {
51283
- const prdPath = join40(workdir, ".nax", "features", options.feature, "prd.json");
51384
+ const prdPath = join41(workdir, ".nax", "features", options.feature, "prd.json");
51284
51385
  if (!_planDeps.existsSync(prdPath)) {
51285
51386
  throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
51286
51387
  stage: "decompose",
@@ -51454,7 +51555,7 @@ var init_plan_decompose = __esm(() => {
51454
51555
 
51455
51556
  // src/cli/plan-runtime.ts
51456
51557
  import { existsSync as existsSync15 } from "fs";
51457
- import { join as join41 } from "path";
51558
+ import { join as join42 } from "path";
51458
51559
  function isRuntimeWithAgentManager(value) {
51459
51560
  return typeof value === "object" && value !== null && "agentManager" in value;
51460
51561
  }
@@ -51506,7 +51607,7 @@ var init_plan_runtime = __esm(() => {
51506
51607
  writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
51507
51608
  scanSourceRoots: (workdir) => scanSourceRoots(workdir),
51508
51609
  createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
51509
- readPackageJson: (workdir) => Bun.file(join41(workdir, "package.json")).json().catch(() => null),
51610
+ readPackageJson: (workdir) => Bun.file(join42(workdir, "package.json")).json().catch(() => null),
51510
51611
  spawnSync: (cmd, opts) => {
51511
51612
  const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
51512
51613
  return { stdout: result.stdout, exitCode: result.exitCode };
@@ -51891,7 +51992,7 @@ var init_metrics = __esm(() => {
51891
51992
 
51892
51993
  // src/commands/common.ts
51893
51994
  import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
51894
- import { join as join42, resolve as resolve13 } from "path";
51995
+ import { join as join43, resolve as resolve13 } from "path";
51895
51996
  function resolveProject(options = {}) {
51896
51997
  const { dir, feature } = options;
51897
51998
  let projectRoot;
@@ -51899,12 +52000,12 @@ function resolveProject(options = {}) {
51899
52000
  let configPath;
51900
52001
  if (dir) {
51901
52002
  projectRoot = realpathSync3(resolve13(dir));
51902
- naxDir = join42(projectRoot, ".nax");
52003
+ naxDir = join43(projectRoot, ".nax");
51903
52004
  if (!existsSync16(naxDir)) {
51904
52005
  throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
51905
52006
  Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
51906
52007
  }
51907
- configPath = join42(naxDir, "config.json");
52008
+ configPath = join43(naxDir, "config.json");
51908
52009
  if (!existsSync16(configPath)) {
51909
52010
  throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
51910
52011
  Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
@@ -51912,17 +52013,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
51912
52013
  } else {
51913
52014
  const found = findProjectRoot(process.cwd());
51914
52015
  if (!found) {
51915
- const cwdNaxDir = join42(process.cwd(), ".nax");
52016
+ const cwdNaxDir = join43(process.cwd(), ".nax");
51916
52017
  if (existsSync16(cwdNaxDir)) {
51917
- const cwdConfigPath = join42(cwdNaxDir, "config.json");
52018
+ const cwdConfigPath = join43(cwdNaxDir, "config.json");
51918
52019
  throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
51919
52020
  Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
51920
52021
  }
51921
52022
  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() });
51922
52023
  }
51923
52024
  projectRoot = found;
51924
- naxDir = join42(projectRoot, ".nax");
51925
- configPath = join42(naxDir, "config.json");
52025
+ naxDir = join43(projectRoot, ".nax");
52026
+ configPath = join43(naxDir, "config.json");
51926
52027
  }
51927
52028
  let featureDir;
51928
52029
  if (feature) {
@@ -51931,8 +52032,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
51931
52032
  } catch (error48) {
51932
52033
  throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
51933
52034
  }
51934
- const featuresDir = join42(naxDir, "features");
51935
- featureDir = join42(featuresDir, feature);
52035
+ const featuresDir = join43(naxDir, "features");
52036
+ featureDir = join43(featuresDir, feature);
51936
52037
  if (!existsSync16(featureDir)) {
51937
52038
  const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
51938
52039
  const availableMsg = availableFeatures.length > 0 ? `
@@ -51965,7 +52066,7 @@ async function resolveProjectAsync(options = {}) {
51965
52066
  }
51966
52067
  const isPlainName = !dir.includes("/") && !dir.includes("\\");
51967
52068
  if (isPlainName) {
51968
- const registryIdentityPath = join42(globalConfigDir(), dir, ".identity");
52069
+ const registryIdentityPath = join43(globalConfigDir(), dir, ".identity");
51969
52070
  const identityFile = Bun.file(registryIdentityPath);
51970
52071
  if (await identityFile.exists()) {
51971
52072
  try {
@@ -51997,12 +52098,12 @@ function findProjectRoot(startDir) {
51997
52098
  let current = resolve13(startDir);
51998
52099
  let depth = 0;
51999
52100
  while (depth < MAX_DIRECTORY_DEPTH) {
52000
- const naxDir = join42(current, ".nax");
52001
- const configPath = join42(naxDir, "config.json");
52101
+ const naxDir = join43(current, ".nax");
52102
+ const configPath = join43(naxDir, "config.json");
52002
52103
  if (existsSync16(configPath)) {
52003
52104
  return realpathSync3(current);
52004
52105
  }
52005
- const parent = join42(current, "..");
52106
+ const parent = join43(current, "..");
52006
52107
  if (parent === current) {
52007
52108
  break;
52008
52109
  }
@@ -53161,10 +53262,10 @@ var init_effectiveness = __esm(() => {
53161
53262
 
53162
53263
  // src/execution/progress.ts
53163
53264
  import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
53164
- import { join as join45 } from "path";
53265
+ import { join as join46 } from "path";
53165
53266
  async function appendProgress(featureDir, storyId, status, message) {
53166
53267
  await mkdir7(featureDir, { recursive: true });
53167
- const progressPath = join45(featureDir, "progress.txt");
53268
+ const progressPath = join46(featureDir, "progress.txt");
53168
53269
  const timestamp = new Date().toISOString();
53169
53270
  const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
53170
53271
  `;
@@ -53358,7 +53459,7 @@ var init_completion = __esm(() => {
53358
53459
 
53359
53460
  // src/constitution/loader.ts
53360
53461
  import { existsSync as existsSync19 } from "fs";
53361
- import { join as join46 } from "path";
53462
+ import { join as join47 } from "path";
53362
53463
  function truncateToTokens(text, maxTokens) {
53363
53464
  const maxChars = maxTokens * 3;
53364
53465
  if (text.length <= maxChars) {
@@ -53380,7 +53481,7 @@ async function loadConstitution(projectDir, config2) {
53380
53481
  }
53381
53482
  let combinedContent = "";
53382
53483
  if (!config2.skipGlobal) {
53383
- const globalPath = join46(globalConfigDir(), config2.path);
53484
+ const globalPath = join47(globalConfigDir(), config2.path);
53384
53485
  if (existsSync19(globalPath)) {
53385
53486
  const validatedPath = validateFilePath(globalPath, globalConfigDir());
53386
53487
  const globalFile = Bun.file(validatedPath);
@@ -53390,7 +53491,7 @@ async function loadConstitution(projectDir, config2) {
53390
53491
  }
53391
53492
  }
53392
53493
  }
53393
- const projectPath = join46(projectDir, config2.path);
53494
+ const projectPath = join47(projectDir, config2.path);
53394
53495
  if (existsSync19(projectPath)) {
53395
53496
  const validatedPath = validateFilePath(projectPath, projectDir);
53396
53497
  const projectFile = Bun.file(validatedPath);
@@ -53964,6 +54065,64 @@ var init_non_blocking_fix = __esm(() => {
53964
54065
  DEFAULT_DEPS = { captureSnapshotRef, rollbackToRef };
53965
54066
  });
53966
54067
 
54068
+ // src/execution/story-orchestrator-logging.ts
54069
+ function formatPhaseResultMessage(opName, success2, stage, status) {
54070
+ if (opName === "greenfield-gate") {
54071
+ return success2 ? "Greenfield-gate: pre-existing tests detected (not greenfield) \u2014 proceeding with normal TDD" : "Greenfield-gate: no pre-existing tests \u2014 greenfield run, pausing TDD test-writer";
54072
+ }
54073
+ if (status === "skipped") {
54074
+ return `Phase skipped: ${opName}`;
54075
+ }
54076
+ if (stage === "rectification") {
54077
+ return `Rectification strategy completed: ${opName}`;
54078
+ }
54079
+ return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
54080
+ }
54081
+ function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
54082
+ if (output === null || output === undefined || typeof output !== "object")
54083
+ return null;
54084
+ const r = output;
54085
+ const success2 = r.success === true || r.passed === true;
54086
+ const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
54087
+ const status = typeof r.status === "string" ? r.status : undefined;
54088
+ const data = { storyId, phase: opName, durationMs };
54089
+ if (findingsCount !== undefined)
54090
+ data.findingsCount = findingsCount;
54091
+ if (status !== undefined)
54092
+ data.status = status;
54093
+ if (typeof r.failureCategory === "string")
54094
+ data.failureCategory = r.failureCategory;
54095
+ if (typeof r.reviewReason === "string")
54096
+ data.reviewReason = r.reviewReason;
54097
+ return { success: success2, data };
54098
+ }
54099
+ function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
54100
+ if (isTddPhase)
54101
+ return;
54102
+ if (opName === "semantic-review" || opName === "adversarial-review")
54103
+ return;
54104
+ const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
54105
+ if (!built)
54106
+ return;
54107
+ const { success: success2 } = built;
54108
+ const data = { ...built.data, ...progressData };
54109
+ const status = typeof built.data.status === "string" ? built.data.status : undefined;
54110
+ const logger = getSafeLogger();
54111
+ const message = formatPhaseResultMessage(opName, success2, stage, status);
54112
+ if (stage === "rectification") {
54113
+ logger?.info("story-orchestrator", message, data);
54114
+ return;
54115
+ }
54116
+ if (success2) {
54117
+ logger?.info("story-orchestrator", message, data);
54118
+ } else {
54119
+ logger?.warn("story-orchestrator", message, data);
54120
+ }
54121
+ }
54122
+ var init_story_orchestrator_logging = __esm(() => {
54123
+ init_logger2();
54124
+ });
54125
+
53967
54126
  // src/execution/story-orchestrator.ts
53968
54127
  async function refreshReviewInputForDispatch(opName, input) {
53969
54128
  if (opName !== "semantic-review" && opName !== "adversarial-review")
@@ -54024,15 +54183,6 @@ async function refreshReviewInputForDispatch(opName, input) {
54024
54183
  return fallback;
54025
54184
  }
54026
54185
  }
54027
- function formatPhaseResultMessage(opName, success2, stage) {
54028
- if (opName === "greenfield-gate") {
54029
- return success2 ? "Greenfield-gate: pre-existing tests detected (not greenfield) \u2014 proceeding with normal TDD" : "Greenfield-gate: no pre-existing tests \u2014 greenfield run, pausing TDD test-writer";
54030
- }
54031
- if (stage === "rectification") {
54032
- return `Rectification strategy completed: ${opName}`;
54033
- }
54034
- return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
54035
- }
54036
54186
  function isSlot(value) {
54037
54187
  return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
54038
54188
  }
@@ -54228,46 +54378,6 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
54228
54378
  logger?.info("review", "Running adversarial check", { storyId });
54229
54379
  }
54230
54380
  }
54231
- function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
54232
- if (output === null || output === undefined || typeof output !== "object")
54233
- return null;
54234
- const r = output;
54235
- const success2 = r.success === true || r.passed === true;
54236
- const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
54237
- const status = typeof r.status === "string" ? r.status : undefined;
54238
- const data = { storyId, phase: opName, durationMs };
54239
- if (findingsCount !== undefined)
54240
- data.findingsCount = findingsCount;
54241
- if (status !== undefined)
54242
- data.status = status;
54243
- if (typeof r.failureCategory === "string")
54244
- data.failureCategory = r.failureCategory;
54245
- if (typeof r.reviewReason === "string")
54246
- data.reviewReason = r.reviewReason;
54247
- return { success: success2, data };
54248
- }
54249
- function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
54250
- if (isTddPhase)
54251
- return;
54252
- if (opName === "semantic-review" || opName === "adversarial-review")
54253
- return;
54254
- const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
54255
- if (!built)
54256
- return;
54257
- const { success: success2 } = built;
54258
- const data = { ...built.data, ...progressData };
54259
- const logger = getSafeLogger();
54260
- const message = formatPhaseResultMessage(opName, success2, stage);
54261
- if (stage === "rectification") {
54262
- logger?.info("story-orchestrator", message, data);
54263
- return;
54264
- }
54265
- if (success2) {
54266
- logger?.info("story-orchestrator", message, data);
54267
- } else {
54268
- logger?.warn("story-orchestrator", message, data);
54269
- }
54270
- }
54271
54381
  function logUnifiedReviewPhaseResult(storyId, opName, output) {
54272
54382
  const logger = getSafeLogger();
54273
54383
  const payload = toReviewDecisionPayload(opName, output);
@@ -54723,6 +54833,7 @@ var init_story_orchestrator = __esm(() => {
54723
54833
  init_prepare_inputs();
54724
54834
  init_git();
54725
54835
  init_non_blocking_fix();
54836
+ init_story_orchestrator_logging();
54726
54837
  _storyOrchestratorDeps = {
54727
54838
  callOp,
54728
54839
  runFixCycle,
@@ -54787,7 +54898,7 @@ var init_story_orchestrator = __esm(() => {
54787
54898
  });
54788
54899
 
54789
54900
  // src/execution/build-plan-for-strategy.ts
54790
- import { join as join47 } from "path";
54901
+ import { join as join48 } from "path";
54791
54902
  function requiresInitialRefCapture(strategy) {
54792
54903
  return isThreeSessionStrategy(strategy);
54793
54904
  }
@@ -54833,13 +54944,14 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
54833
54944
  }
54834
54945
  if (shouldRunRectification(config2) && inputs.rectification) {
54835
54946
  const sink = makeDeclarationSink();
54836
- const packageDir = join47(ctx.packageDir, story.workdir ?? "");
54947
+ const packageDir = join48(ctx.packageDir, story.workdir ?? "");
54837
54948
  const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
54838
54949
  const strategies = [];
54839
- if (config2.quality.commands.lintFix || config2.quality.commands.lintFixScoped) {
54950
+ const pkgQuality = ctx.packageView.select(qualityConfigSelector).quality;
54951
+ if (pkgQuality?.commands?.lintFix || pkgQuality?.commands?.lintFixScoped) {
54840
54952
  strategies.push(makeMechanicalLintFixStrategy());
54841
54953
  }
54842
- if (config2.quality.commands.formatFix || config2.quality.commands.formatFixScoped) {
54954
+ if (pkgQuality?.commands?.formatFix || pkgQuality?.commands?.formatFixScoped) {
54843
54955
  strategies.push(makeMechanicalFormatFixStrategy());
54844
54956
  }
54845
54957
  if (inputs.fullSuiteGate && (isThreeSession || regressionMode === "per-story")) {
@@ -56366,7 +56478,7 @@ function buildFrontmatter(story, ctx, role) {
56366
56478
  }
56367
56479
 
56368
56480
  // src/cli/prompts-tdd.ts
56369
- import { join as join48 } from "path";
56481
+ import { join as join49 } from "path";
56370
56482
  async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
56371
56483
  const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
56372
56484
  TddPromptBuilder.for("test-writer", { isolation: "strict" }).withLoader(ctx.workdir, ctx.config).story(story).context(ctx.contextMarkdown).constitution(ctx.constitution?.content).testCommand(ctx.config.quality?.commands?.test).build(),
@@ -56385,7 +56497,7 @@ ${frontmatter}---
56385
56497
 
56386
56498
  ${session.prompt}`;
56387
56499
  if (outputDir) {
56388
- const promptFile = join48(outputDir, `${story.id}.${session.role}.md`);
56500
+ const promptFile = join49(outputDir, `${story.id}.${session.role}.md`);
56389
56501
  await Bun.write(promptFile, fullOutput);
56390
56502
  logger.info("cli", "Written TDD prompt file", {
56391
56503
  storyId: story.id,
@@ -56401,7 +56513,7 @@ ${"=".repeat(80)}`);
56401
56513
  }
56402
56514
  }
56403
56515
  if (outputDir && ctx.contextMarkdown) {
56404
- const contextFile = join48(outputDir, `${story.id}.context.md`);
56516
+ const contextFile = join49(outputDir, `${story.id}.context.md`);
56405
56517
  const frontmatter = buildFrontmatter(story, ctx);
56406
56518
  const contextOutput = `---
56407
56519
  ${frontmatter}---
@@ -56416,16 +56528,16 @@ var init_prompts_tdd = __esm(() => {
56416
56528
 
56417
56529
  // src/cli/prompts-main.ts
56418
56530
  import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
56419
- import { join as join49 } from "path";
56531
+ import { join as join50 } from "path";
56420
56532
  async function promptsCommand(options) {
56421
56533
  const logger = getLogger();
56422
56534
  const { feature, workdir, config: config2, storyId, outputDir } = options;
56423
- const naxDir = join49(workdir, ".nax");
56535
+ const naxDir = join50(workdir, ".nax");
56424
56536
  if (!existsSync20(naxDir)) {
56425
56537
  throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
56426
56538
  }
56427
- const featureDir = join49(naxDir, "features", feature);
56428
- const prdPath = join49(featureDir, "prd.json");
56539
+ const featureDir = join50(naxDir, "features", feature);
56540
+ const prdPath = join50(featureDir, "prd.json");
56429
56541
  if (!existsSync20(prdPath)) {
56430
56542
  throw new Error(`Feature "${feature}" not found or missing prd.json`);
56431
56543
  }
@@ -56492,10 +56604,10 @@ ${frontmatter}---
56492
56604
 
56493
56605
  ${ctx.prompt}`;
56494
56606
  if (outputDir) {
56495
- const promptFile = join49(outputDir, `${story.id}.prompt.md`);
56607
+ const promptFile = join50(outputDir, `${story.id}.prompt.md`);
56496
56608
  await Bun.write(promptFile, fullOutput);
56497
56609
  if (ctx.contextMarkdown) {
56498
- const contextFile = join49(outputDir, `${story.id}.context.md`);
56610
+ const contextFile = join50(outputDir, `${story.id}.context.md`);
56499
56611
  const contextOutput = `---
56500
56612
  ${frontmatter}---
56501
56613
 
@@ -56531,12 +56643,12 @@ var init_prompts_main = __esm(() => {
56531
56643
 
56532
56644
  // src/cli/prompts-init.ts
56533
56645
  import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
56534
- import { join as join50 } from "path";
56646
+ import { join as join51 } from "path";
56535
56647
  async function promptsInitCommand(options) {
56536
56648
  const { workdir, force = false, autoWireConfig = true } = options;
56537
- const templatesDir = join50(workdir, ".nax", "templates");
56649
+ const templatesDir = join51(workdir, ".nax", "templates");
56538
56650
  mkdirSync4(templatesDir, { recursive: true });
56539
- const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join50(templatesDir, f)));
56651
+ const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join51(templatesDir, f)));
56540
56652
  if (existingFiles.length > 0 && !force) {
56541
56653
  _promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
56542
56654
  Pass --force to overwrite existing templates.`);
@@ -56544,7 +56656,7 @@ async function promptsInitCommand(options) {
56544
56656
  }
56545
56657
  const written = [];
56546
56658
  for (const template of TEMPLATE_ROLES) {
56547
- const filePath = join50(templatesDir, template.file);
56659
+ const filePath = join51(templatesDir, template.file);
56548
56660
  const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
56549
56661
  const content = TEMPLATE_HEADER + roleBody;
56550
56662
  await Bun.write(filePath, content);
@@ -56560,7 +56672,7 @@ async function promptsInitCommand(options) {
56560
56672
  return written;
56561
56673
  }
56562
56674
  async function autoWirePromptsConfig(workdir) {
56563
- const configPath = join50(workdir, "nax.config.json");
56675
+ const configPath = join51(workdir, "nax.config.json");
56564
56676
  if (!existsSync21(configPath)) {
56565
56677
  const exampleConfig = JSON.stringify({
56566
56678
  prompts: {
@@ -56728,7 +56840,7 @@ __export(exports_init_context, {
56728
56840
  generateContextTemplate: () => generateContextTemplate,
56729
56841
  _initContextDeps: () => _initContextDeps
56730
56842
  });
56731
- import { basename as basename9, join as join51 } from "path";
56843
+ import { basename as basename9, join as join52 } from "path";
56732
56844
  async function bunFileExists(path13) {
56733
56845
  return Bun.file(path13).exists();
56734
56846
  }
@@ -56763,7 +56875,7 @@ async function findFiles(dir, maxFiles = 200) {
56763
56875
  return [];
56764
56876
  }
56765
56877
  async function readPackageManifest(projectRoot) {
56766
- const packageJsonPath = join51(projectRoot, "package.json");
56878
+ const packageJsonPath = join52(projectRoot, "package.json");
56767
56879
  if (!await bunFileExists(packageJsonPath)) {
56768
56880
  return null;
56769
56881
  }
@@ -56781,7 +56893,7 @@ async function readPackageManifest(projectRoot) {
56781
56893
  }
56782
56894
  }
56783
56895
  async function readReadmeSnippet(projectRoot) {
56784
- const readmePath = join51(projectRoot, "README.md");
56896
+ const readmePath = join52(projectRoot, "README.md");
56785
56897
  if (!await bunFileExists(readmePath)) {
56786
56898
  return null;
56787
56899
  }
@@ -56799,7 +56911,7 @@ async function detectEntryPoints(projectRoot) {
56799
56911
  const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
56800
56912
  const found = [];
56801
56913
  for (const candidate of candidates) {
56802
- const path13 = join51(projectRoot, candidate);
56914
+ const path13 = join52(projectRoot, candidate);
56803
56915
  if (await bunFileExists(path13)) {
56804
56916
  found.push(candidate);
56805
56917
  }
@@ -56810,7 +56922,7 @@ async function detectConfigFiles(projectRoot) {
56810
56922
  const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
56811
56923
  const found = [];
56812
56924
  for (const candidate of candidates) {
56813
- const path13 = join51(projectRoot, candidate);
56925
+ const path13 = join52(projectRoot, candidate);
56814
56926
  if (await bunFileExists(path13)) {
56815
56927
  found.push(candidate);
56816
56928
  }
@@ -56971,8 +57083,8 @@ function generatePackageContextTemplate(packagePath) {
56971
57083
  }
56972
57084
  async function initPackage(repoRoot, packagePath, force = false) {
56973
57085
  const logger = getLogger();
56974
- const naxDir = join51(repoRoot, ".nax", "mono", packagePath);
56975
- const contextPath = join51(naxDir, "context.md");
57086
+ const naxDir = join52(repoRoot, ".nax", "mono", packagePath);
57087
+ const contextPath = join52(naxDir, "context.md");
56976
57088
  if (await bunFileExists(contextPath) && !force) {
56977
57089
  logger.info("init", "Package context.md already exists (use --force to overwrite)", {
56978
57090
  storyId: "init-context",
@@ -56989,8 +57101,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
56989
57101
  }
56990
57102
  async function initContext(projectRoot, options = {}) {
56991
57103
  const logger = getLogger();
56992
- const naxDir = join51(projectRoot, ".nax");
56993
- const contextPath = join51(naxDir, "context.md");
57104
+ const naxDir = join52(projectRoot, ".nax");
57105
+ const contextPath = join52(naxDir, "context.md");
56994
57106
  if (await bunFileExists(contextPath) && !options.force) {
56995
57107
  logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
56996
57108
  storyId: "init-context",
@@ -57026,9 +57138,9 @@ var init_init_context = __esm(() => {
57026
57138
 
57027
57139
  // src/cli/init-detect.ts
57028
57140
  import { existsSync as existsSync22, readFileSync } from "fs";
57029
- import { join as join52 } from "path";
57141
+ import { join as join53 } from "path";
57030
57142
  function readPackageJson(projectRoot) {
57031
- const pkgPath = join52(projectRoot, "package.json");
57143
+ const pkgPath = join53(projectRoot, "package.json");
57032
57144
  if (!existsSync22(pkgPath))
57033
57145
  return;
57034
57146
  try {
@@ -57071,41 +57183,41 @@ function detectStack(projectRoot) {
57071
57183
  };
57072
57184
  }
57073
57185
  function detectRuntime(projectRoot) {
57074
- if (existsSync22(join52(projectRoot, "bun.lockb")) || existsSync22(join52(projectRoot, "bunfig.toml"))) {
57186
+ if (existsSync22(join53(projectRoot, "bun.lockb")) || existsSync22(join53(projectRoot, "bunfig.toml"))) {
57075
57187
  return "bun";
57076
57188
  }
57077
- if (existsSync22(join52(projectRoot, "package-lock.json")) || existsSync22(join52(projectRoot, "yarn.lock")) || existsSync22(join52(projectRoot, "pnpm-lock.yaml"))) {
57189
+ if (existsSync22(join53(projectRoot, "package-lock.json")) || existsSync22(join53(projectRoot, "yarn.lock")) || existsSync22(join53(projectRoot, "pnpm-lock.yaml"))) {
57078
57190
  return "node";
57079
57191
  }
57080
57192
  return "unknown";
57081
57193
  }
57082
57194
  function detectLanguage2(projectRoot) {
57083
- if (existsSync22(join52(projectRoot, "tsconfig.json")))
57195
+ if (existsSync22(join53(projectRoot, "tsconfig.json")))
57084
57196
  return "typescript";
57085
- if (existsSync22(join52(projectRoot, "pyproject.toml")) || existsSync22(join52(projectRoot, "setup.py"))) {
57197
+ if (existsSync22(join53(projectRoot, "pyproject.toml")) || existsSync22(join53(projectRoot, "setup.py"))) {
57086
57198
  return "python";
57087
57199
  }
57088
- if (existsSync22(join52(projectRoot, "Cargo.toml")))
57200
+ if (existsSync22(join53(projectRoot, "Cargo.toml")))
57089
57201
  return "rust";
57090
- if (existsSync22(join52(projectRoot, "go.mod")))
57202
+ if (existsSync22(join53(projectRoot, "go.mod")))
57091
57203
  return "go";
57092
57204
  return "unknown";
57093
57205
  }
57094
57206
  function detectLinter(projectRoot) {
57095
- if (existsSync22(join52(projectRoot, "biome.json")) || existsSync22(join52(projectRoot, "biome.jsonc"))) {
57207
+ if (existsSync22(join53(projectRoot, "biome.json")) || existsSync22(join53(projectRoot, "biome.jsonc"))) {
57096
57208
  return "biome";
57097
57209
  }
57098
- if (existsSync22(join52(projectRoot, ".eslintrc.json")) || existsSync22(join52(projectRoot, ".eslintrc.js")) || existsSync22(join52(projectRoot, "eslint.config.js"))) {
57210
+ if (existsSync22(join53(projectRoot, ".eslintrc.json")) || existsSync22(join53(projectRoot, ".eslintrc.js")) || existsSync22(join53(projectRoot, "eslint.config.js"))) {
57099
57211
  return "eslint";
57100
57212
  }
57101
57213
  return "unknown";
57102
57214
  }
57103
57215
  function detectMonorepo(projectRoot) {
57104
- if (existsSync22(join52(projectRoot, "turbo.json")))
57216
+ if (existsSync22(join53(projectRoot, "turbo.json")))
57105
57217
  return "turborepo";
57106
- if (existsSync22(join52(projectRoot, "nx.json")))
57218
+ if (existsSync22(join53(projectRoot, "nx.json")))
57107
57219
  return "nx";
57108
- if (existsSync22(join52(projectRoot, "pnpm-workspace.yaml")))
57220
+ if (existsSync22(join53(projectRoot, "pnpm-workspace.yaml")))
57109
57221
  return "pnpm-workspaces";
57110
57222
  const pkg = readPackageJson(projectRoot);
57111
57223
  if (pkg?.workspaces)
@@ -57249,7 +57361,7 @@ __export(exports_init, {
57249
57361
  });
57250
57362
  import { existsSync as existsSync23 } from "fs";
57251
57363
  import { mkdir as mkdir8 } from "fs/promises";
57252
- import { join as join53 } from "path";
57364
+ import { join as join54 } from "path";
57253
57365
  function validateProjectName(name) {
57254
57366
  if (!name)
57255
57367
  return { valid: false, error: "name must be non-empty" };
@@ -57285,7 +57397,7 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
57285
57397
  }
57286
57398
  async function updateGitignore(projectRoot) {
57287
57399
  const logger = getLogger();
57288
- const gitignorePath = join53(projectRoot, ".gitignore");
57400
+ const gitignorePath = join54(projectRoot, ".gitignore");
57289
57401
  let existing = "";
57290
57402
  if (existsSync23(gitignorePath)) {
57291
57403
  existing = await Bun.file(gitignorePath).text();
@@ -57371,7 +57483,7 @@ async function initGlobal() {
57371
57483
  await mkdir8(globalDir, { recursive: true });
57372
57484
  logger.info("init", "Created global config directory", { path: globalDir });
57373
57485
  }
57374
- const configPath = join53(globalDir, "config.json");
57486
+ const configPath = join54(globalDir, "config.json");
57375
57487
  if (!existsSync23(configPath)) {
57376
57488
  await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
57377
57489
  `);
@@ -57379,14 +57491,14 @@ async function initGlobal() {
57379
57491
  } else {
57380
57492
  logger.info("init", "Global config already exists", { path: configPath });
57381
57493
  }
57382
- const constitutionPath = join53(globalDir, "constitution.md");
57494
+ const constitutionPath = join54(globalDir, "constitution.md");
57383
57495
  if (!existsSync23(constitutionPath)) {
57384
57496
  await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
57385
57497
  logger.info("init", "Created global constitution", { path: constitutionPath });
57386
57498
  } else {
57387
57499
  logger.info("init", "Global constitution already exists", { path: constitutionPath });
57388
57500
  }
57389
- const hooksDir = join53(globalDir, "hooks");
57501
+ const hooksDir = join54(globalDir, "hooks");
57390
57502
  if (!existsSync23(hooksDir)) {
57391
57503
  await mkdir8(hooksDir, { recursive: true });
57392
57504
  logger.info("init", "Created global hooks directory", { path: hooksDir });
@@ -57419,7 +57531,7 @@ async function initProject(projectRoot, options) {
57419
57531
  if (detectedName && !options?.force) {
57420
57532
  const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
57421
57533
  if (collision.collision && collision.existing) {
57422
- const configPath2 = join53(projectDir, "config.json");
57534
+ const configPath2 = join54(projectDir, "config.json");
57423
57535
  throw new NaxError([
57424
57536
  `Project name collision: "${detectedName}"`,
57425
57537
  ` This project: ${projectRoot}`,
@@ -57447,7 +57559,7 @@ async function initProject(projectRoot, options) {
57447
57559
  linter: stack.linter,
57448
57560
  monorepo: stack.monorepo
57449
57561
  });
57450
- const configPath = join53(projectDir, "config.json");
57562
+ const configPath = join54(projectDir, "config.json");
57451
57563
  if (!existsSync23(configPath)) {
57452
57564
  await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
57453
57565
  `);
@@ -57456,14 +57568,14 @@ async function initProject(projectRoot, options) {
57456
57568
  logger.info("init", "Project config already exists", { path: configPath });
57457
57569
  }
57458
57570
  await initContext(projectRoot, { ai: options?.ai, force: options?.force });
57459
- const constitutionPath = join53(projectDir, "constitution.md");
57571
+ const constitutionPath = join54(projectDir, "constitution.md");
57460
57572
  if (!existsSync23(constitutionPath) || options?.force) {
57461
57573
  await Bun.write(constitutionPath, buildConstitution(stack));
57462
57574
  logger.info("init", "Created project constitution", { path: constitutionPath });
57463
57575
  } else {
57464
57576
  logger.info("init", "Project constitution already exists", { path: constitutionPath });
57465
57577
  }
57466
- const hooksDir = join53(projectDir, "hooks");
57578
+ const hooksDir = join54(projectDir, "hooks");
57467
57579
  if (!existsSync23(hooksDir)) {
57468
57580
  await mkdir8(hooksDir, { recursive: true });
57469
57581
  logger.info("init", "Created project hooks directory", { path: hooksDir });
@@ -57525,36 +57637,36 @@ var init_init2 = __esm(() => {
57525
57637
  });
57526
57638
 
57527
57639
  // src/cli/setup-analyze.ts
57528
- import { join as join54 } from "path";
57640
+ import { join as join55 } from "path";
57529
57641
  async function detectPackageManager(workdir) {
57530
57642
  const { fileExists } = _analyzeRepoDeps;
57531
- if (await fileExists(join54(workdir, "bun.lock")))
57643
+ if (await fileExists(join55(workdir, "bun.lock")))
57532
57644
  return { pmRunPrefix: "bun run", pmDlx: "bunx" };
57533
- if (await fileExists(join54(workdir, "bun.lockb")))
57645
+ if (await fileExists(join55(workdir, "bun.lockb")))
57534
57646
  return { pmRunPrefix: "bun run", pmDlx: "bunx" };
57535
- if (await fileExists(join54(workdir, "package-lock.json")))
57647
+ if (await fileExists(join55(workdir, "package-lock.json")))
57536
57648
  return { pmRunPrefix: "npm run", pmDlx: "npx" };
57537
- if (await fileExists(join54(workdir, "yarn.lock")))
57649
+ if (await fileExists(join55(workdir, "yarn.lock")))
57538
57650
  return { pmRunPrefix: "yarn", pmDlx: "yarn dlx" };
57539
- if (await fileExists(join54(workdir, "pnpm-lock.yaml")))
57651
+ if (await fileExists(join55(workdir, "pnpm-lock.yaml")))
57540
57652
  return { pmRunPrefix: "pnpm run", pmDlx: "pnpx" };
57541
57653
  return { pmRunPrefix: "npm run", pmDlx: "npx" };
57542
57654
  }
57543
57655
  async function detectOrchestrator(workdir) {
57544
57656
  const { fileExists } = _analyzeRepoDeps;
57545
- if (await fileExists(join54(workdir, "turbo.json")))
57657
+ if (await fileExists(join55(workdir, "turbo.json")))
57546
57658
  return "turbo";
57547
- if (await fileExists(join54(workdir, "nx.json")))
57659
+ if (await fileExists(join55(workdir, "nx.json")))
57548
57660
  return "nx";
57549
57661
  return "none";
57550
57662
  }
57551
57663
  async function getMissingScripts(packageDir) {
57552
- const pkg = await _analyzeRepoDeps.readJson(join54(packageDir, "package.json"));
57664
+ const pkg = await _analyzeRepoDeps.readJson(join55(packageDir, "package.json"));
57553
57665
  const scripts = pkg?.scripts ?? {};
57554
57666
  return CANONICAL_SCRIPTS.filter((s) => !(s in scripts));
57555
57667
  }
57556
57668
  async function buildPackageFacts(workdir, relativeDir, testPatternMap) {
57557
- const packageDir = relativeDir === "" ? workdir : join54(workdir, relativeDir);
57669
+ const packageDir = relativeDir === "" ? workdir : join55(workdir, relativeDir);
57558
57670
  const [profile, missingScripts] = await Promise.all([
57559
57671
  _analyzeRepoDeps.detectProjectProfile(packageDir, {}),
57560
57672
  getMissingScripts(packageDir)
@@ -57615,7 +57727,7 @@ var init_setup_analyze = __esm(() => {
57615
57727
  });
57616
57728
 
57617
57729
  // src/cli/setup-fill.ts
57618
- import { join as join55 } from "path";
57730
+ import { join as join56 } from "path";
57619
57731
  async function addScriptToPackageJson(pkgJsonPath, key, value) {
57620
57732
  const pkg = await _fillScriptsDeps.readJson(pkgJsonPath) ?? {};
57621
57733
  const scripts = pkg.scripts ?? {};
@@ -57638,18 +57750,18 @@ async function fillScripts(workdir, analysis) {
57638
57750
  if (shape === "single") {
57639
57751
  const rootPkg = packages[0];
57640
57752
  if (rootPkg?.missingScripts.includes(TYPE_CHECK_KEY)) {
57641
- await addScriptToPackageJson(join55(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57753
+ await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57642
57754
  }
57643
57755
  return;
57644
57756
  }
57645
57757
  for (const pkg of packages) {
57646
57758
  if (!pkg.missingScripts.includes(TYPE_CHECK_KEY))
57647
57759
  continue;
57648
- await addScriptToPackageJson(join55(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57760
+ await addScriptToPackageJson(join56(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57649
57761
  }
57650
57762
  if (orchestrator === "turbo" && packages.some((p) => p.missingScripts.includes(TYPE_CHECK_KEY))) {
57651
- await addTurboTask(join55(workdir, "turbo.json"), TYPE_CHECK_KEY);
57652
- await addScriptToPackageJson(join55(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_TURBO_PASSTHROUGH);
57763
+ await addTurboTask(join56(workdir, "turbo.json"), TYPE_CHECK_KEY);
57764
+ await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_TURBO_PASSTHROUGH);
57653
57765
  }
57654
57766
  }
57655
57767
  var TYPE_CHECK_KEY = "type-check", TYPE_CHECK_SCRIPT = "tsc --noEmit -p tsconfig.json", TYPE_CHECK_TURBO_PASSTHROUGH = "turbo run type-check", _fillScriptsDeps;
@@ -57710,11 +57822,11 @@ __export(exports_setup, {
57710
57822
  setupCommand: () => setupCommand,
57711
57823
  _setupDeps: () => _setupDeps
57712
57824
  });
57713
- import { join as join56 } from "path";
57825
+ import { join as join57 } from "path";
57714
57826
  async function setupCommand(options = {}) {
57715
57827
  const workdir = options.dir ?? process.cwd();
57716
- const naxDir = join56(workdir, ".nax");
57717
- const naxConfigPath = join56(naxDir, "config.json");
57828
+ const naxDir = join57(workdir, ".nax");
57829
+ const naxConfigPath = join57(naxDir, "config.json");
57718
57830
  const exists = await _setupDeps.fileExists(naxConfigPath);
57719
57831
  if (exists && !options.force) {
57720
57832
  _setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
@@ -57747,9 +57859,9 @@ ${JSON.stringify(plan.config, null, 2)}`);
57747
57859
  await _setupDeps.mkdir(naxDir);
57748
57860
  await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
57749
57861
  for (const mc of plan.monoConfigs) {
57750
- const monoDir = join56(naxDir, "mono", mc.relativeDir);
57862
+ const monoDir = join57(naxDir, "mono", mc.relativeDir);
57751
57863
  await _setupDeps.mkdir(monoDir);
57752
- await _setupDeps.writeFile(join56(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
57864
+ await _setupDeps.writeFile(join57(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
57753
57865
  }
57754
57866
  const gateResult = await _setupDeps.runGate(workdir, plan.config);
57755
57867
  if (gateResult !== 0) {
@@ -59178,12 +59290,12 @@ var init_loader4 = __esm(() => {
59178
59290
  });
59179
59291
 
59180
59292
  // src/utils/paths.ts
59181
- import { join as join67 } from "path";
59293
+ import { join as join68 } from "path";
59182
59294
  function getRunsDir() {
59183
- return process.env.NAX_RUNS_DIR ?? join67(globalConfigDir(), "runs");
59295
+ return process.env.NAX_RUNS_DIR ?? join68(globalConfigDir(), "runs");
59184
59296
  }
59185
59297
  function getEventsRootDir() {
59186
- return join67(globalConfigDir(), "events");
59298
+ return join68(globalConfigDir(), "events");
59187
59299
  }
59188
59300
  var init_paths3 = __esm(() => {
59189
59301
  init_paths();
@@ -59243,7 +59355,7 @@ var init_command_argv = __esm(() => {
59243
59355
  });
59244
59356
 
59245
59357
  // src/hooks/runner.ts
59246
- import { join as join74 } from "path";
59358
+ import { join as join75 } from "path";
59247
59359
  function createDrainDeadline2(deadlineMs) {
59248
59360
  let timeoutId;
59249
59361
  const promise2 = new Promise((resolve16) => {
@@ -59262,14 +59374,14 @@ async function loadHooksConfig(projectDir, globalDir) {
59262
59374
  let globalHooks = { hooks: {} };
59263
59375
  let projectHooks = { hooks: {} };
59264
59376
  let skipGlobal = false;
59265
- const projectPath = join74(projectDir, "hooks.json");
59377
+ const projectPath = join75(projectDir, "hooks.json");
59266
59378
  const projectData = await loadJsonFile(projectPath, "hooks");
59267
59379
  if (projectData) {
59268
59380
  projectHooks = projectData;
59269
59381
  skipGlobal = projectData.skipGlobal ?? false;
59270
59382
  }
59271
59383
  if (!skipGlobal && globalDir) {
59272
- const globalPath = join74(globalDir, "hooks.json");
59384
+ const globalPath = join75(globalDir, "hooks.json");
59273
59385
  const globalData = await loadJsonFile(globalPath, "hooks");
59274
59386
  if (globalData) {
59275
59387
  globalHooks = globalData;
@@ -59439,7 +59551,7 @@ var package_default;
59439
59551
  var init_package = __esm(() => {
59440
59552
  package_default = {
59441
59553
  name: "@nathapp/nax",
59442
- version: "0.69.1",
59554
+ version: "0.69.3",
59443
59555
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
59444
59556
  type: "module",
59445
59557
  bin: {
@@ -59534,8 +59646,8 @@ var init_version = __esm(() => {
59534
59646
  NAX_VERSION = package_default.version;
59535
59647
  NAX_COMMIT = (() => {
59536
59648
  try {
59537
- if (/^[0-9a-f]{6,10}$/.test("3b2af55b"))
59538
- return "3b2af55b";
59649
+ if (/^[0-9a-f]{6,10}$/.test("7917c240"))
59650
+ return "7917c240";
59539
59651
  } catch {}
59540
59652
  try {
59541
59653
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -60412,15 +60524,15 @@ var init_acceptance_loop = __esm(() => {
60412
60524
 
60413
60525
  // src/session/scratch-purge.ts
60414
60526
  import { mkdir as mkdir12, rename, rm } from "fs/promises";
60415
- import { dirname as dirname12, join as join75 } from "path";
60527
+ import { dirname as dirname12, join as join76 } from "path";
60416
60528
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
60417
- const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
60529
+ const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
60418
60530
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
60419
60531
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
60420
60532
  let purged = 0;
60421
60533
  for (const sessionId of sessionIds) {
60422
- const sessionDir = join75(sessionsDir, sessionId);
60423
- const descriptorPath = join75(sessionDir, "descriptor.json");
60534
+ const sessionDir = join76(sessionsDir, sessionId);
60535
+ const descriptorPath = join76(sessionDir, "descriptor.json");
60424
60536
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
60425
60537
  continue;
60426
60538
  let lastActivityAt;
@@ -60436,7 +60548,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
60436
60548
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
60437
60549
  continue;
60438
60550
  if (archiveInsteadOfDelete) {
60439
- const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60551
+ const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60440
60552
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
60441
60553
  } else {
60442
60554
  await _scratchPurgeDeps.remove(sessionDir);
@@ -61174,12 +61286,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
61174
61286
 
61175
61287
  // src/pipeline/subscribers/events-writer.ts
61176
61288
  import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
61177
- import { basename as basename13, join as join76 } from "path";
61289
+ import { basename as basename13, join as join77 } from "path";
61178
61290
  function wireEventsWriter(bus, feature, runId, workdir) {
61179
61291
  const logger = getSafeLogger();
61180
61292
  const project = basename13(workdir);
61181
- const eventsDir = join76(getEventsRootDir(), project);
61182
- const eventsFile = join76(eventsDir, "events.jsonl");
61293
+ const eventsDir = join77(getEventsRootDir(), project);
61294
+ const eventsFile = join77(eventsDir, "events.jsonl");
61183
61295
  let dirReady = false;
61184
61296
  const write = (line) => {
61185
61297
  return (async () => {
@@ -61360,12 +61472,12 @@ var init_interaction2 = __esm(() => {
61360
61472
 
61361
61473
  // src/pipeline/subscribers/registry.ts
61362
61474
  import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
61363
- import { basename as basename14, join as join77 } from "path";
61475
+ import { basename as basename14, join as join78 } from "path";
61364
61476
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
61365
61477
  const logger = getSafeLogger();
61366
61478
  const project = basename14(workdir);
61367
- const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
61368
- const metaFile = join77(runDir, "meta.json");
61479
+ const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
61480
+ const metaFile = join78(runDir, "meta.json");
61369
61481
  const unsub = bus.on("run:started", (_ev) => {
61370
61482
  return (async () => {
61371
61483
  try {
@@ -61375,8 +61487,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
61375
61487
  project,
61376
61488
  feature,
61377
61489
  workdir,
61378
- statusPath: join77(outputDir, "features", feature, "status.json"),
61379
- eventsDir: join77(outputDir, "features", feature, "runs"),
61490
+ statusPath: join78(outputDir, "features", feature, "status.json"),
61491
+ eventsDir: join78(outputDir, "features", feature, "runs"),
61380
61492
  registeredAt: new Date().toISOString()
61381
61493
  };
61382
61494
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -61622,7 +61734,7 @@ var init_types9 = __esm(() => {
61622
61734
 
61623
61735
  // src/worktree/dependencies.ts
61624
61736
  import { existsSync as existsSync30 } from "fs";
61625
- import { join as join78 } from "path";
61737
+ import { join as join79 } from "path";
61626
61738
  async function prepareWorktreeDependencies(options) {
61627
61739
  const mode = options.config.execution.worktreeDependencies.mode;
61628
61740
  const resolvedCwd = resolveDependencyCwd(options);
@@ -61636,7 +61748,7 @@ async function prepareWorktreeDependencies(options) {
61636
61748
  }
61637
61749
  }
61638
61750
  function resolveDependencyCwd(options) {
61639
- return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
61751
+ return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
61640
61752
  }
61641
61753
  function resolveInheritedDependencies(options, resolvedCwd) {
61642
61754
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -61646,7 +61758,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
61646
61758
  }
61647
61759
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
61648
61760
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
61649
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join78(directory, filename))));
61761
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
61650
61762
  }
61651
61763
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
61652
61764
  const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
@@ -61710,13 +61822,13 @@ __export(exports_manager, {
61710
61822
  });
61711
61823
  import { existsSync as existsSync31, symlinkSync } from "fs";
61712
61824
  import { mkdir as mkdir15 } from "fs/promises";
61713
- import { join as join79 } from "path";
61825
+ import { join as join80 } from "path";
61714
61826
 
61715
61827
  class WorktreeManager {
61716
61828
  async ensureGitExcludes(projectRoot) {
61717
61829
  const logger = getSafeLogger();
61718
- const infoDir = join79(projectRoot, ".git", "info");
61719
- const excludePath = join79(infoDir, "exclude");
61830
+ const infoDir = join80(projectRoot, ".git", "info");
61831
+ const excludePath = join80(infoDir, "exclude");
61720
61832
  try {
61721
61833
  await mkdir15(infoDir, { recursive: true });
61722
61834
  let existing = "";
@@ -61743,7 +61855,7 @@ ${missing.join(`
61743
61855
  }
61744
61856
  async create(projectRoot, storyId) {
61745
61857
  validateStoryId(storyId);
61746
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
61858
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
61747
61859
  const branchName = `nax/${storyId}`;
61748
61860
  try {
61749
61861
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -61784,9 +61896,9 @@ ${missing.join(`
61784
61896
  }
61785
61897
  throw new Error(`Failed to create worktree: ${String(error48)}`);
61786
61898
  }
61787
- const envSource = join79(projectRoot, ".env");
61899
+ const envSource = join80(projectRoot, ".env");
61788
61900
  if (existsSync31(envSource)) {
61789
- const envTarget = join79(worktreePath, ".env");
61901
+ const envTarget = join80(worktreePath, ".env");
61790
61902
  try {
61791
61903
  symlinkSync(envSource, envTarget, "file");
61792
61904
  } catch (error48) {
@@ -61797,7 +61909,7 @@ ${missing.join(`
61797
61909
  }
61798
61910
  async remove(projectRoot, storyId) {
61799
61911
  validateStoryId(storyId);
61800
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
61912
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
61801
61913
  const branchName = `nax/${storyId}`;
61802
61914
  try {
61803
61915
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -62609,10 +62721,10 @@ var init_merge_conflict_rectify = __esm(() => {
62609
62721
  });
62610
62722
 
62611
62723
  // src/execution/pipeline-result-handler.ts
62612
- import { join as join80 } from "path";
62724
+ import { join as join81 } from "path";
62613
62725
  async function removeWorktreeDirectory(projectRoot, storyId) {
62614
62726
  const logger = getSafeLogger();
62615
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62727
+ const worktreePath = join81(projectRoot, ".nax-wt", storyId);
62616
62728
  try {
62617
62729
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
62618
62730
  cwd: projectRoot,
@@ -62829,7 +62941,7 @@ var init_pipeline_result_handler = __esm(() => {
62829
62941
 
62830
62942
  // src/execution/iteration-runner.ts
62831
62943
  import { existsSync as existsSync32 } from "fs";
62832
- import { join as join81 } from "path";
62944
+ import { join as join82 } from "path";
62833
62945
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
62834
62946
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
62835
62947
  if (ctx.dryRun) {
@@ -62854,7 +62966,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62854
62966
  const storyStartTime = Date.now();
62855
62967
  let effectiveWorkdir = ctx.workdir;
62856
62968
  if (ctx.config.execution.storyIsolation === "worktree") {
62857
- const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
62969
+ const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
62858
62970
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
62859
62971
  if (!worktreeExists) {
62860
62972
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -62874,7 +62986,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62874
62986
  }
62875
62987
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
62876
62988
  const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
62877
- const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join81(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
62989
+ const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join82(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
62878
62990
  let dependencyContext;
62879
62991
  if (ctx.config.execution.storyIsolation === "worktree") {
62880
62992
  try {
@@ -62901,7 +63013,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62901
63013
  };
62902
63014
  }
62903
63015
  }
62904
- 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;
63016
+ 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;
62905
63017
  const pipelineContext = {
62906
63018
  config: effectiveConfig,
62907
63019
  rootConfig: ctx.config,
@@ -63103,7 +63215,7 @@ __export(exports_parallel_worker, {
63103
63215
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
63104
63216
  _parallelWorkerDeps: () => _parallelWorkerDeps
63105
63217
  });
63106
- import { join as join82 } from "path";
63218
+ import { join as join83 } from "path";
63107
63219
  function buildWorktreePipelineContext(base, _story) {
63108
63220
  return { ...base, prd: structuredClone(base.prd) };
63109
63221
  }
@@ -63126,7 +63238,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
63126
63238
  story,
63127
63239
  stories: [story],
63128
63240
  projectDir: context.projectDir,
63129
- workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
63241
+ workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
63130
63242
  worktreeDependencyContext: dependencyContext,
63131
63243
  routing,
63132
63244
  storyGitRef: storyGitRef ?? undefined
@@ -64013,7 +64125,7 @@ async function writeStatusFile(filePath, status) {
64013
64125
  var init_status_file = () => {};
64014
64126
 
64015
64127
  // src/execution/status-writer.ts
64016
- import { join as join83 } from "path";
64128
+ import { join as join84 } from "path";
64017
64129
 
64018
64130
  class StatusWriter {
64019
64131
  statusFile;
@@ -64132,7 +64244,7 @@ class StatusWriter {
64132
64244
  if (!this._prd)
64133
64245
  return;
64134
64246
  const safeLogger = getSafeLogger();
64135
- const featureStatusPath = join83(featureDir, "status.json");
64247
+ const featureStatusPath = join84(featureDir, "status.json");
64136
64248
  const write = async () => {
64137
64249
  try {
64138
64250
  const base = this.getSnapshot(totalCost, iterations);
@@ -64566,7 +64678,7 @@ __export(exports_run_initialization, {
64566
64678
  initializeRun: () => initializeRun,
64567
64679
  _reconcileDeps: () => _reconcileDeps
64568
64680
  });
64569
- import { join as join84 } from "path";
64681
+ import { join as join85 } from "path";
64570
64682
  async function reconcileState(prd, prdPath, workdir, config2) {
64571
64683
  const logger = getSafeLogger();
64572
64684
  let reconciledCount = 0;
@@ -64583,7 +64695,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
64583
64695
  });
64584
64696
  continue;
64585
64697
  }
64586
- const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
64698
+ const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
64587
64699
  try {
64588
64700
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
64589
64701
  if (!reviewResult.success) {
@@ -64849,6 +64961,17 @@ async function setupRun(options) {
64849
64961
  featureName: options.feature,
64850
64962
  agentStreamEvents: options.agentStreamEvents
64851
64963
  });
64964
+ try {
64965
+ const workspacePackages = await discoverWorkspacePackages(workdir);
64966
+ if (workspacePackages.length > 0) {
64967
+ await runtime.packages.hydrate(workspacePackages);
64968
+ }
64969
+ } catch (err) {
64970
+ getSafeLogger()?.warn("run-setup", "Per-package config hydration failed \u2014 using root config", {
64971
+ storyId: "_setup",
64972
+ error: errorMessage(err)
64973
+ });
64974
+ }
64852
64975
  await runtime.pidRegistry.cleanupStale();
64853
64976
  const cleanupCrashHandlers = installCrashHandlers({
64854
64977
  statusWriter,
@@ -65015,6 +65138,7 @@ async function setupRun(options) {
65015
65138
  var _runSetupDeps;
65016
65139
  var init_run_setup = __esm(() => {
65017
65140
  init_pipeline();
65141
+ init_test_runners();
65018
65142
  init_paths();
65019
65143
  init_errors();
65020
65144
  init_interaction();
@@ -65024,7 +65148,6 @@ var init_run_setup = __esm(() => {
65024
65148
  init_project();
65025
65149
  init_runtime();
65026
65150
  init_session();
65027
- init_resolver();
65028
65151
  init_version();
65029
65152
  init_crash_recovery();
65030
65153
  init_helpers();
@@ -94376,7 +94499,7 @@ __export(exports_curator, {
94376
94499
  });
94377
94500
  import { readdirSync as readdirSync8 } from "fs";
94378
94501
  import { unlink as unlink4 } from "fs/promises";
94379
- import { basename as basename15, join as join86 } from "path";
94502
+ import { basename as basename15, join as join87 } from "path";
94380
94503
  function getProjectKey(config2, projectDir) {
94381
94504
  return config2.name?.trim() || basename15(projectDir);
94382
94505
  }
@@ -94459,7 +94582,7 @@ async function curatorStatus(options) {
94459
94582
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94460
94583
  const projectKey = getProjectKey(config2, resolved.projectDir);
94461
94584
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94462
- const runsDir = join86(outputDir, "runs");
94585
+ const runsDir = join87(outputDir, "runs");
94463
94586
  const runIds = listRunIds(runsDir);
94464
94587
  let runId;
94465
94588
  if (options.run) {
@@ -94476,8 +94599,8 @@ async function curatorStatus(options) {
94476
94599
  runId = runIds[runIds.length - 1];
94477
94600
  }
94478
94601
  console.log(`Run: ${runId}`);
94479
- const runDir = join86(runsDir, runId);
94480
- const observationsPath = join86(runDir, "observations.jsonl");
94602
+ const runDir = join87(runsDir, runId);
94603
+ const observationsPath = join87(runDir, "observations.jsonl");
94481
94604
  const observations = await parseObservations(observationsPath);
94482
94605
  const counts = new Map;
94483
94606
  for (const obs of observations) {
@@ -94487,7 +94610,7 @@ async function curatorStatus(options) {
94487
94610
  for (const [kind, count] of counts.entries()) {
94488
94611
  console.log(` ${kind}: ${count}`);
94489
94612
  }
94490
- const proposalsPath = join86(runDir, "curator-proposals.md");
94613
+ const proposalsPath = join87(runDir, "curator-proposals.md");
94491
94614
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94492
94615
  if (proposalText !== null) {
94493
94616
  console.log("");
@@ -94501,8 +94624,8 @@ async function curatorCommit(options) {
94501
94624
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94502
94625
  const projectKey = getProjectKey(config2, resolved.projectDir);
94503
94626
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94504
- const runDir = join86(outputDir, "runs", options.runId);
94505
- const proposalsPath = join86(runDir, "curator-proposals.md");
94627
+ const runDir = join87(outputDir, "runs", options.runId);
94628
+ const proposalsPath = join87(runDir, "curator-proposals.md");
94506
94629
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94507
94630
  if (proposalText === null) {
94508
94631
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -94518,7 +94641,7 @@ async function curatorCommit(options) {
94518
94641
  const dropFileState = new Map;
94519
94642
  const skippedDrops = new Set;
94520
94643
  for (const drop2 of drops) {
94521
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94644
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94522
94645
  if (!dropFileState.has(targetPath)) {
94523
94646
  const fileExists2 = await Bun.file(targetPath).exists();
94524
94647
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -94552,7 +94675,7 @@ async function curatorCommit(options) {
94552
94675
  if (skippedDrops.has(drop2)) {
94553
94676
  continue;
94554
94677
  }
94555
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94678
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94556
94679
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
94557
94680
  const filtered = filterDropContent(existing, drop2.description);
94558
94681
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -94561,7 +94684,7 @@ async function curatorCommit(options) {
94561
94684
  }
94562
94685
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
94563
94686
  for (const add2 of adds) {
94564
- const targetPath = join86(resolved.projectDir, add2.canonicalFile);
94687
+ const targetPath = join87(resolved.projectDir, add2.canonicalFile);
94565
94688
  const content = buildAddContent(add2);
94566
94689
  await _curatorCmdDeps.appendFile(targetPath, content);
94567
94690
  modifiedFiles.add(targetPath);
@@ -94598,7 +94721,7 @@ async function curatorDryrun(options) {
94598
94721
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94599
94722
  const projectKey = getProjectKey(config2, resolved.projectDir);
94600
94723
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94601
- const runsDir = join86(outputDir, "runs");
94724
+ const runsDir = join87(outputDir, "runs");
94602
94725
  const runIds = listRunIds(runsDir);
94603
94726
  if (runIds.length === 0) {
94604
94727
  console.log("No runs found.");
@@ -94609,7 +94732,7 @@ async function curatorDryrun(options) {
94609
94732
  console.log(`Run ${options.run} not found in ${runsDir}.`);
94610
94733
  return;
94611
94734
  }
94612
- const observationsPath = join86(runsDir, runId, "observations.jsonl");
94735
+ const observationsPath = join87(runsDir, runId, "observations.jsonl");
94613
94736
  const observations = await parseObservations(observationsPath);
94614
94737
  const thresholds = getThresholds(config2);
94615
94738
  const proposals = runHeuristics(observations, thresholds);
@@ -94650,12 +94773,12 @@ async function curatorGc(options) {
94650
94773
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
94651
94774
  const projectKey = getProjectKey(config2, resolved.projectDir);
94652
94775
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94653
- const perRunsDir = join86(outputDir, "runs");
94776
+ const perRunsDir = join87(outputDir, "runs");
94654
94777
  for (const runId of uniqueRunIds) {
94655
94778
  if (!keepSet.has(runId)) {
94656
- const runDir = join86(perRunsDir, runId);
94657
- await _curatorCmdDeps.removeFile(join86(runDir, "observations.jsonl"));
94658
- await _curatorCmdDeps.removeFile(join86(runDir, "curator-proposals.md"));
94779
+ const runDir = join87(perRunsDir, runId);
94780
+ await _curatorCmdDeps.removeFile(join87(runDir, "observations.jsonl"));
94781
+ await _curatorCmdDeps.removeFile(join87(runDir, "curator-proposals.md"));
94659
94782
  }
94660
94783
  }
94661
94784
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -94700,7 +94823,7 @@ var init_curator2 = __esm(() => {
94700
94823
  init_source();
94701
94824
  import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
94702
94825
  import { homedir as homedir3 } from "os";
94703
- import { basename as basename16, join as join87 } from "path";
94826
+ import { basename as basename16, join as join88 } from "path";
94704
94827
 
94705
94828
  // node_modules/commander/esm.mjs
94706
94829
  var import__ = __toESM(require_commander(), 1);
@@ -94724,12 +94847,12 @@ init_errors();
94724
94847
  init_operations();
94725
94848
 
94726
94849
  // src/plan/strategies/context-builder.ts
94727
- import { join as join38 } from "path";
94850
+ import { join as join39 } from "path";
94728
94851
  init_config();
94729
94852
  init_errors();
94730
94853
  init_interaction();
94731
94854
  async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94732
- const naxDir = join38(workdir, ".nax");
94855
+ const naxDir = join39(workdir, ".nax");
94733
94856
  if (!deps.existsSync(naxDir)) {
94734
94857
  throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
94735
94858
  stage: "plan",
@@ -94737,8 +94860,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94737
94860
  });
94738
94861
  }
94739
94862
  validateFeatureName(options.feature);
94740
- const outputDir = join38(naxDir, "features", options.feature);
94741
- const outputPath = join38(outputDir, "prd.json");
94863
+ const outputDir = join39(naxDir, "features", options.feature);
94864
+ const outputPath = join39(outputDir, "prd.json");
94742
94865
  const [specContent, sourceRoots, pkg] = await Promise.all([
94743
94866
  deps.readFile(options.from),
94744
94867
  deps.scanSourceRoots(workdir),
@@ -94753,7 +94876,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94753
94876
  ...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
94754
94877
  ];
94755
94878
  const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
94756
- const packageJson = await deps.readPackageJsonAt(join38(workdir, relativePath, "package.json"));
94879
+ const packageJson = await deps.readPackageJsonAt(join39(workdir, relativePath, "package.json"));
94757
94880
  return buildPackageSummary(relativePath, packageJson);
94758
94881
  }));
94759
94882
  const projectName = detectProjectName(workdir, pkg);
@@ -95356,7 +95479,7 @@ init_interaction();
95356
95479
  init_prd();
95357
95480
  init_runtime();
95358
95481
  import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
95359
- import { basename as basename7, join as join43, resolve as resolve14 } from "path";
95482
+ import { basename as basename7, join as join44, resolve as resolve14 } from "path";
95360
95483
  var _statusFeaturesDeps = {
95361
95484
  projectOutputDir,
95362
95485
  loadConfig
@@ -95370,7 +95493,7 @@ function isPidAlive(pid) {
95370
95493
  }
95371
95494
  }
95372
95495
  async function loadStatusFile(featureDir) {
95373
- const statusPath = join43(featureDir, "status.json");
95496
+ const statusPath = join44(featureDir, "status.json");
95374
95497
  if (!existsSync17(statusPath)) {
95375
95498
  return null;
95376
95499
  }
@@ -95385,7 +95508,7 @@ async function loadProjectStatusFile(projectDir) {
95385
95508
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95386
95509
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95387
95510
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95388
- const statusPath = join43(outputDir, "status.json");
95511
+ const statusPath = join44(outputDir, "status.json");
95389
95512
  if (!existsSync17(statusPath)) {
95390
95513
  return null;
95391
95514
  }
@@ -95397,7 +95520,7 @@ async function loadProjectStatusFile(projectDir) {
95397
95520
  }
95398
95521
  }
95399
95522
  async function getFeatureSummary(featureName, featureDir) {
95400
- const prdPath = join43(featureDir, "prd.json");
95523
+ const prdPath = join44(featureDir, "prd.json");
95401
95524
  if (!existsSync17(prdPath)) {
95402
95525
  return {
95403
95526
  name: featureName,
@@ -95440,7 +95563,7 @@ async function getFeatureSummary(featureName, featureDir) {
95440
95563
  };
95441
95564
  }
95442
95565
  }
95443
- const runsDir = join43(featureDir, "runs");
95566
+ const runsDir = join44(featureDir, "runs");
95444
95567
  if (existsSync17(runsDir)) {
95445
95568
  const runs = readdirSync3(runsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl") && e.name !== "latest.jsonl").map((e) => e.name).sort().reverse();
95446
95569
  if (runs.length > 0) {
@@ -95454,7 +95577,7 @@ async function displayAllFeatures(projectDir) {
95454
95577
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95455
95578
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95456
95579
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95457
- const featuresDir = join43(outputDir, "features");
95580
+ const featuresDir = join44(outputDir, "features");
95458
95581
  if (!existsSync17(featuresDir)) {
95459
95582
  console.log(source_default.dim("No features found."));
95460
95583
  return;
@@ -95495,7 +95618,7 @@ async function displayAllFeatures(projectDir) {
95495
95618
  console.log();
95496
95619
  }
95497
95620
  }
95498
- const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join43(featuresDir, name))));
95621
+ const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join44(featuresDir, name))));
95499
95622
  console.log(source_default.bold(`\uD83D\uDCCA Features
95500
95623
  `));
95501
95624
  const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
@@ -95521,7 +95644,7 @@ async function displayAllFeatures(projectDir) {
95521
95644
  console.log();
95522
95645
  }
95523
95646
  async function displayFeatureDetails(featureName, featureDir) {
95524
- const prdPath = join43(featureDir, "prd.json");
95647
+ const prdPath = join44(featureDir, "prd.json");
95525
95648
  if (!existsSync17(prdPath)) {
95526
95649
  console.log(source_default.bold(`
95527
95650
  \uD83D\uDCCA ${featureName}
@@ -95667,7 +95790,7 @@ async function displayFeatureStatus(options = {}) {
95667
95790
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95668
95791
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95669
95792
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95670
- featureDir = join43(outputDir, "features", options.feature);
95793
+ featureDir = join44(outputDir, "features", options.feature);
95671
95794
  } else {
95672
95795
  const resolved = resolveProject({ feature: options.feature });
95673
95796
  if (!resolved.featureDir) {
@@ -95687,7 +95810,7 @@ init_errors();
95687
95810
  init_logger2();
95688
95811
  init_runtime();
95689
95812
  import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
95690
- import { basename as basename8, join as join44 } from "path";
95813
+ import { basename as basename8, join as join45 } from "path";
95691
95814
  async function resolveOutputDir2(workdir, override) {
95692
95815
  if (override)
95693
95816
  return override;
@@ -95711,7 +95834,7 @@ async function runsListCommand(options) {
95711
95834
  const logger = getLogger();
95712
95835
  const { feature, workdir } = options;
95713
95836
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
95714
- const runsDir = join44(outputDir, "features", feature, "runs");
95837
+ const runsDir = join45(outputDir, "features", feature, "runs");
95715
95838
  if (!existsSync18(runsDir)) {
95716
95839
  logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
95717
95840
  return;
@@ -95723,7 +95846,7 @@ async function runsListCommand(options) {
95723
95846
  }
95724
95847
  logger.info("cli", `Runs for ${feature}`, { count: files.length });
95725
95848
  for (const file3 of files.sort().reverse()) {
95726
- const logPath = join44(runsDir, file3);
95849
+ const logPath = join45(runsDir, file3);
95727
95850
  const entries = await parseRunLog(logPath);
95728
95851
  const startEvent = entries.find((e) => e.message === "run.start");
95729
95852
  const completeEvent = entries.find((e) => e.message === "run.complete");
@@ -95750,7 +95873,7 @@ async function runsShowCommand(options) {
95750
95873
  const logger = getLogger();
95751
95874
  const { runId, feature, workdir } = options;
95752
95875
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
95753
- const logPath = join44(outputDir, "features", feature, "runs", `${runId}.jsonl`);
95876
+ const logPath = join45(outputDir, "features", feature, "runs", `${runId}.jsonl`);
95754
95877
  if (!existsSync18(logPath)) {
95755
95878
  logger.error("cli", "Run not found", { runId, feature, logPath });
95756
95879
  throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
@@ -95866,7 +95989,7 @@ init_source();
95866
95989
  init_loader();
95867
95990
  init_generator2();
95868
95991
  import { existsSync as existsSync24 } from "fs";
95869
- import { join as join61 } from "path";
95992
+ import { join as join62 } from "path";
95870
95993
  var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
95871
95994
  async function generateCommand(options) {
95872
95995
  const workdir = options.dir ?? process.cwd();
@@ -95909,7 +96032,7 @@ async function generateCommand(options) {
95909
96032
  return;
95910
96033
  }
95911
96034
  if (options.package) {
95912
- const packageDir = join61(workdir, options.package);
96035
+ const packageDir = join62(workdir, options.package);
95913
96036
  if (dryRun) {
95914
96037
  console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
95915
96038
  }
@@ -95929,8 +96052,8 @@ async function generateCommand(options) {
95929
96052
  process.exit(1);
95930
96053
  return;
95931
96054
  }
95932
- const contextPath = options.context ? join61(workdir, options.context) : join61(workdir, ".nax/context.md");
95933
- const outputDir = options.output ? join61(workdir, options.output) : workdir;
96055
+ const contextPath = options.context ? join62(workdir, options.context) : join62(workdir, ".nax/context.md");
96056
+ const outputDir = options.output ? join62(workdir, options.output) : workdir;
95934
96057
  const autoInject = !options.noAutoInject;
95935
96058
  if (!existsSync24(contextPath)) {
95936
96059
  console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
@@ -96036,7 +96159,7 @@ async function generateCommand(options) {
96036
96159
  // src/cli/config-display.ts
96037
96160
  init_loader();
96038
96161
  import { existsSync as existsSync26 } from "fs";
96039
- import { join as join63 } from "path";
96162
+ import { join as join64 } from "path";
96040
96163
 
96041
96164
  // src/cli/config-descriptions.ts
96042
96165
  var FIELD_DESCRIPTIONS = {
@@ -96288,7 +96411,7 @@ function deepEqual(a, b) {
96288
96411
  init_defaults();
96289
96412
  init_loader();
96290
96413
  import { existsSync as existsSync25 } from "fs";
96291
- import { join as join62 } from "path";
96414
+ import { join as join63 } from "path";
96292
96415
  async function loadConfigFile(path18) {
96293
96416
  if (!existsSync25(path18))
96294
96417
  return null;
@@ -96310,7 +96433,7 @@ async function loadProjectConfig() {
96310
96433
  const projectDir = findProjectDir();
96311
96434
  if (!projectDir)
96312
96435
  return null;
96313
- const projectPath = join62(projectDir, "config.json");
96436
+ const projectPath = join63(projectDir, "config.json");
96314
96437
  return await loadConfigFile(projectPath);
96315
96438
  }
96316
96439
 
@@ -96370,7 +96493,7 @@ async function configCommand(config2, options = {}) {
96370
96493
  function determineConfigSources() {
96371
96494
  const globalPath = globalConfigPath();
96372
96495
  const projectDir = findProjectDir();
96373
- const projectPath = projectDir ? join63(projectDir, "config.json") : null;
96496
+ const projectPath = projectDir ? join64(projectDir, "config.json") : null;
96374
96497
  return {
96375
96498
  global: fileExists(globalPath) ? globalPath : null,
96376
96499
  project: projectPath && fileExists(projectPath) ? projectPath : null
@@ -96519,15 +96642,15 @@ init_paths();
96519
96642
  init_profile();
96520
96643
  import { mkdirSync as mkdirSync5 } from "fs";
96521
96644
  import { readdirSync as readdirSync5 } from "fs";
96522
- import { join as join64 } from "path";
96645
+ import { join as join65 } from "path";
96523
96646
  var _profileCLIDeps = {
96524
96647
  env: process.env
96525
96648
  };
96526
96649
  var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
96527
96650
  var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
96528
96651
  async function profileListCommand(startDir) {
96529
- const globalProfilesDir = join64(globalConfigDir(), "profiles");
96530
- const projectProfilesDir = join64(projectConfigDir(startDir), "profiles");
96652
+ const globalProfilesDir = join65(globalConfigDir(), "profiles");
96653
+ const projectProfilesDir = join65(projectConfigDir(startDir), "profiles");
96531
96654
  const globalProfiles = scanProfileDir(globalProfilesDir);
96532
96655
  const projectProfiles = scanProfileDir(projectProfilesDir);
96533
96656
  const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
@@ -96586,7 +96709,7 @@ function maskProfileValues(obj) {
96586
96709
  return result;
96587
96710
  }
96588
96711
  async function profileUseCommand(profileName, startDir) {
96589
- const configPath = join64(projectConfigDir(startDir), "config.json");
96712
+ const configPath = join65(projectConfigDir(startDir), "config.json");
96590
96713
  const configFile = Bun.file(configPath);
96591
96714
  let existing = {};
96592
96715
  if (await configFile.exists()) {
@@ -96605,8 +96728,8 @@ async function profileCurrentCommand(startDir) {
96605
96728
  return resolveProfileName({}, _profileCLIDeps.env, startDir);
96606
96729
  }
96607
96730
  async function profileCreateCommand(profileName, startDir) {
96608
- const profilesDir = join64(projectConfigDir(startDir), "profiles");
96609
- const profilePath = join64(profilesDir, `${profileName}.json`);
96731
+ const profilesDir = join65(projectConfigDir(startDir), "profiles");
96732
+ const profilePath = join65(profilesDir, `${profileName}.json`);
96610
96733
  const profileFile = Bun.file(profilePath);
96611
96734
  if (await profileFile.exists()) {
96612
96735
  throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
@@ -96728,7 +96851,7 @@ async function contextInspectCommand(options) {
96728
96851
  init_canonical_loader();
96729
96852
  init_errors();
96730
96853
  import { mkdir as mkdir11 } from "fs/promises";
96731
- import { basename as basename12, join as join65 } from "path";
96854
+ import { basename as basename12, join as join66 } from "path";
96732
96855
  var _rulesCLIDeps = {
96733
96856
  readFile: async (path18) => Bun.file(path18).text(),
96734
96857
  writeFile: async (path18, content) => {
@@ -96737,7 +96860,7 @@ var _rulesCLIDeps = {
96737
96860
  fileExists: async (path18) => Bun.file(path18).exists(),
96738
96861
  globInDir: (dir) => {
96739
96862
  try {
96740
- return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join65(dir, f));
96863
+ return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join66(dir, f));
96741
96864
  } catch {
96742
96865
  return [];
96743
96866
  }
@@ -96786,7 +96909,7 @@ ${r.content}`).join(`
96786
96909
  `);
96787
96910
  const shimContent = `${header + body}
96788
96911
  `;
96789
- const shimPath = join65(workdir, shimFileName);
96912
+ const shimPath = join66(workdir, shimFileName);
96790
96913
  if (options.dryRun) {
96791
96914
  console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
96792
96915
  return;
@@ -96815,14 +96938,14 @@ function neutralizeContent(content) {
96815
96938
  }
96816
96939
  async function collectMigrationSources(workdir) {
96817
96940
  const sources = [];
96818
- const claudeMdPath = join65(workdir, "CLAUDE.md");
96941
+ const claudeMdPath = join66(workdir, "CLAUDE.md");
96819
96942
  if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
96820
96943
  const content = await _rulesCLIDeps.readFile(claudeMdPath);
96821
96944
  if (content.trim()) {
96822
96945
  sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
96823
96946
  }
96824
96947
  }
96825
- const rulesDir = join65(workdir, ".claude", "rules");
96948
+ const rulesDir = join66(workdir, ".claude", "rules");
96826
96949
  const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
96827
96950
  for (const filePath of ruleFiles) {
96828
96951
  try {
@@ -96842,7 +96965,7 @@ async function rulesMigrateCommand(options) {
96842
96965
  console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
96843
96966
  return;
96844
96967
  }
96845
- const targetDir = join65(workdir, CANONICAL_RULES_DIR);
96968
+ const targetDir = join66(workdir, CANONICAL_RULES_DIR);
96846
96969
  if (!options.dryRun) {
96847
96970
  try {
96848
96971
  await _rulesCLIDeps.mkdir(targetDir);
@@ -96853,7 +96976,7 @@ async function rulesMigrateCommand(options) {
96853
96976
  let written = 0;
96854
96977
  let skipped = 0;
96855
96978
  for (const { sourcePath, targetFileName, content } of sources) {
96856
- const targetPath = join65(targetDir, targetFileName);
96979
+ const targetPath = join66(targetDir, targetFileName);
96857
96980
  if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
96858
96981
  console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
96859
96982
  skipped++;
@@ -96892,7 +97015,7 @@ function collectCanonicalRuleRoots(workdir) {
96892
97015
  const packageRel = normalized.slice(0, idx);
96893
97016
  if (!packageRel)
96894
97017
  continue;
96895
- roots.add(join65(workdir, packageRel));
97018
+ roots.add(join66(workdir, packageRel));
96896
97019
  }
96897
97020
  return [...roots].sort();
96898
97021
  }
@@ -96914,7 +97037,7 @@ init_logger2();
96914
97037
  init_detect2();
96915
97038
  init_workspace();
96916
97039
  init_common();
96917
- import { join as join66 } from "path";
97040
+ import { join as join67 } from "path";
96918
97041
  function resolveEffective(detected, configPatterns) {
96919
97042
  if (configPatterns !== undefined)
96920
97043
  return "config";
@@ -96999,7 +97122,7 @@ async function detectCommand(options) {
96999
97122
  const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
97000
97123
  const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
97001
97124
  const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
97002
- const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
97125
+ const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
97003
97126
  const pkgRaw = await loadRawConfig(pkgConfigPath);
97004
97127
  const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
97005
97128
  const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
@@ -97053,13 +97176,13 @@ async function detectCommand(options) {
97053
97176
  if (rootDetected.confidence === "empty") {
97054
97177
  console.log(source_default.yellow(" root: skipped (empty detection)"));
97055
97178
  } else {
97056
- const rootConfigPath = join66(workdir, ".nax", "config.json");
97179
+ const rootConfigPath = join67(workdir, ".nax", "config.json");
97057
97180
  try {
97058
97181
  const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
97059
97182
  if (status === "skipped") {
97060
97183
  console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
97061
97184
  } else {
97062
- console.log(source_default.green(` root: ${status} \u2192 ${join66(".nax", "config.json")}`));
97185
+ console.log(source_default.green(` root: ${status} \u2192 ${join67(".nax", "config.json")}`));
97063
97186
  }
97064
97187
  } catch (err) {
97065
97188
  console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
@@ -97072,13 +97195,13 @@ async function detectCommand(options) {
97072
97195
  console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
97073
97196
  continue;
97074
97197
  }
97075
- const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
97198
+ const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
97076
97199
  try {
97077
97200
  const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
97078
97201
  if (status === "skipped") {
97079
97202
  console.log(source_default.dim(` ${dir}: skipped (already set)`));
97080
97203
  } else {
97081
- console.log(source_default.green(` ${dir}: ${status} \u2192 ${join66(".nax", "mono", dir, "config.json")}`));
97204
+ console.log(source_default.green(` ${dir}: ${status} \u2192 ${join67(".nax", "mono", dir, "config.json")}`));
97082
97205
  }
97083
97206
  } catch (err) {
97084
97207
  console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
@@ -97096,19 +97219,19 @@ async function detectCommand(options) {
97096
97219
  // src/commands/logs.ts
97097
97220
  init_common();
97098
97221
  import { existsSync as existsSync28 } from "fs";
97099
- import { join as join70 } from "path";
97222
+ import { join as join71 } from "path";
97100
97223
 
97101
97224
  // src/commands/logs-formatter.ts
97102
97225
  init_source();
97103
97226
  init_formatter();
97104
97227
  import { readdirSync as readdirSync7 } from "fs";
97105
- import { join as join69 } from "path";
97228
+ import { join as join70 } from "path";
97106
97229
 
97107
97230
  // src/commands/logs-reader.ts
97108
97231
  init_paths3();
97109
97232
  import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
97110
97233
  import { readdir as readdir4 } from "fs/promises";
97111
- import { join as join68 } from "path";
97234
+ import { join as join69 } from "path";
97112
97235
  var _logsReaderDeps = {
97113
97236
  getRunsDir
97114
97237
  };
@@ -97122,7 +97245,7 @@ async function resolveRunFileFromRegistry(runId) {
97122
97245
  }
97123
97246
  let matched = null;
97124
97247
  for (const entry of entries) {
97125
- const metaPath = join68(runsDir, entry, "meta.json");
97248
+ const metaPath = join69(runsDir, entry, "meta.json");
97126
97249
  try {
97127
97250
  const meta3 = await Bun.file(metaPath).json();
97128
97251
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -97144,14 +97267,14 @@ async function resolveRunFileFromRegistry(runId) {
97144
97267
  return null;
97145
97268
  }
97146
97269
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
97147
- return join68(matched.eventsDir, specificFile ?? files[0]);
97270
+ return join69(matched.eventsDir, specificFile ?? files[0]);
97148
97271
  }
97149
97272
  async function selectRunFile(runsDir) {
97150
97273
  const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97151
97274
  if (files.length === 0) {
97152
97275
  return null;
97153
97276
  }
97154
- return join68(runsDir, files[0]);
97277
+ return join69(runsDir, files[0]);
97155
97278
  }
97156
97279
  async function extractRunSummary(filePath) {
97157
97280
  const file3 = Bun.file(filePath);
@@ -97237,7 +97360,7 @@ Runs:
97237
97360
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
97238
97361
  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"));
97239
97362
  for (const file3 of files) {
97240
- const filePath = join69(runsDir, file3);
97363
+ const filePath = join70(runsDir, file3);
97241
97364
  const summary = await extractRunSummary(filePath);
97242
97365
  const timestamp = file3.replace(".jsonl", "");
97243
97366
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -97351,7 +97474,7 @@ async function logsCommand(options) {
97351
97474
  return;
97352
97475
  }
97353
97476
  const resolved = resolveProject({ dir: options.dir });
97354
- const naxDir = join70(resolved.projectDir, ".nax");
97477
+ const naxDir = join71(resolved.projectDir, ".nax");
97355
97478
  const configPath = resolved.configPath;
97356
97479
  const configFile = Bun.file(configPath);
97357
97480
  const config2 = await configFile.json();
@@ -97359,8 +97482,8 @@ async function logsCommand(options) {
97359
97482
  if (!featureName) {
97360
97483
  throw new Error("No feature specified in config.json");
97361
97484
  }
97362
- const featureDir = join70(naxDir, "features", featureName);
97363
- const runsDir = join70(featureDir, "runs");
97485
+ const featureDir = join71(naxDir, "features", featureName);
97486
+ const runsDir = join71(featureDir, "runs");
97364
97487
  if (!existsSync28(runsDir)) {
97365
97488
  throw new Error(`No runs directory found for feature: ${featureName}`);
97366
97489
  }
@@ -97386,7 +97509,7 @@ init_prd();
97386
97509
  init_precheck();
97387
97510
  init_common();
97388
97511
  import { existsSync as existsSync29 } from "fs";
97389
- import { join as join71 } from "path";
97512
+ import { join as join72 } from "path";
97390
97513
  async function precheckCommand(options) {
97391
97514
  const resolved = resolveProject({
97392
97515
  dir: options.dir,
@@ -97408,9 +97531,9 @@ async function precheckCommand(options) {
97408
97531
  process.exit(1);
97409
97532
  }
97410
97533
  }
97411
- const naxDir = join71(resolved.projectDir, ".nax");
97412
- const featureDir = join71(naxDir, "features", featureName);
97413
- const prdPath = join71(featureDir, "prd.json");
97534
+ const naxDir = join72(resolved.projectDir, ".nax");
97535
+ const featureDir = join72(naxDir, "features", featureName);
97536
+ const prdPath = join72(featureDir, "prd.json");
97414
97537
  if (!existsSync29(featureDir)) {
97415
97538
  console.error(source_default.red(`Feature not found: ${featureName}`));
97416
97539
  process.exit(1);
@@ -97433,7 +97556,7 @@ async function precheckCommand(options) {
97433
97556
  init_source();
97434
97557
  init_paths3();
97435
97558
  import { readdir as readdir5 } from "fs/promises";
97436
- import { join as join72 } from "path";
97559
+ import { join as join73 } from "path";
97437
97560
  var DEFAULT_LIMIT = 20;
97438
97561
  var _runsCmdDeps = {
97439
97562
  getRunsDir
@@ -97488,7 +97611,7 @@ async function runsCommand(options = {}) {
97488
97611
  }
97489
97612
  const rows = [];
97490
97613
  for (const entry of entries) {
97491
- const metaPath = join72(runsDir, entry, "meta.json");
97614
+ const metaPath = join73(runsDir, entry, "meta.json");
97492
97615
  let meta3;
97493
97616
  try {
97494
97617
  meta3 = await Bun.file(metaPath).json();
@@ -97565,7 +97688,7 @@ async function runsCommand(options = {}) {
97565
97688
 
97566
97689
  // src/commands/unlock.ts
97567
97690
  init_source();
97568
- import { join as join73 } from "path";
97691
+ import { join as join74 } from "path";
97569
97692
  function isProcessAlive2(pid) {
97570
97693
  try {
97571
97694
  process.kill(pid, 0);
@@ -97580,7 +97703,7 @@ function formatLockAge(ageMs) {
97580
97703
  }
97581
97704
  async function unlockCommand(options) {
97582
97705
  const workdir = options.dir ?? process.cwd();
97583
- const lockPath = join73(workdir, "nax.lock");
97706
+ const lockPath = join74(workdir, "nax.lock");
97584
97707
  const lockFile = Bun.file(lockPath);
97585
97708
  const exists = await lockFile.exists();
97586
97709
  if (!exists) {
@@ -98128,6 +98251,7 @@ init_run_regression();
98128
98251
 
98129
98252
  // src/execution/index.ts
98130
98253
  init_story_orchestrator();
98254
+ init_story_orchestrator_logging();
98131
98255
  init_plan_inputs();
98132
98256
  init_build_plan_for_strategy();
98133
98257
  init_post_run();
@@ -106004,7 +106128,7 @@ Next: nax generate --package ${options.package}`));
106004
106128
  }
106005
106129
  return;
106006
106130
  }
106007
- const naxDir = join87(workdir, ".nax");
106131
+ const naxDir = join88(workdir, ".nax");
106008
106132
  if (existsSync35(naxDir) && !options.force) {
106009
106133
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
106010
106134
  return;
@@ -106033,11 +106157,11 @@ Next: nax generate --package ${options.package}`));
106033
106157
  }
106034
106158
  }
106035
106159
  }
106036
- mkdirSync7(join87(naxDir, "features"), { recursive: true });
106037
- mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
106160
+ mkdirSync7(join88(naxDir, "features"), { recursive: true });
106161
+ mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
106038
106162
  const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
106039
- await Bun.write(join87(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106040
- await Bun.write(join87(naxDir, "hooks.json"), JSON.stringify({
106163
+ await Bun.write(join88(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106164
+ await Bun.write(join88(naxDir, "hooks.json"), JSON.stringify({
106041
106165
  hooks: {
106042
106166
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
106043
106167
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -106045,12 +106169,12 @@ Next: nax generate --package ${options.package}`));
106045
106169
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
106046
106170
  }
106047
106171
  }, null, 2));
106048
- await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
106172
+ await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
106049
106173
  *.tmp
106050
106174
  .paused.json
106051
106175
  .nax-verifier-verdict.json
106052
106176
  `);
106053
- await Bun.write(join87(naxDir, "context.md"), `# Project Context
106177
+ await Bun.write(join88(naxDir, "context.md"), `# Project Context
106054
106178
 
106055
106179
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
106056
106180
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -106198,8 +106322,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
106198
106322
  console.error(source_default.red("nax not initialized. Run: nax init"));
106199
106323
  process.exit(1);
106200
106324
  }
106201
- const featureDir = join87(naxDir, "features", options.feature);
106202
- const prdPath = join87(featureDir, "prd.json");
106325
+ const featureDir = join88(naxDir, "features", options.feature);
106326
+ const prdPath = join88(featureDir, "prd.json");
106203
106327
  if (options.plan && options.from) {
106204
106328
  if (existsSync35(prdPath) && !options.force) {
106205
106329
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
@@ -106221,10 +106345,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106221
106345
  }
106222
106346
  }
106223
106347
  try {
106224
- const planLogDir = join87(featureDir, "plan");
106348
+ const planLogDir = join88(featureDir, "plan");
106225
106349
  mkdirSync7(planLogDir, { recursive: true });
106226
106350
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106227
- const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
106351
+ const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
106228
106352
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106229
106353
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106230
106354
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -106270,10 +106394,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106270
106394
  resetLogger();
106271
106395
  const projectKey = config2.name?.trim() || basename16(workdir);
106272
106396
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
106273
- const runsDir = join87(outputDir, "features", options.feature, "runs");
106397
+ const runsDir = join88(outputDir, "features", options.feature, "runs");
106274
106398
  mkdirSync7(runsDir, { recursive: true });
106275
106399
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106276
- const logFilePath = join87(runsDir, `${runId}.jsonl`);
106400
+ const logFilePath = join88(runsDir, `${runId}.jsonl`);
106277
106401
  const isTTY = process.stdout.isTTY ?? false;
106278
106402
  const headlessFlag = options.headless ?? false;
106279
106403
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -106291,7 +106415,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106291
106415
  config2.agent.default = options.agent;
106292
106416
  }
106293
106417
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
106294
- const globalNaxDir = join87(homedir3(), ".nax");
106418
+ const globalNaxDir = join88(homedir3(), ".nax");
106295
106419
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
106296
106420
  const eventEmitter = new PipelineEventEmitter;
106297
106421
  const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
@@ -106311,12 +106435,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
106311
106435
  events: eventEmitter,
106312
106436
  ptyOptions: null,
106313
106437
  agentStreamEvents,
106314
- queueFilePath: join87(workdir, ".queue.txt")
106438
+ queueFilePath: join88(workdir, ".queue.txt")
106315
106439
  });
106316
106440
  } else {
106317
106441
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
106318
106442
  }
106319
- const statusFilePath = join87(outputDir, "status.json");
106443
+ const statusFilePath = join88(outputDir, "status.json");
106320
106444
  let parallel;
106321
106445
  if (options.parallel !== undefined) {
106322
106446
  parallel = Number.parseInt(options.parallel, 10);
@@ -106343,7 +106467,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106343
106467
  skipPrecheck: options.skipPrecheck ?? false,
106344
106468
  agentStreamEvents
106345
106469
  });
106346
- const latestSymlink = join87(runsDir, "latest.jsonl");
106470
+ const latestSymlink = join88(runsDir, "latest.jsonl");
106347
106471
  try {
106348
106472
  if (existsSync35(latestSymlink)) {
106349
106473
  Bun.spawnSync(["rm", latestSymlink]);
@@ -106404,9 +106528,9 @@ features.command("create <name>").description("Create a new feature").option("-d
106404
106528
  console.error(source_default.red("nax not initialized. Run: nax init"));
106405
106529
  process.exit(1);
106406
106530
  }
106407
- const featureDir = join87(naxDir, "features", name);
106531
+ const featureDir = join88(naxDir, "features", name);
106408
106532
  mkdirSync7(featureDir, { recursive: true });
106409
- await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
106533
+ await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
106410
106534
 
106411
106535
  ## Overview
106412
106536
 
@@ -106439,7 +106563,7 @@ features.command("create <name>").description("Create a new feature").option("-d
106439
106563
 
106440
106564
  <!-- What this feature explicitly does NOT cover. -->
106441
106565
  `);
106442
- await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
106566
+ await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
106443
106567
 
106444
106568
  Created: ${new Date().toISOString()}
106445
106569
 
@@ -106465,7 +106589,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106465
106589
  console.error(source_default.red("nax not initialized."));
106466
106590
  process.exit(1);
106467
106591
  }
106468
- const featuresDir = join87(naxDir, "features");
106592
+ const featuresDir = join88(naxDir, "features");
106469
106593
  if (!existsSync35(featuresDir)) {
106470
106594
  console.log(source_default.dim("No features yet."));
106471
106595
  return;
@@ -106480,7 +106604,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106480
106604
  Features:
106481
106605
  `));
106482
106606
  for (const name of entries) {
106483
- const prdPath = join87(featuresDir, name, "prd.json");
106607
+ const prdPath = join88(featuresDir, name, "prd.json");
106484
106608
  if (existsSync35(prdPath)) {
106485
106609
  const prd = await loadPRD(prdPath);
106486
106610
  const c = countStories(prd);
@@ -106515,10 +106639,10 @@ Use: nax plan -f <feature> --from <spec>`));
106515
106639
  cliOverrides.profile = options.profile;
106516
106640
  }
106517
106641
  const config2 = await loadConfig(workdir, cliOverrides);
106518
- const featureLogDir = join87(naxDir, "features", options.feature, "plan");
106642
+ const featureLogDir = join88(naxDir, "features", options.feature, "plan");
106519
106643
  mkdirSync7(featureLogDir, { recursive: true });
106520
106644
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106521
- const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
106645
+ const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
106522
106646
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106523
106647
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106524
106648
  try {