@nathapp/nax 0.69.1 → 0.69.2

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 +570 -454
  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) {
@@ -38708,7 +38754,7 @@ var init_full_suite_gate = __esm(() => {
38708
38754
  config: fullSuiteGateConfigSelector,
38709
38755
  async execute(input, ctx, deps = _fullSuiteGateDeps) {
38710
38756
  const logger = getLogger();
38711
- const ctxConfig = ctx.config;
38757
+ const ctxConfig = ctx.packageView.config;
38712
38758
  const enabled = ctxConfig?.execution?.regressionGate?.enabled ?? true;
38713
38759
  if (!enabled) {
38714
38760
  logger.info("verify[regression]", "Regression gate disabled \u2014 skipping full-suite run", {
@@ -38979,7 +39025,7 @@ var init_apply_test_edit_declarations = __esm(() => {
38979
39025
  });
38980
39026
 
38981
39027
  // src/operations/validate-mock-structure-files.ts
38982
- import { join as join24 } from "path";
39028
+ import { join as join25 } from "path";
38983
39029
  async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
38984
39030
  const fileExists = deps?.fileExists ?? defaultFileExists;
38985
39031
  const valid = [];
@@ -38992,7 +39038,7 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
38992
39038
  const files = d.files ?? [d.file];
38993
39039
  let allValid = true;
38994
39040
  for (const file3 of files) {
38995
- const absolutePath = join24(packageDir, file3);
39041
+ const absolutePath = join25(packageDir, file3);
38996
39042
  const exists = await fileExists(absolutePath);
38997
39043
  if (!exists) {
38998
39044
  allValid = false;
@@ -39214,9 +39260,9 @@ var init_mechanical_lintfix_strategy = __esm(() => {
39214
39260
  stage: "rectification",
39215
39261
  config: qualityConfigSelector,
39216
39262
  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;
39263
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39264
+ const broad = quality?.commands?.lintFix;
39265
+ const scoped2 = quality?.commands?.lintFixScoped;
39220
39266
  const command = buildCommand(broad, scoped2, input.scopeFiles);
39221
39267
  if (!command)
39222
39268
  return { applied: true, exitCode: 0 };
@@ -39225,7 +39271,7 @@ var init_mechanical_lintfix_strategy = __esm(() => {
39225
39271
  command,
39226
39272
  workdir: input.workdir,
39227
39273
  storyId: input.storyId,
39228
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39274
+ stripEnvVars: quality?.stripEnvVars ?? []
39229
39275
  });
39230
39276
  return { applied: true, exitCode: result.exitCode };
39231
39277
  }
@@ -39273,9 +39319,9 @@ var init_mechanical_formatfix_strategy = __esm(() => {
39273
39319
  stage: "rectification",
39274
39320
  config: qualityConfigSelector,
39275
39321
  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;
39322
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39323
+ const broad = quality?.commands?.formatFix;
39324
+ const scoped2 = quality?.commands?.formatFixScoped;
39279
39325
  const command = buildCommand2(broad, scoped2, input.scopeFiles);
39280
39326
  if (!command)
39281
39327
  return { applied: true, exitCode: 0 };
@@ -39284,7 +39330,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
39284
39330
  command,
39285
39331
  workdir: input.workdir,
39286
39332
  storyId: input.storyId,
39287
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39333
+ stripEnvVars: quality?.stripEnvVars ?? []
39288
39334
  });
39289
39335
  return { applied: true, exitCode: result.exitCode };
39290
39336
  }
@@ -39295,6 +39341,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
39295
39341
  var _lintCheckDeps, lintCheckOp;
39296
39342
  var init_lint_check = __esm(() => {
39297
39343
  init_config();
39344
+ init_logger2();
39298
39345
  init_runner();
39299
39346
  init_lint_parsing();
39300
39347
  _lintCheckDeps = {
@@ -39307,21 +39354,25 @@ var init_lint_check = __esm(() => {
39307
39354
  stage: "review",
39308
39355
  config: qualityConfigSelector,
39309
39356
  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 };
39357
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39358
+ const command = quality?.commands?.lint;
39359
+ if (!command) {
39360
+ getSafeLogger()?.warn("quality", "No lint command configured \u2014 skipping lint gate", {
39361
+ storyId: input.storyId,
39362
+ packageDir: ctx.packageView.packageDir
39363
+ });
39364
+ return { success: true, status: "skipped", findings: [], durationMs: 0 };
39314
39365
  }
39315
39366
  const start = Date.now();
39316
39367
  const result = await deps.runQualityCommand({
39317
39368
  commandName: "lint",
39318
- command: command ?? "",
39369
+ command,
39319
39370
  workdir: input.workdir,
39320
39371
  storyId: input.storyId,
39321
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39372
+ stripEnvVars: quality?.stripEnvVars ?? []
39322
39373
  });
39323
39374
  if (result.exitCode === 0) {
39324
- return { success: true, findings: [], durationMs: Date.now() - start };
39375
+ return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39325
39376
  }
39326
39377
  const parsed = deps.parseLintOutput(result.output, "auto", { workdir: input.workdir });
39327
39378
  return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
@@ -39545,6 +39596,7 @@ var init_typecheck_parsing = __esm(() => {
39545
39596
  var _typecheckCheckDeps, typecheckCheckOp;
39546
39597
  var init_typecheck_check = __esm(() => {
39547
39598
  init_config();
39599
+ init_logger2();
39548
39600
  init_runner();
39549
39601
  init_typecheck_parsing();
39550
39602
  _typecheckCheckDeps = {
@@ -39557,21 +39609,25 @@ var init_typecheck_check = __esm(() => {
39557
39609
  stage: "review",
39558
39610
  config: qualityConfigSelector,
39559
39611
  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 };
39612
+ const quality = ctx.packageView.select(qualityConfigSelector).quality;
39613
+ const command = quality?.commands?.typecheck;
39614
+ if (!command) {
39615
+ getSafeLogger()?.warn("quality", "No typecheck command configured \u2014 skipping typecheck gate", {
39616
+ storyId: input.storyId,
39617
+ packageDir: ctx.packageView.packageDir
39618
+ });
39619
+ return { success: true, status: "skipped", findings: [], durationMs: 0 };
39564
39620
  }
39565
39621
  const start = Date.now();
39566
39622
  const result = await deps.runQualityCommand({
39567
39623
  commandName: "typecheck",
39568
- command: command ?? "",
39624
+ command,
39569
39625
  workdir: input.workdir,
39570
39626
  storyId: input.storyId,
39571
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39627
+ stripEnvVars: quality?.stripEnvVars ?? []
39572
39628
  });
39573
39629
  if (result.exitCode === 0) {
39574
- return { success: true, findings: [], durationMs: Date.now() - start };
39630
+ return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39575
39631
  }
39576
39632
  const parsed = deps.parseTypecheckOutput(result.output, "auto", { workdir: input.workdir });
39577
39633
  return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
@@ -39600,12 +39656,16 @@ var init_verify_scoped = __esm(() => {
39600
39656
  config: qualityConfigSelector,
39601
39657
  async execute(input, ctx, deps = _verifyScopedDeps) {
39602
39658
  const logger = getLogger();
39603
- const ctxConfig = ctx.config;
39604
- const baseCommand = ctxConfig?.quality?.commands?.test;
39605
- if (!ctxConfig || !baseCommand) {
39659
+ const quality = ctx.packageView.select(qualityConfigSelector);
39660
+ const baseCommand = quality.quality?.commands?.test;
39661
+ if (!baseCommand) {
39662
+ logger.warn("quality", "No test command configured \u2014 skipping scoped verify", {
39663
+ storyId: input.storyId,
39664
+ packageDir: ctx.packageView.packageDir
39665
+ });
39606
39666
  return {
39607
39667
  success: true,
39608
- status: "passed",
39668
+ status: "skipped",
39609
39669
  findings: [],
39610
39670
  durationMs: 0,
39611
39671
  passCount: 0,
@@ -39617,11 +39677,11 @@ var init_verify_scoped = __esm(() => {
39617
39677
  workdir: input.workdir,
39618
39678
  storyId: input.storyId,
39619
39679
  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,
39680
+ testCommand: baseCommand,
39681
+ testScopedTemplate: quality.quality?.commands?.testScoped,
39682
+ smartRunnerConfig: quality.execution?.smartTestRunner,
39683
+ scopeTestThreshold: quality.quality?.scopeTestThreshold,
39684
+ fallbackFullSuiteCommand: quality.quality?.commands?.test,
39625
39685
  naxIgnoreIndex: input.naxIgnoreIndex
39626
39686
  });
39627
39687
  if (selection.isFullSuite && regressionMode === "deferred" && !selection.isMonorepoOrchestrator && !selection.thresholdFallback) {
@@ -39648,7 +39708,7 @@ var init_verify_scoped = __esm(() => {
39648
39708
  command: selection.effectiveCommand
39649
39709
  });
39650
39710
  }
39651
- const scopedTimeout = ctxConfig.execution?.regressionGate?.timeoutSeconds ?? 600;
39711
+ const scopedTimeout = quality.execution?.regressionGate?.timeoutSeconds ?? 600;
39652
39712
  logger.info("verify[scoped]", "Running scoped tests", {
39653
39713
  storyId: input.storyId,
39654
39714
  packageDir: input.packageDir,
@@ -39661,14 +39721,14 @@ var init_verify_scoped = __esm(() => {
39661
39721
  const result = await deps.regression({
39662
39722
  workdir: input.workdir,
39663
39723
  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
39724
+ timeoutSeconds: quality.execution?.regressionGate?.timeoutSeconds ?? 600,
39725
+ forceExit: quality.quality?.forceExit,
39726
+ detectOpenHandles: quality.quality?.detectOpenHandles,
39727
+ detectOpenHandlesRetries: quality.quality?.detectOpenHandlesRetries,
39728
+ gracePeriodMs: quality.quality?.gracePeriodMs,
39729
+ drainTimeoutMs: quality.quality?.drainTimeoutMs,
39730
+ shell: quality.quality?.shell,
39731
+ stripEnvVars: quality.quality?.stripEnvVars
39672
39732
  });
39673
39733
  const durationMs = Date.now() - start;
39674
39734
  const parsed = result.output ? deps.parseTestOutput(result.output) : { passed: 0, failed: 0, failures: [] };
@@ -40440,7 +40500,7 @@ var init_lint_parsing = __esm(() => {
40440
40500
  });
40441
40501
 
40442
40502
  // src/review/scoped-lint.ts
40443
- import { join as join25, relative as relative10 } from "path";
40503
+ import { join as join26, relative as relative10 } from "path";
40444
40504
  function shellQuotePath4(path5) {
40445
40505
  return `'${path5.replaceAll("'", "'\\''")}'`;
40446
40506
  }
@@ -40488,7 +40548,7 @@ function uniqueFiles(files) {
40488
40548
  async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
40489
40549
  const inScope = [];
40490
40550
  for (const relPath of files) {
40491
- const absPath = join25(workdir, relPath);
40551
+ const absPath = join26(workdir, relPath);
40492
40552
  const exists = await _scopedLintDeps.fileExists(absPath);
40493
40553
  if (!exists)
40494
40554
  continue;
@@ -43862,7 +43922,7 @@ var init_call = __esm(() => {
43862
43922
 
43863
43923
  // src/runtime/cost-aggregator.ts
43864
43924
  import { mkdirSync as mkdirSync2 } from "fs";
43865
- import { join as join26 } from "path";
43925
+ import { join as join27 } from "path";
43866
43926
  function makeCorrelationId() {
43867
43927
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
43868
43928
  }
@@ -44053,7 +44113,7 @@ class CostAggregator {
44053
44113
  if (events.length === 0 && errors3.length === 0)
44054
44114
  return;
44055
44115
  mkdirSync2(this._drainDir, { recursive: true });
44056
- const path5 = join26(this._drainDir, `${this._runId}.jsonl`);
44116
+ const path5 = join27(this._drainDir, `${this._runId}.jsonl`);
44057
44117
  const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
44058
44118
  await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
44059
44119
  `)}
@@ -44093,7 +44153,7 @@ var init_cost_aggregator = __esm(() => {
44093
44153
  // src/runtime/prompt-auditor.ts
44094
44154
  import { appendFileSync } from "fs";
44095
44155
  import { mkdir as mkdir4 } from "fs/promises";
44096
- import { join as join27 } from "path";
44156
+ import { join as join28 } from "path";
44097
44157
  function createNoOpPromptAuditor() {
44098
44158
  return {
44099
44159
  record() {},
@@ -44159,8 +44219,8 @@ class PromptAuditor {
44159
44219
  _jsonlPath;
44160
44220
  _featureDir;
44161
44221
  constructor(runId, flushDir, featureName) {
44162
- this._featureDir = join27(flushDir, featureName);
44163
- this._jsonlPath = join27(this._featureDir, `${runId}.jsonl`);
44222
+ this._featureDir = join28(flushDir, featureName);
44223
+ this._jsonlPath = join28(this._featureDir, `${runId}.jsonl`);
44164
44224
  }
44165
44225
  record(entry) {
44166
44226
  this._enqueue(entry);
@@ -44209,7 +44269,7 @@ class PromptAuditor {
44209
44269
  const auditEntry = entry;
44210
44270
  const filename = deriveTxtFilename(auditEntry);
44211
44271
  try {
44212
- await _promptAuditorDeps.write(join27(this._featureDir, filename), buildTxtContent(auditEntry));
44272
+ await _promptAuditorDeps.write(join28(this._featureDir, filename), buildTxtContent(auditEntry));
44213
44273
  } catch (err) {
44214
44274
  throw tagAuditError(err, "txt");
44215
44275
  }
@@ -44344,17 +44404,44 @@ function createPackageView(config2, packageDir, repoRoot) {
44344
44404
  }
44345
44405
  function createPackageRegistry(loader, repoRoot) {
44346
44406
  const cache = new Map;
44407
+ const mergedConfigs = new Map;
44408
+ function toRelativeKey(packageDir) {
44409
+ if (!packageDir)
44410
+ return "";
44411
+ const prefix = repoRoot.endsWith("/") ? repoRoot : `${repoRoot}/`;
44412
+ if (packageDir.startsWith(prefix))
44413
+ return packageDir.slice(prefix.length);
44414
+ if (packageDir === repoRoot)
44415
+ return "";
44416
+ return packageDir;
44417
+ }
44347
44418
  function resolve12(packageDir) {
44348
- const key = packageDir ?? "";
44419
+ const key = toRelativeKey(packageDir);
44349
44420
  const cached2 = cache.get(key);
44350
44421
  if (cached2 !== undefined) {
44351
44422
  return cached2;
44352
44423
  }
44353
- const config2 = loader.current();
44424
+ const config2 = mergedConfigs.get(key) ?? loader.current();
44354
44425
  const view = createPackageView(config2, key, repoRoot);
44355
44426
  cache.set(key, view);
44356
44427
  return view;
44357
44428
  }
44429
+ async function hydrate(packageDirs, loadOverride2) {
44430
+ const load = loadOverride2 ?? (await Promise.resolve().then(() => (init_config(), exports_config))).loadPackageOverride;
44431
+ for (const dir of packageDirs) {
44432
+ if (!dir) {
44433
+ continue;
44434
+ }
44435
+ if (mergedConfigs.has(dir)) {
44436
+ continue;
44437
+ }
44438
+ const override = await load(repoRoot, dir);
44439
+ if (override !== null) {
44440
+ mergedConfigs.set(dir, mergePackageConfig(loader.current(), override));
44441
+ cache.delete(dir);
44442
+ }
44443
+ }
44444
+ }
44358
44445
  return {
44359
44446
  all() {
44360
44447
  return [...cache.values()];
@@ -44362,9 +44449,13 @@ function createPackageRegistry(loader, repoRoot) {
44362
44449
  resolve: resolve12,
44363
44450
  repo() {
44364
44451
  return resolve12(undefined);
44365
- }
44452
+ },
44453
+ hydrate
44366
44454
  };
44367
44455
  }
44456
+ var init_packages = __esm(() => {
44457
+ init_config();
44458
+ });
44368
44459
 
44369
44460
  // src/runtime/agent-stream-events.ts
44370
44461
  class AgentStreamEventBus {
@@ -45322,7 +45413,7 @@ var init_pid_registry = __esm(() => {
45322
45413
  // src/session/manager-deps.ts
45323
45414
  import { randomUUID as randomUUID3 } from "crypto";
45324
45415
  import { mkdir as mkdir5 } from "fs/promises";
45325
- import { isAbsolute as isAbsolute9, join as join28, relative as relative11, sep as sep2 } from "path";
45416
+ import { isAbsolute as isAbsolute9, join as join29, relative as relative11, sep as sep2 } from "path";
45326
45417
  function resolveProjectDirFromScratchDir(scratchDir) {
45327
45418
  const marker = `${sep2}.nax${sep2}features${sep2}`;
45328
45419
  const markerIdx = scratchDir.lastIndexOf(marker);
@@ -45343,7 +45434,7 @@ var init_manager_deps = __esm(() => {
45343
45434
  now: () => new Date().toISOString(),
45344
45435
  nowMs: () => Date.now(),
45345
45436
  uuid: () => randomUUID3(),
45346
- sessionScratchDir: (projectDir, featureName, sessionId) => join28(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45437
+ sessionScratchDir: (projectDir, featureName, sessionId) => join29(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45347
45438
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
45348
45439
  await mkdir5(scratchDir, { recursive: true });
45349
45440
  const { handle: _handle, ...persistable } = descriptor;
@@ -45354,7 +45445,7 @@ var init_manager_deps = __esm(() => {
45354
45445
  persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
45355
45446
  }
45356
45447
  }
45357
- await Bun.write(join28(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45448
+ await Bun.write(join29(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
45358
45449
  }
45359
45450
  };
45360
45451
  });
@@ -46105,7 +46196,7 @@ __export(exports_runtime, {
46105
46196
  CostAggregator: () => CostAggregator,
46106
46197
  AgentStreamEventBus: () => AgentStreamEventBus
46107
46198
  });
46108
- import { basename as basename5, join as join29 } from "path";
46199
+ import { basename as basename5, join as join30 } from "path";
46109
46200
  function createRuntime(config2, workdir, opts) {
46110
46201
  const runId = crypto.randomUUID();
46111
46202
  const controller = new AbortController;
@@ -46121,10 +46212,10 @@ function createRuntime(config2, workdir, opts) {
46121
46212
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
46122
46213
  const globalDir = globalOutputDir();
46123
46214
  const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
46124
- const costDir = join29(outputDir, "cost");
46215
+ const costDir = join30(outputDir, "cost");
46125
46216
  const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
46126
46217
  const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
46127
- const auditDir = config2.agent?.promptAudit?.dir ?? join29(outputDir, "prompt-audit");
46218
+ const auditDir = config2.agent?.promptAudit?.dir ?? join30(outputDir, "prompt-audit");
46128
46219
  let promptAuditor;
46129
46220
  if (opts?.promptAuditor) {
46130
46221
  promptAuditor = opts.promptAuditor;
@@ -46230,6 +46321,7 @@ var init_runtime = __esm(() => {
46230
46321
  init_prompt_auditor();
46231
46322
  init_review_audit();
46232
46323
  init_paths2();
46324
+ init_packages();
46233
46325
  init_dispatch_events();
46234
46326
  init_agent_stream_events();
46235
46327
  init_middleware();
@@ -46247,6 +46339,7 @@ var init_runtime = __esm(() => {
46247
46339
  init_cost_aggregator();
46248
46340
  init_dispatch_events();
46249
46341
  init_middleware();
46342
+ init_packages();
46250
46343
  init_paths2();
46251
46344
  init_prompt_auditor();
46252
46345
  init_session_run_hop();
@@ -46275,9 +46368,9 @@ async function allSettledBounded(tasks, limit) {
46275
46368
 
46276
46369
  // src/context/injector.ts
46277
46370
  import { existsSync as existsSync8 } from "fs";
46278
- import { join as join30 } from "path";
46371
+ import { join as join31 } from "path";
46279
46372
  async function detectNode(workdir) {
46280
- const pkgPath = join30(workdir, "package.json");
46373
+ const pkgPath = join31(workdir, "package.json");
46281
46374
  if (!existsSync8(pkgPath))
46282
46375
  return null;
46283
46376
  try {
@@ -46294,7 +46387,7 @@ async function detectNode(workdir) {
46294
46387
  }
46295
46388
  }
46296
46389
  async function detectGo(workdir) {
46297
- const goMod = join30(workdir, "go.mod");
46390
+ const goMod = join31(workdir, "go.mod");
46298
46391
  if (!existsSync8(goMod))
46299
46392
  return null;
46300
46393
  try {
@@ -46318,7 +46411,7 @@ async function detectGo(workdir) {
46318
46411
  }
46319
46412
  }
46320
46413
  async function detectRust(workdir) {
46321
- const cargoPath = join30(workdir, "Cargo.toml");
46414
+ const cargoPath = join31(workdir, "Cargo.toml");
46322
46415
  if (!existsSync8(cargoPath))
46323
46416
  return null;
46324
46417
  try {
@@ -46334,8 +46427,8 @@ async function detectRust(workdir) {
46334
46427
  }
46335
46428
  }
46336
46429
  async function detectPython(workdir) {
46337
- const pyproject = join30(workdir, "pyproject.toml");
46338
- const requirements = join30(workdir, "requirements.txt");
46430
+ const pyproject = join31(workdir, "pyproject.toml");
46431
+ const requirements = join31(workdir, "requirements.txt");
46339
46432
  if (!existsSync8(pyproject) && !existsSync8(requirements))
46340
46433
  return null;
46341
46434
  try {
@@ -46354,7 +46447,7 @@ async function detectPython(workdir) {
46354
46447
  }
46355
46448
  }
46356
46449
  async function detectPhp(workdir) {
46357
- const composerPath = join30(workdir, "composer.json");
46450
+ const composerPath = join31(workdir, "composer.json");
46358
46451
  if (!existsSync8(composerPath))
46359
46452
  return null;
46360
46453
  try {
@@ -46367,7 +46460,7 @@ async function detectPhp(workdir) {
46367
46460
  }
46368
46461
  }
46369
46462
  async function detectRuby(workdir) {
46370
- const gemfile = join30(workdir, "Gemfile");
46463
+ const gemfile = join31(workdir, "Gemfile");
46371
46464
  if (!existsSync8(gemfile))
46372
46465
  return null;
46373
46466
  try {
@@ -46379,9 +46472,9 @@ async function detectRuby(workdir) {
46379
46472
  }
46380
46473
  }
46381
46474
  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");
46475
+ const pom = join31(workdir, "pom.xml");
46476
+ const gradle = join31(workdir, "build.gradle");
46477
+ const gradleKts = join31(workdir, "build.gradle.kts");
46385
46478
  if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
46386
46479
  return null;
46387
46480
  try {
@@ -46389,7 +46482,7 @@ async function detectJvm(workdir) {
46389
46482
  const content2 = await Bun.file(pom).text();
46390
46483
  const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
46391
46484
  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";
46485
+ const lang2 = existsSync8(join31(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46393
46486
  return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
46394
46487
  }
46395
46488
  const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
@@ -46643,7 +46736,7 @@ var init_windsurf = __esm(() => {
46643
46736
 
46644
46737
  // src/context/generator.ts
46645
46738
  import { existsSync as existsSync9 } from "fs";
46646
- import { join as join31, relative as relative12 } from "path";
46739
+ import { join as join32, relative as relative12 } from "path";
46647
46740
  async function loadContextContent(options, config2) {
46648
46741
  if (!_generatorDeps.existsSync(options.contextPath)) {
46649
46742
  throw new Error(`Context file not found: ${options.contextPath}`);
@@ -46661,7 +46754,7 @@ async function generateFor(agent, options, config2) {
46661
46754
  try {
46662
46755
  const context = await loadContextContent(options, config2);
46663
46756
  const content = generator.generate(context);
46664
- const outputPath = join31(options.outputDir, generator.outputFile);
46757
+ const outputPath = join32(options.outputDir, generator.outputFile);
46665
46758
  validateFilePath(outputPath, options.outputDir);
46666
46759
  if (!options.dryRun) {
46667
46760
  await _generatorDeps.writeFile(outputPath, content);
@@ -46679,7 +46772,7 @@ async function generateAll(options, config2, agentFilter) {
46679
46772
  for (const [agentKey, generator] of entries) {
46680
46773
  try {
46681
46774
  const content = generator.generate(context);
46682
- const outputPath = join31(options.outputDir, generator.outputFile);
46775
+ const outputPath = join32(options.outputDir, generator.outputFile);
46683
46776
  validateFilePath(outputPath, options.outputDir);
46684
46777
  if (!options.dryRun) {
46685
46778
  await _generatorDeps.writeFile(outputPath, content);
@@ -46699,7 +46792,7 @@ async function discoverPackages(repoRoot) {
46699
46792
  const glob = new Bun.Glob(pattern);
46700
46793
  for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
46701
46794
  const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
46702
- const pkgAbsolute = join31(repoRoot, pkgRelative);
46795
+ const pkgAbsolute = join32(repoRoot, pkgRelative);
46703
46796
  if (!seen.has(pkgAbsolute)) {
46704
46797
  seen.add(pkgAbsolute);
46705
46798
  packages.push(pkgAbsolute);
@@ -46731,14 +46824,14 @@ async function discoverWorkspacePackages2(repoRoot) {
46731
46824
  }
46732
46825
  }
46733
46826
  }
46734
- const turboPath = join31(repoRoot, "turbo.json");
46827
+ const turboPath = join32(repoRoot, "turbo.json");
46735
46828
  try {
46736
46829
  const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
46737
46830
  if (Array.isArray(turbo.packages)) {
46738
46831
  await resolveGlobs(turbo.packages);
46739
46832
  }
46740
46833
  } catch {}
46741
- const pkgPath = join31(repoRoot, "package.json");
46834
+ const pkgPath = join32(repoRoot, "package.json");
46742
46835
  try {
46743
46836
  const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
46744
46837
  const ws = pkg.workspaces;
@@ -46746,7 +46839,7 @@ async function discoverWorkspacePackages2(repoRoot) {
46746
46839
  if (patterns.length > 0)
46747
46840
  await resolveGlobs(patterns);
46748
46841
  } catch {}
46749
- const pnpmPath = join31(repoRoot, "pnpm-workspace.yaml");
46842
+ const pnpmPath = join32(repoRoot, "pnpm-workspace.yaml");
46750
46843
  try {
46751
46844
  const raw = await _generatorDeps.readTextFile(pnpmPath);
46752
46845
  const lines = raw.split(`
@@ -46772,7 +46865,7 @@ async function discoverWorkspacePackages2(repoRoot) {
46772
46865
  async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
46773
46866
  const resolvedRepoRoot = repoRoot ?? packageDir;
46774
46867
  const relativePkgPath = relative12(resolvedRepoRoot, packageDir);
46775
- const contextPath = join31(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
46868
+ const contextPath = join32(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
46776
46869
  if (!_generatorDeps.existsSync(contextPath)) {
46777
46870
  return [
46778
46871
  {
@@ -46840,7 +46933,7 @@ var init_generator2 = __esm(() => {
46840
46933
  });
46841
46934
 
46842
46935
  // src/analyze/scanner.ts
46843
- import { join as join32 } from "path";
46936
+ import { join as join33 } from "path";
46844
46937
  function resolveFrameworkAndRunner(language, pkg) {
46845
46938
  if (language === "go")
46846
46939
  return { framework: "", testRunner: "go-test" };
@@ -46862,7 +46955,7 @@ async function scanSourceRoots(workdir) {
46862
46955
  });
46863
46956
  try {
46864
46957
  const language = await deps.detectLanguage(workdir);
46865
- const pkg = await deps.readPackageJson(join32(workdir, "package.json"));
46958
+ const pkg = await deps.readPackageJson(join33(workdir, "package.json"));
46866
46959
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
46867
46960
  return [{ path: ".", language, framework, testRunner }];
46868
46961
  } catch {
@@ -46880,9 +46973,9 @@ async function scanSourceRoots(workdir) {
46880
46973
  packages = packages.slice(0, MAX_SOURCE_ROOTS);
46881
46974
  }
46882
46975
  return Promise.all(packages.map(async (pkgPath) => {
46883
- const pkgDir = pkgPath === "." ? workdir : join32(workdir, pkgPath);
46976
+ const pkgDir = pkgPath === "." ? workdir : join33(workdir, pkgPath);
46884
46977
  const language = await deps.detectLanguage(pkgDir);
46885
- const pkg = await deps.readPackageJson(join32(pkgDir, "package.json"));
46978
+ const pkg = await deps.readPackageJson(join33(pkgDir, "package.json"));
46886
46979
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
46887
46980
  return { path: pkgPath, language, framework, testRunner };
46888
46981
  }));
@@ -46915,7 +47008,7 @@ var init_analyze = __esm(() => {
46915
47008
  });
46916
47009
 
46917
47010
  // src/debate/pre-phase/grounder.ts
46918
- import { join as join33 } from "path";
47011
+ import { join as join34 } from "path";
46919
47012
  async function buildCodebaseContext(workdir) {
46920
47013
  const roots = await _grounderDeps.scanSourceRoots(workdir);
46921
47014
  return buildSourceRootsSection(normalizeRoots(workdir, roots));
@@ -46927,7 +47020,7 @@ function normalizeRoots(workdir, roots) {
46927
47020
  }));
46928
47021
  }
46929
47022
  async function writeManifestArtifact(ctx, manifest) {
46930
- const manifestPath = join33(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47023
+ const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
46931
47024
  await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
46932
47025
  }
46933
47026
  var _grounderDeps, grounderStrategy = async (ctx) => {
@@ -47270,7 +47363,7 @@ function formatSpecDeltas(blockers, manifest) {
47270
47363
 
47271
47364
  // src/debate/verifiers/checks.ts
47272
47365
  import { existsSync as defaultExistsSync } from "fs";
47273
- import { join as join34 } from "path";
47366
+ import { join as join35 } from "path";
47274
47367
  function checkFilesExist(prd, workdir, deps) {
47275
47368
  const existsSync10 = deps?.existsSync ?? defaultExistsSync;
47276
47369
  const findings = [];
@@ -47280,7 +47373,7 @@ function checkFilesExist(prd, workdir, deps) {
47280
47373
  for (const entry of story.contextFiles) {
47281
47374
  const filePath = typeof entry === "string" ? entry : entry.path;
47282
47375
  const factId = typeof entry === "string" ? undefined : entry.factId;
47283
- const absPath = join34(workdir, filePath);
47376
+ const absPath = join35(workdir, filePath);
47284
47377
  if (existsSync10(absPath))
47285
47378
  continue;
47286
47379
  if (factId) {
@@ -47391,7 +47484,7 @@ var init_checks3 = () => {};
47391
47484
 
47392
47485
  // src/debate/verifiers/plan-checklist.ts
47393
47486
  import { existsSync as existsSync10 } from "fs";
47394
- import { join as join35 } from "path";
47487
+ import { join as join36 } from "path";
47395
47488
  function parsePrd(output) {
47396
47489
  if (!output)
47397
47490
  return null;
@@ -47402,7 +47495,7 @@ function parsePrd(output) {
47402
47495
  }
47403
47496
  }
47404
47497
  async function loadManifest(ctx) {
47405
- const manifestPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47498
+ const manifestPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
47406
47499
  const raw = await _planChecklistDeps.readFile(manifestPath);
47407
47500
  if (!raw)
47408
47501
  return null;
@@ -47415,7 +47508,7 @@ async function loadManifest(ctx) {
47415
47508
  }
47416
47509
  }
47417
47510
  async function emitSpecDeltas(ctx, blockers, manifest) {
47418
- const artifactPath = join35(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47511
+ const artifactPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
47419
47512
  const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
47420
47513
  await _planChecklistDeps.write(artifactPath, content);
47421
47514
  return artifactPath;
@@ -47761,7 +47854,7 @@ var init_runner_plan_helpers = __esm(() => {
47761
47854
  });
47762
47855
 
47763
47856
  // src/debate/runner-plan.ts
47764
- import { join as join36 } from "path";
47857
+ import { join as join37 } from "path";
47765
47858
  async function runPlan(ctx, taskContext, outputFormat, opts) {
47766
47859
  const logger = _debateSessionDeps.getSafeLogger();
47767
47860
  const config2 = ctx.stageConfig;
@@ -47820,7 +47913,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
47820
47913
  sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
47821
47914
  proposers: ctx.stageConfig.proposers
47822
47915
  });
47823
- const outputPaths = resolved.map((_, i) => join36(opts.outputDir, `prd-debate-${i}.json`));
47916
+ const outputPaths = resolved.map((_, i) => join37(opts.outputDir, `prd-debate-${i}.json`));
47824
47917
  const successful = [];
47825
47918
  let rebuttalList;
47826
47919
  if (selectorKind === "verifier-pick") {
@@ -50063,9 +50156,9 @@ function validateFeatureName(feature) {
50063
50156
 
50064
50157
  // src/plan/critic.ts
50065
50158
  import { mkdir as mkdir6 } from "fs/promises";
50066
- import { dirname as dirname7, join as join39 } from "path";
50159
+ import { dirname as dirname7, join as join40 } from "path";
50067
50160
  async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
50068
- const path7 = join39(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50161
+ const path7 = join40(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
50069
50162
  await mkdir6(dirname7(path7), { recursive: true });
50070
50163
  await Bun.write(path7, formatSpecDeltas(findings, manifest));
50071
50164
  return path7;
@@ -51278,9 +51371,9 @@ __export(exports_plan_decompose, {
51278
51371
  runReplanLoop: () => runReplanLoop,
51279
51372
  planDecomposeCommand: () => planDecomposeCommand
51280
51373
  });
51281
- import { join as join40 } from "path";
51374
+ import { join as join41 } from "path";
51282
51375
  async function planDecomposeCommand(workdir, config2, options) {
51283
- const prdPath = join40(workdir, ".nax", "features", options.feature, "prd.json");
51376
+ const prdPath = join41(workdir, ".nax", "features", options.feature, "prd.json");
51284
51377
  if (!_planDeps.existsSync(prdPath)) {
51285
51378
  throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
51286
51379
  stage: "decompose",
@@ -51454,7 +51547,7 @@ var init_plan_decompose = __esm(() => {
51454
51547
 
51455
51548
  // src/cli/plan-runtime.ts
51456
51549
  import { existsSync as existsSync15 } from "fs";
51457
- import { join as join41 } from "path";
51550
+ import { join as join42 } from "path";
51458
51551
  function isRuntimeWithAgentManager(value) {
51459
51552
  return typeof value === "object" && value !== null && "agentManager" in value;
51460
51553
  }
@@ -51506,7 +51599,7 @@ var init_plan_runtime = __esm(() => {
51506
51599
  writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
51507
51600
  scanSourceRoots: (workdir) => scanSourceRoots(workdir),
51508
51601
  createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
51509
- readPackageJson: (workdir) => Bun.file(join41(workdir, "package.json")).json().catch(() => null),
51602
+ readPackageJson: (workdir) => Bun.file(join42(workdir, "package.json")).json().catch(() => null),
51510
51603
  spawnSync: (cmd, opts) => {
51511
51604
  const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
51512
51605
  return { stdout: result.stdout, exitCode: result.exitCode };
@@ -51891,7 +51984,7 @@ var init_metrics = __esm(() => {
51891
51984
 
51892
51985
  // src/commands/common.ts
51893
51986
  import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
51894
- import { join as join42, resolve as resolve13 } from "path";
51987
+ import { join as join43, resolve as resolve13 } from "path";
51895
51988
  function resolveProject(options = {}) {
51896
51989
  const { dir, feature } = options;
51897
51990
  let projectRoot;
@@ -51899,12 +51992,12 @@ function resolveProject(options = {}) {
51899
51992
  let configPath;
51900
51993
  if (dir) {
51901
51994
  projectRoot = realpathSync3(resolve13(dir));
51902
- naxDir = join42(projectRoot, ".nax");
51995
+ naxDir = join43(projectRoot, ".nax");
51903
51996
  if (!existsSync16(naxDir)) {
51904
51997
  throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
51905
51998
  Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
51906
51999
  }
51907
- configPath = join42(naxDir, "config.json");
52000
+ configPath = join43(naxDir, "config.json");
51908
52001
  if (!existsSync16(configPath)) {
51909
52002
  throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
51910
52003
  Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
@@ -51912,17 +52005,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
51912
52005
  } else {
51913
52006
  const found = findProjectRoot(process.cwd());
51914
52007
  if (!found) {
51915
- const cwdNaxDir = join42(process.cwd(), ".nax");
52008
+ const cwdNaxDir = join43(process.cwd(), ".nax");
51916
52009
  if (existsSync16(cwdNaxDir)) {
51917
- const cwdConfigPath = join42(cwdNaxDir, "config.json");
52010
+ const cwdConfigPath = join43(cwdNaxDir, "config.json");
51918
52011
  throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
51919
52012
  Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
51920
52013
  }
51921
52014
  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
52015
  }
51923
52016
  projectRoot = found;
51924
- naxDir = join42(projectRoot, ".nax");
51925
- configPath = join42(naxDir, "config.json");
52017
+ naxDir = join43(projectRoot, ".nax");
52018
+ configPath = join43(naxDir, "config.json");
51926
52019
  }
51927
52020
  let featureDir;
51928
52021
  if (feature) {
@@ -51931,8 +52024,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
51931
52024
  } catch (error48) {
51932
52025
  throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
51933
52026
  }
51934
- const featuresDir = join42(naxDir, "features");
51935
- featureDir = join42(featuresDir, feature);
52027
+ const featuresDir = join43(naxDir, "features");
52028
+ featureDir = join43(featuresDir, feature);
51936
52029
  if (!existsSync16(featureDir)) {
51937
52030
  const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
51938
52031
  const availableMsg = availableFeatures.length > 0 ? `
@@ -51965,7 +52058,7 @@ async function resolveProjectAsync(options = {}) {
51965
52058
  }
51966
52059
  const isPlainName = !dir.includes("/") && !dir.includes("\\");
51967
52060
  if (isPlainName) {
51968
- const registryIdentityPath = join42(globalConfigDir(), dir, ".identity");
52061
+ const registryIdentityPath = join43(globalConfigDir(), dir, ".identity");
51969
52062
  const identityFile = Bun.file(registryIdentityPath);
51970
52063
  if (await identityFile.exists()) {
51971
52064
  try {
@@ -51997,12 +52090,12 @@ function findProjectRoot(startDir) {
51997
52090
  let current = resolve13(startDir);
51998
52091
  let depth = 0;
51999
52092
  while (depth < MAX_DIRECTORY_DEPTH) {
52000
- const naxDir = join42(current, ".nax");
52001
- const configPath = join42(naxDir, "config.json");
52093
+ const naxDir = join43(current, ".nax");
52094
+ const configPath = join43(naxDir, "config.json");
52002
52095
  if (existsSync16(configPath)) {
52003
52096
  return realpathSync3(current);
52004
52097
  }
52005
- const parent = join42(current, "..");
52098
+ const parent = join43(current, "..");
52006
52099
  if (parent === current) {
52007
52100
  break;
52008
52101
  }
@@ -53161,10 +53254,10 @@ var init_effectiveness = __esm(() => {
53161
53254
 
53162
53255
  // src/execution/progress.ts
53163
53256
  import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
53164
- import { join as join45 } from "path";
53257
+ import { join as join46 } from "path";
53165
53258
  async function appendProgress(featureDir, storyId, status, message) {
53166
53259
  await mkdir7(featureDir, { recursive: true });
53167
- const progressPath = join45(featureDir, "progress.txt");
53260
+ const progressPath = join46(featureDir, "progress.txt");
53168
53261
  const timestamp = new Date().toISOString();
53169
53262
  const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
53170
53263
  `;
@@ -53358,7 +53451,7 @@ var init_completion = __esm(() => {
53358
53451
 
53359
53452
  // src/constitution/loader.ts
53360
53453
  import { existsSync as existsSync19 } from "fs";
53361
- import { join as join46 } from "path";
53454
+ import { join as join47 } from "path";
53362
53455
  function truncateToTokens(text, maxTokens) {
53363
53456
  const maxChars = maxTokens * 3;
53364
53457
  if (text.length <= maxChars) {
@@ -53380,7 +53473,7 @@ async function loadConstitution(projectDir, config2) {
53380
53473
  }
53381
53474
  let combinedContent = "";
53382
53475
  if (!config2.skipGlobal) {
53383
- const globalPath = join46(globalConfigDir(), config2.path);
53476
+ const globalPath = join47(globalConfigDir(), config2.path);
53384
53477
  if (existsSync19(globalPath)) {
53385
53478
  const validatedPath = validateFilePath(globalPath, globalConfigDir());
53386
53479
  const globalFile = Bun.file(validatedPath);
@@ -53390,7 +53483,7 @@ async function loadConstitution(projectDir, config2) {
53390
53483
  }
53391
53484
  }
53392
53485
  }
53393
- const projectPath = join46(projectDir, config2.path);
53486
+ const projectPath = join47(projectDir, config2.path);
53394
53487
  if (existsSync19(projectPath)) {
53395
53488
  const validatedPath = validateFilePath(projectPath, projectDir);
53396
53489
  const projectFile = Bun.file(validatedPath);
@@ -53964,6 +54057,64 @@ var init_non_blocking_fix = __esm(() => {
53964
54057
  DEFAULT_DEPS = { captureSnapshotRef, rollbackToRef };
53965
54058
  });
53966
54059
 
54060
+ // src/execution/story-orchestrator-logging.ts
54061
+ function formatPhaseResultMessage(opName, success2, stage, status) {
54062
+ if (opName === "greenfield-gate") {
54063
+ 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";
54064
+ }
54065
+ if (status === "skipped") {
54066
+ return `Phase skipped: ${opName}`;
54067
+ }
54068
+ if (stage === "rectification") {
54069
+ return `Rectification strategy completed: ${opName}`;
54070
+ }
54071
+ return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
54072
+ }
54073
+ function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
54074
+ if (output === null || output === undefined || typeof output !== "object")
54075
+ return null;
54076
+ const r = output;
54077
+ const success2 = r.success === true || r.passed === true;
54078
+ const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
54079
+ const status = typeof r.status === "string" ? r.status : undefined;
54080
+ const data = { storyId, phase: opName, durationMs };
54081
+ if (findingsCount !== undefined)
54082
+ data.findingsCount = findingsCount;
54083
+ if (status !== undefined)
54084
+ data.status = status;
54085
+ if (typeof r.failureCategory === "string")
54086
+ data.failureCategory = r.failureCategory;
54087
+ if (typeof r.reviewReason === "string")
54088
+ data.reviewReason = r.reviewReason;
54089
+ return { success: success2, data };
54090
+ }
54091
+ function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
54092
+ if (isTddPhase)
54093
+ return;
54094
+ if (opName === "semantic-review" || opName === "adversarial-review")
54095
+ return;
54096
+ const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
54097
+ if (!built)
54098
+ return;
54099
+ const { success: success2 } = built;
54100
+ const data = { ...built.data, ...progressData };
54101
+ const status = typeof built.data.status === "string" ? built.data.status : undefined;
54102
+ const logger = getSafeLogger();
54103
+ const message = formatPhaseResultMessage(opName, success2, stage, status);
54104
+ if (stage === "rectification") {
54105
+ logger?.info("story-orchestrator", message, data);
54106
+ return;
54107
+ }
54108
+ if (success2) {
54109
+ logger?.info("story-orchestrator", message, data);
54110
+ } else {
54111
+ logger?.warn("story-orchestrator", message, data);
54112
+ }
54113
+ }
54114
+ var init_story_orchestrator_logging = __esm(() => {
54115
+ init_logger2();
54116
+ });
54117
+
53967
54118
  // src/execution/story-orchestrator.ts
53968
54119
  async function refreshReviewInputForDispatch(opName, input) {
53969
54120
  if (opName !== "semantic-review" && opName !== "adversarial-review")
@@ -54024,15 +54175,6 @@ async function refreshReviewInputForDispatch(opName, input) {
54024
54175
  return fallback;
54025
54176
  }
54026
54177
  }
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
54178
  function isSlot(value) {
54037
54179
  return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
54038
54180
  }
@@ -54228,46 +54370,6 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
54228
54370
  logger?.info("review", "Running adversarial check", { storyId });
54229
54371
  }
54230
54372
  }
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
54373
  function logUnifiedReviewPhaseResult(storyId, opName, output) {
54272
54374
  const logger = getSafeLogger();
54273
54375
  const payload = toReviewDecisionPayload(opName, output);
@@ -54723,6 +54825,7 @@ var init_story_orchestrator = __esm(() => {
54723
54825
  init_prepare_inputs();
54724
54826
  init_git();
54725
54827
  init_non_blocking_fix();
54828
+ init_story_orchestrator_logging();
54726
54829
  _storyOrchestratorDeps = {
54727
54830
  callOp,
54728
54831
  runFixCycle,
@@ -54787,7 +54890,7 @@ var init_story_orchestrator = __esm(() => {
54787
54890
  });
54788
54891
 
54789
54892
  // src/execution/build-plan-for-strategy.ts
54790
- import { join as join47 } from "path";
54893
+ import { join as join48 } from "path";
54791
54894
  function requiresInitialRefCapture(strategy) {
54792
54895
  return isThreeSessionStrategy(strategy);
54793
54896
  }
@@ -54833,13 +54936,14 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
54833
54936
  }
54834
54937
  if (shouldRunRectification(config2) && inputs.rectification) {
54835
54938
  const sink = makeDeclarationSink();
54836
- const packageDir = join47(ctx.packageDir, story.workdir ?? "");
54939
+ const packageDir = join48(ctx.packageDir, story.workdir ?? "");
54837
54940
  const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
54838
54941
  const strategies = [];
54839
- if (config2.quality.commands.lintFix || config2.quality.commands.lintFixScoped) {
54942
+ const pkgQuality = ctx.packageView.select(qualityConfigSelector).quality;
54943
+ if (pkgQuality?.commands?.lintFix || pkgQuality?.commands?.lintFixScoped) {
54840
54944
  strategies.push(makeMechanicalLintFixStrategy());
54841
54945
  }
54842
- if (config2.quality.commands.formatFix || config2.quality.commands.formatFixScoped) {
54946
+ if (pkgQuality?.commands?.formatFix || pkgQuality?.commands?.formatFixScoped) {
54843
54947
  strategies.push(makeMechanicalFormatFixStrategy());
54844
54948
  }
54845
54949
  if (inputs.fullSuiteGate && (isThreeSession || regressionMode === "per-story")) {
@@ -56366,7 +56470,7 @@ function buildFrontmatter(story, ctx, role) {
56366
56470
  }
56367
56471
 
56368
56472
  // src/cli/prompts-tdd.ts
56369
- import { join as join48 } from "path";
56473
+ import { join as join49 } from "path";
56370
56474
  async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
56371
56475
  const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
56372
56476
  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 +56489,7 @@ ${frontmatter}---
56385
56489
 
56386
56490
  ${session.prompt}`;
56387
56491
  if (outputDir) {
56388
- const promptFile = join48(outputDir, `${story.id}.${session.role}.md`);
56492
+ const promptFile = join49(outputDir, `${story.id}.${session.role}.md`);
56389
56493
  await Bun.write(promptFile, fullOutput);
56390
56494
  logger.info("cli", "Written TDD prompt file", {
56391
56495
  storyId: story.id,
@@ -56401,7 +56505,7 @@ ${"=".repeat(80)}`);
56401
56505
  }
56402
56506
  }
56403
56507
  if (outputDir && ctx.contextMarkdown) {
56404
- const contextFile = join48(outputDir, `${story.id}.context.md`);
56508
+ const contextFile = join49(outputDir, `${story.id}.context.md`);
56405
56509
  const frontmatter = buildFrontmatter(story, ctx);
56406
56510
  const contextOutput = `---
56407
56511
  ${frontmatter}---
@@ -56416,16 +56520,16 @@ var init_prompts_tdd = __esm(() => {
56416
56520
 
56417
56521
  // src/cli/prompts-main.ts
56418
56522
  import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
56419
- import { join as join49 } from "path";
56523
+ import { join as join50 } from "path";
56420
56524
  async function promptsCommand(options) {
56421
56525
  const logger = getLogger();
56422
56526
  const { feature, workdir, config: config2, storyId, outputDir } = options;
56423
- const naxDir = join49(workdir, ".nax");
56527
+ const naxDir = join50(workdir, ".nax");
56424
56528
  if (!existsSync20(naxDir)) {
56425
56529
  throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
56426
56530
  }
56427
- const featureDir = join49(naxDir, "features", feature);
56428
- const prdPath = join49(featureDir, "prd.json");
56531
+ const featureDir = join50(naxDir, "features", feature);
56532
+ const prdPath = join50(featureDir, "prd.json");
56429
56533
  if (!existsSync20(prdPath)) {
56430
56534
  throw new Error(`Feature "${feature}" not found or missing prd.json`);
56431
56535
  }
@@ -56492,10 +56596,10 @@ ${frontmatter}---
56492
56596
 
56493
56597
  ${ctx.prompt}`;
56494
56598
  if (outputDir) {
56495
- const promptFile = join49(outputDir, `${story.id}.prompt.md`);
56599
+ const promptFile = join50(outputDir, `${story.id}.prompt.md`);
56496
56600
  await Bun.write(promptFile, fullOutput);
56497
56601
  if (ctx.contextMarkdown) {
56498
- const contextFile = join49(outputDir, `${story.id}.context.md`);
56602
+ const contextFile = join50(outputDir, `${story.id}.context.md`);
56499
56603
  const contextOutput = `---
56500
56604
  ${frontmatter}---
56501
56605
 
@@ -56531,12 +56635,12 @@ var init_prompts_main = __esm(() => {
56531
56635
 
56532
56636
  // src/cli/prompts-init.ts
56533
56637
  import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
56534
- import { join as join50 } from "path";
56638
+ import { join as join51 } from "path";
56535
56639
  async function promptsInitCommand(options) {
56536
56640
  const { workdir, force = false, autoWireConfig = true } = options;
56537
- const templatesDir = join50(workdir, ".nax", "templates");
56641
+ const templatesDir = join51(workdir, ".nax", "templates");
56538
56642
  mkdirSync4(templatesDir, { recursive: true });
56539
- const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join50(templatesDir, f)));
56643
+ const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join51(templatesDir, f)));
56540
56644
  if (existingFiles.length > 0 && !force) {
56541
56645
  _promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
56542
56646
  Pass --force to overwrite existing templates.`);
@@ -56544,7 +56648,7 @@ async function promptsInitCommand(options) {
56544
56648
  }
56545
56649
  const written = [];
56546
56650
  for (const template of TEMPLATE_ROLES) {
56547
- const filePath = join50(templatesDir, template.file);
56651
+ const filePath = join51(templatesDir, template.file);
56548
56652
  const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
56549
56653
  const content = TEMPLATE_HEADER + roleBody;
56550
56654
  await Bun.write(filePath, content);
@@ -56560,7 +56664,7 @@ async function promptsInitCommand(options) {
56560
56664
  return written;
56561
56665
  }
56562
56666
  async function autoWirePromptsConfig(workdir) {
56563
- const configPath = join50(workdir, "nax.config.json");
56667
+ const configPath = join51(workdir, "nax.config.json");
56564
56668
  if (!existsSync21(configPath)) {
56565
56669
  const exampleConfig = JSON.stringify({
56566
56670
  prompts: {
@@ -56728,7 +56832,7 @@ __export(exports_init_context, {
56728
56832
  generateContextTemplate: () => generateContextTemplate,
56729
56833
  _initContextDeps: () => _initContextDeps
56730
56834
  });
56731
- import { basename as basename9, join as join51 } from "path";
56835
+ import { basename as basename9, join as join52 } from "path";
56732
56836
  async function bunFileExists(path13) {
56733
56837
  return Bun.file(path13).exists();
56734
56838
  }
@@ -56763,7 +56867,7 @@ async function findFiles(dir, maxFiles = 200) {
56763
56867
  return [];
56764
56868
  }
56765
56869
  async function readPackageManifest(projectRoot) {
56766
- const packageJsonPath = join51(projectRoot, "package.json");
56870
+ const packageJsonPath = join52(projectRoot, "package.json");
56767
56871
  if (!await bunFileExists(packageJsonPath)) {
56768
56872
  return null;
56769
56873
  }
@@ -56781,7 +56885,7 @@ async function readPackageManifest(projectRoot) {
56781
56885
  }
56782
56886
  }
56783
56887
  async function readReadmeSnippet(projectRoot) {
56784
- const readmePath = join51(projectRoot, "README.md");
56888
+ const readmePath = join52(projectRoot, "README.md");
56785
56889
  if (!await bunFileExists(readmePath)) {
56786
56890
  return null;
56787
56891
  }
@@ -56799,7 +56903,7 @@ async function detectEntryPoints(projectRoot) {
56799
56903
  const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
56800
56904
  const found = [];
56801
56905
  for (const candidate of candidates) {
56802
- const path13 = join51(projectRoot, candidate);
56906
+ const path13 = join52(projectRoot, candidate);
56803
56907
  if (await bunFileExists(path13)) {
56804
56908
  found.push(candidate);
56805
56909
  }
@@ -56810,7 +56914,7 @@ async function detectConfigFiles(projectRoot) {
56810
56914
  const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
56811
56915
  const found = [];
56812
56916
  for (const candidate of candidates) {
56813
- const path13 = join51(projectRoot, candidate);
56917
+ const path13 = join52(projectRoot, candidate);
56814
56918
  if (await bunFileExists(path13)) {
56815
56919
  found.push(candidate);
56816
56920
  }
@@ -56971,8 +57075,8 @@ function generatePackageContextTemplate(packagePath) {
56971
57075
  }
56972
57076
  async function initPackage(repoRoot, packagePath, force = false) {
56973
57077
  const logger = getLogger();
56974
- const naxDir = join51(repoRoot, ".nax", "mono", packagePath);
56975
- const contextPath = join51(naxDir, "context.md");
57078
+ const naxDir = join52(repoRoot, ".nax", "mono", packagePath);
57079
+ const contextPath = join52(naxDir, "context.md");
56976
57080
  if (await bunFileExists(contextPath) && !force) {
56977
57081
  logger.info("init", "Package context.md already exists (use --force to overwrite)", {
56978
57082
  storyId: "init-context",
@@ -56989,8 +57093,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
56989
57093
  }
56990
57094
  async function initContext(projectRoot, options = {}) {
56991
57095
  const logger = getLogger();
56992
- const naxDir = join51(projectRoot, ".nax");
56993
- const contextPath = join51(naxDir, "context.md");
57096
+ const naxDir = join52(projectRoot, ".nax");
57097
+ const contextPath = join52(naxDir, "context.md");
56994
57098
  if (await bunFileExists(contextPath) && !options.force) {
56995
57099
  logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
56996
57100
  storyId: "init-context",
@@ -57026,9 +57130,9 @@ var init_init_context = __esm(() => {
57026
57130
 
57027
57131
  // src/cli/init-detect.ts
57028
57132
  import { existsSync as existsSync22, readFileSync } from "fs";
57029
- import { join as join52 } from "path";
57133
+ import { join as join53 } from "path";
57030
57134
  function readPackageJson(projectRoot) {
57031
- const pkgPath = join52(projectRoot, "package.json");
57135
+ const pkgPath = join53(projectRoot, "package.json");
57032
57136
  if (!existsSync22(pkgPath))
57033
57137
  return;
57034
57138
  try {
@@ -57071,41 +57175,41 @@ function detectStack(projectRoot) {
57071
57175
  };
57072
57176
  }
57073
57177
  function detectRuntime(projectRoot) {
57074
- if (existsSync22(join52(projectRoot, "bun.lockb")) || existsSync22(join52(projectRoot, "bunfig.toml"))) {
57178
+ if (existsSync22(join53(projectRoot, "bun.lockb")) || existsSync22(join53(projectRoot, "bunfig.toml"))) {
57075
57179
  return "bun";
57076
57180
  }
57077
- if (existsSync22(join52(projectRoot, "package-lock.json")) || existsSync22(join52(projectRoot, "yarn.lock")) || existsSync22(join52(projectRoot, "pnpm-lock.yaml"))) {
57181
+ if (existsSync22(join53(projectRoot, "package-lock.json")) || existsSync22(join53(projectRoot, "yarn.lock")) || existsSync22(join53(projectRoot, "pnpm-lock.yaml"))) {
57078
57182
  return "node";
57079
57183
  }
57080
57184
  return "unknown";
57081
57185
  }
57082
57186
  function detectLanguage2(projectRoot) {
57083
- if (existsSync22(join52(projectRoot, "tsconfig.json")))
57187
+ if (existsSync22(join53(projectRoot, "tsconfig.json")))
57084
57188
  return "typescript";
57085
- if (existsSync22(join52(projectRoot, "pyproject.toml")) || existsSync22(join52(projectRoot, "setup.py"))) {
57189
+ if (existsSync22(join53(projectRoot, "pyproject.toml")) || existsSync22(join53(projectRoot, "setup.py"))) {
57086
57190
  return "python";
57087
57191
  }
57088
- if (existsSync22(join52(projectRoot, "Cargo.toml")))
57192
+ if (existsSync22(join53(projectRoot, "Cargo.toml")))
57089
57193
  return "rust";
57090
- if (existsSync22(join52(projectRoot, "go.mod")))
57194
+ if (existsSync22(join53(projectRoot, "go.mod")))
57091
57195
  return "go";
57092
57196
  return "unknown";
57093
57197
  }
57094
57198
  function detectLinter(projectRoot) {
57095
- if (existsSync22(join52(projectRoot, "biome.json")) || existsSync22(join52(projectRoot, "biome.jsonc"))) {
57199
+ if (existsSync22(join53(projectRoot, "biome.json")) || existsSync22(join53(projectRoot, "biome.jsonc"))) {
57096
57200
  return "biome";
57097
57201
  }
57098
- if (existsSync22(join52(projectRoot, ".eslintrc.json")) || existsSync22(join52(projectRoot, ".eslintrc.js")) || existsSync22(join52(projectRoot, "eslint.config.js"))) {
57202
+ if (existsSync22(join53(projectRoot, ".eslintrc.json")) || existsSync22(join53(projectRoot, ".eslintrc.js")) || existsSync22(join53(projectRoot, "eslint.config.js"))) {
57099
57203
  return "eslint";
57100
57204
  }
57101
57205
  return "unknown";
57102
57206
  }
57103
57207
  function detectMonorepo(projectRoot) {
57104
- if (existsSync22(join52(projectRoot, "turbo.json")))
57208
+ if (existsSync22(join53(projectRoot, "turbo.json")))
57105
57209
  return "turborepo";
57106
- if (existsSync22(join52(projectRoot, "nx.json")))
57210
+ if (existsSync22(join53(projectRoot, "nx.json")))
57107
57211
  return "nx";
57108
- if (existsSync22(join52(projectRoot, "pnpm-workspace.yaml")))
57212
+ if (existsSync22(join53(projectRoot, "pnpm-workspace.yaml")))
57109
57213
  return "pnpm-workspaces";
57110
57214
  const pkg = readPackageJson(projectRoot);
57111
57215
  if (pkg?.workspaces)
@@ -57249,7 +57353,7 @@ __export(exports_init, {
57249
57353
  });
57250
57354
  import { existsSync as existsSync23 } from "fs";
57251
57355
  import { mkdir as mkdir8 } from "fs/promises";
57252
- import { join as join53 } from "path";
57356
+ import { join as join54 } from "path";
57253
57357
  function validateProjectName(name) {
57254
57358
  if (!name)
57255
57359
  return { valid: false, error: "name must be non-empty" };
@@ -57285,7 +57389,7 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
57285
57389
  }
57286
57390
  async function updateGitignore(projectRoot) {
57287
57391
  const logger = getLogger();
57288
- const gitignorePath = join53(projectRoot, ".gitignore");
57392
+ const gitignorePath = join54(projectRoot, ".gitignore");
57289
57393
  let existing = "";
57290
57394
  if (existsSync23(gitignorePath)) {
57291
57395
  existing = await Bun.file(gitignorePath).text();
@@ -57371,7 +57475,7 @@ async function initGlobal() {
57371
57475
  await mkdir8(globalDir, { recursive: true });
57372
57476
  logger.info("init", "Created global config directory", { path: globalDir });
57373
57477
  }
57374
- const configPath = join53(globalDir, "config.json");
57478
+ const configPath = join54(globalDir, "config.json");
57375
57479
  if (!existsSync23(configPath)) {
57376
57480
  await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
57377
57481
  `);
@@ -57379,14 +57483,14 @@ async function initGlobal() {
57379
57483
  } else {
57380
57484
  logger.info("init", "Global config already exists", { path: configPath });
57381
57485
  }
57382
- const constitutionPath = join53(globalDir, "constitution.md");
57486
+ const constitutionPath = join54(globalDir, "constitution.md");
57383
57487
  if (!existsSync23(constitutionPath)) {
57384
57488
  await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
57385
57489
  logger.info("init", "Created global constitution", { path: constitutionPath });
57386
57490
  } else {
57387
57491
  logger.info("init", "Global constitution already exists", { path: constitutionPath });
57388
57492
  }
57389
- const hooksDir = join53(globalDir, "hooks");
57493
+ const hooksDir = join54(globalDir, "hooks");
57390
57494
  if (!existsSync23(hooksDir)) {
57391
57495
  await mkdir8(hooksDir, { recursive: true });
57392
57496
  logger.info("init", "Created global hooks directory", { path: hooksDir });
@@ -57419,7 +57523,7 @@ async function initProject(projectRoot, options) {
57419
57523
  if (detectedName && !options?.force) {
57420
57524
  const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
57421
57525
  if (collision.collision && collision.existing) {
57422
- const configPath2 = join53(projectDir, "config.json");
57526
+ const configPath2 = join54(projectDir, "config.json");
57423
57527
  throw new NaxError([
57424
57528
  `Project name collision: "${detectedName}"`,
57425
57529
  ` This project: ${projectRoot}`,
@@ -57447,7 +57551,7 @@ async function initProject(projectRoot, options) {
57447
57551
  linter: stack.linter,
57448
57552
  monorepo: stack.monorepo
57449
57553
  });
57450
- const configPath = join53(projectDir, "config.json");
57554
+ const configPath = join54(projectDir, "config.json");
57451
57555
  if (!existsSync23(configPath)) {
57452
57556
  await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
57453
57557
  `);
@@ -57456,14 +57560,14 @@ async function initProject(projectRoot, options) {
57456
57560
  logger.info("init", "Project config already exists", { path: configPath });
57457
57561
  }
57458
57562
  await initContext(projectRoot, { ai: options?.ai, force: options?.force });
57459
- const constitutionPath = join53(projectDir, "constitution.md");
57563
+ const constitutionPath = join54(projectDir, "constitution.md");
57460
57564
  if (!existsSync23(constitutionPath) || options?.force) {
57461
57565
  await Bun.write(constitutionPath, buildConstitution(stack));
57462
57566
  logger.info("init", "Created project constitution", { path: constitutionPath });
57463
57567
  } else {
57464
57568
  logger.info("init", "Project constitution already exists", { path: constitutionPath });
57465
57569
  }
57466
- const hooksDir = join53(projectDir, "hooks");
57570
+ const hooksDir = join54(projectDir, "hooks");
57467
57571
  if (!existsSync23(hooksDir)) {
57468
57572
  await mkdir8(hooksDir, { recursive: true });
57469
57573
  logger.info("init", "Created project hooks directory", { path: hooksDir });
@@ -57525,36 +57629,36 @@ var init_init2 = __esm(() => {
57525
57629
  });
57526
57630
 
57527
57631
  // src/cli/setup-analyze.ts
57528
- import { join as join54 } from "path";
57632
+ import { join as join55 } from "path";
57529
57633
  async function detectPackageManager(workdir) {
57530
57634
  const { fileExists } = _analyzeRepoDeps;
57531
- if (await fileExists(join54(workdir, "bun.lock")))
57635
+ if (await fileExists(join55(workdir, "bun.lock")))
57532
57636
  return { pmRunPrefix: "bun run", pmDlx: "bunx" };
57533
- if (await fileExists(join54(workdir, "bun.lockb")))
57637
+ if (await fileExists(join55(workdir, "bun.lockb")))
57534
57638
  return { pmRunPrefix: "bun run", pmDlx: "bunx" };
57535
- if (await fileExists(join54(workdir, "package-lock.json")))
57639
+ if (await fileExists(join55(workdir, "package-lock.json")))
57536
57640
  return { pmRunPrefix: "npm run", pmDlx: "npx" };
57537
- if (await fileExists(join54(workdir, "yarn.lock")))
57641
+ if (await fileExists(join55(workdir, "yarn.lock")))
57538
57642
  return { pmRunPrefix: "yarn", pmDlx: "yarn dlx" };
57539
- if (await fileExists(join54(workdir, "pnpm-lock.yaml")))
57643
+ if (await fileExists(join55(workdir, "pnpm-lock.yaml")))
57540
57644
  return { pmRunPrefix: "pnpm run", pmDlx: "pnpx" };
57541
57645
  return { pmRunPrefix: "npm run", pmDlx: "npx" };
57542
57646
  }
57543
57647
  async function detectOrchestrator(workdir) {
57544
57648
  const { fileExists } = _analyzeRepoDeps;
57545
- if (await fileExists(join54(workdir, "turbo.json")))
57649
+ if (await fileExists(join55(workdir, "turbo.json")))
57546
57650
  return "turbo";
57547
- if (await fileExists(join54(workdir, "nx.json")))
57651
+ if (await fileExists(join55(workdir, "nx.json")))
57548
57652
  return "nx";
57549
57653
  return "none";
57550
57654
  }
57551
57655
  async function getMissingScripts(packageDir) {
57552
- const pkg = await _analyzeRepoDeps.readJson(join54(packageDir, "package.json"));
57656
+ const pkg = await _analyzeRepoDeps.readJson(join55(packageDir, "package.json"));
57553
57657
  const scripts = pkg?.scripts ?? {};
57554
57658
  return CANONICAL_SCRIPTS.filter((s) => !(s in scripts));
57555
57659
  }
57556
57660
  async function buildPackageFacts(workdir, relativeDir, testPatternMap) {
57557
- const packageDir = relativeDir === "" ? workdir : join54(workdir, relativeDir);
57661
+ const packageDir = relativeDir === "" ? workdir : join55(workdir, relativeDir);
57558
57662
  const [profile, missingScripts] = await Promise.all([
57559
57663
  _analyzeRepoDeps.detectProjectProfile(packageDir, {}),
57560
57664
  getMissingScripts(packageDir)
@@ -57615,7 +57719,7 @@ var init_setup_analyze = __esm(() => {
57615
57719
  });
57616
57720
 
57617
57721
  // src/cli/setup-fill.ts
57618
- import { join as join55 } from "path";
57722
+ import { join as join56 } from "path";
57619
57723
  async function addScriptToPackageJson(pkgJsonPath, key, value) {
57620
57724
  const pkg = await _fillScriptsDeps.readJson(pkgJsonPath) ?? {};
57621
57725
  const scripts = pkg.scripts ?? {};
@@ -57638,18 +57742,18 @@ async function fillScripts(workdir, analysis) {
57638
57742
  if (shape === "single") {
57639
57743
  const rootPkg = packages[0];
57640
57744
  if (rootPkg?.missingScripts.includes(TYPE_CHECK_KEY)) {
57641
- await addScriptToPackageJson(join55(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57745
+ await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57642
57746
  }
57643
57747
  return;
57644
57748
  }
57645
57749
  for (const pkg of packages) {
57646
57750
  if (!pkg.missingScripts.includes(TYPE_CHECK_KEY))
57647
57751
  continue;
57648
- await addScriptToPackageJson(join55(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57752
+ await addScriptToPackageJson(join56(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57649
57753
  }
57650
57754
  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);
57755
+ await addTurboTask(join56(workdir, "turbo.json"), TYPE_CHECK_KEY);
57756
+ await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_TURBO_PASSTHROUGH);
57653
57757
  }
57654
57758
  }
57655
57759
  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 +57814,11 @@ __export(exports_setup, {
57710
57814
  setupCommand: () => setupCommand,
57711
57815
  _setupDeps: () => _setupDeps
57712
57816
  });
57713
- import { join as join56 } from "path";
57817
+ import { join as join57 } from "path";
57714
57818
  async function setupCommand(options = {}) {
57715
57819
  const workdir = options.dir ?? process.cwd();
57716
- const naxDir = join56(workdir, ".nax");
57717
- const naxConfigPath = join56(naxDir, "config.json");
57820
+ const naxDir = join57(workdir, ".nax");
57821
+ const naxConfigPath = join57(naxDir, "config.json");
57718
57822
  const exists = await _setupDeps.fileExists(naxConfigPath);
57719
57823
  if (exists && !options.force) {
57720
57824
  _setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
@@ -57747,9 +57851,9 @@ ${JSON.stringify(plan.config, null, 2)}`);
57747
57851
  await _setupDeps.mkdir(naxDir);
57748
57852
  await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
57749
57853
  for (const mc of plan.monoConfigs) {
57750
- const monoDir = join56(naxDir, "mono", mc.relativeDir);
57854
+ const monoDir = join57(naxDir, "mono", mc.relativeDir);
57751
57855
  await _setupDeps.mkdir(monoDir);
57752
- await _setupDeps.writeFile(join56(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
57856
+ await _setupDeps.writeFile(join57(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
57753
57857
  }
57754
57858
  const gateResult = await _setupDeps.runGate(workdir, plan.config);
57755
57859
  if (gateResult !== 0) {
@@ -59178,12 +59282,12 @@ var init_loader4 = __esm(() => {
59178
59282
  });
59179
59283
 
59180
59284
  // src/utils/paths.ts
59181
- import { join as join67 } from "path";
59285
+ import { join as join68 } from "path";
59182
59286
  function getRunsDir() {
59183
- return process.env.NAX_RUNS_DIR ?? join67(globalConfigDir(), "runs");
59287
+ return process.env.NAX_RUNS_DIR ?? join68(globalConfigDir(), "runs");
59184
59288
  }
59185
59289
  function getEventsRootDir() {
59186
- return join67(globalConfigDir(), "events");
59290
+ return join68(globalConfigDir(), "events");
59187
59291
  }
59188
59292
  var init_paths3 = __esm(() => {
59189
59293
  init_paths();
@@ -59243,7 +59347,7 @@ var init_command_argv = __esm(() => {
59243
59347
  });
59244
59348
 
59245
59349
  // src/hooks/runner.ts
59246
- import { join as join74 } from "path";
59350
+ import { join as join75 } from "path";
59247
59351
  function createDrainDeadline2(deadlineMs) {
59248
59352
  let timeoutId;
59249
59353
  const promise2 = new Promise((resolve16) => {
@@ -59262,14 +59366,14 @@ async function loadHooksConfig(projectDir, globalDir) {
59262
59366
  let globalHooks = { hooks: {} };
59263
59367
  let projectHooks = { hooks: {} };
59264
59368
  let skipGlobal = false;
59265
- const projectPath = join74(projectDir, "hooks.json");
59369
+ const projectPath = join75(projectDir, "hooks.json");
59266
59370
  const projectData = await loadJsonFile(projectPath, "hooks");
59267
59371
  if (projectData) {
59268
59372
  projectHooks = projectData;
59269
59373
  skipGlobal = projectData.skipGlobal ?? false;
59270
59374
  }
59271
59375
  if (!skipGlobal && globalDir) {
59272
- const globalPath = join74(globalDir, "hooks.json");
59376
+ const globalPath = join75(globalDir, "hooks.json");
59273
59377
  const globalData = await loadJsonFile(globalPath, "hooks");
59274
59378
  if (globalData) {
59275
59379
  globalHooks = globalData;
@@ -59439,7 +59543,7 @@ var package_default;
59439
59543
  var init_package = __esm(() => {
59440
59544
  package_default = {
59441
59545
  name: "@nathapp/nax",
59442
- version: "0.69.1",
59546
+ version: "0.69.2",
59443
59547
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
59444
59548
  type: "module",
59445
59549
  bin: {
@@ -59534,8 +59638,8 @@ var init_version = __esm(() => {
59534
59638
  NAX_VERSION = package_default.version;
59535
59639
  NAX_COMMIT = (() => {
59536
59640
  try {
59537
- if (/^[0-9a-f]{6,10}$/.test("3b2af55b"))
59538
- return "3b2af55b";
59641
+ if (/^[0-9a-f]{6,10}$/.test("99db370f"))
59642
+ return "99db370f";
59539
59643
  } catch {}
59540
59644
  try {
59541
59645
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -60412,15 +60516,15 @@ var init_acceptance_loop = __esm(() => {
60412
60516
 
60413
60517
  // src/session/scratch-purge.ts
60414
60518
  import { mkdir as mkdir12, rename, rm } from "fs/promises";
60415
- import { dirname as dirname12, join as join75 } from "path";
60519
+ import { dirname as dirname12, join as join76 } from "path";
60416
60520
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
60417
- const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
60521
+ const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
60418
60522
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
60419
60523
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
60420
60524
  let purged = 0;
60421
60525
  for (const sessionId of sessionIds) {
60422
- const sessionDir = join75(sessionsDir, sessionId);
60423
- const descriptorPath = join75(sessionDir, "descriptor.json");
60526
+ const sessionDir = join76(sessionsDir, sessionId);
60527
+ const descriptorPath = join76(sessionDir, "descriptor.json");
60424
60528
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
60425
60529
  continue;
60426
60530
  let lastActivityAt;
@@ -60436,7 +60540,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
60436
60540
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
60437
60541
  continue;
60438
60542
  if (archiveInsteadOfDelete) {
60439
- const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60543
+ const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60440
60544
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
60441
60545
  } else {
60442
60546
  await _scratchPurgeDeps.remove(sessionDir);
@@ -61174,12 +61278,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
61174
61278
 
61175
61279
  // src/pipeline/subscribers/events-writer.ts
61176
61280
  import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
61177
- import { basename as basename13, join as join76 } from "path";
61281
+ import { basename as basename13, join as join77 } from "path";
61178
61282
  function wireEventsWriter(bus, feature, runId, workdir) {
61179
61283
  const logger = getSafeLogger();
61180
61284
  const project = basename13(workdir);
61181
- const eventsDir = join76(getEventsRootDir(), project);
61182
- const eventsFile = join76(eventsDir, "events.jsonl");
61285
+ const eventsDir = join77(getEventsRootDir(), project);
61286
+ const eventsFile = join77(eventsDir, "events.jsonl");
61183
61287
  let dirReady = false;
61184
61288
  const write = (line) => {
61185
61289
  return (async () => {
@@ -61360,12 +61464,12 @@ var init_interaction2 = __esm(() => {
61360
61464
 
61361
61465
  // src/pipeline/subscribers/registry.ts
61362
61466
  import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
61363
- import { basename as basename14, join as join77 } from "path";
61467
+ import { basename as basename14, join as join78 } from "path";
61364
61468
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
61365
61469
  const logger = getSafeLogger();
61366
61470
  const project = basename14(workdir);
61367
- const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
61368
- const metaFile = join77(runDir, "meta.json");
61471
+ const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
61472
+ const metaFile = join78(runDir, "meta.json");
61369
61473
  const unsub = bus.on("run:started", (_ev) => {
61370
61474
  return (async () => {
61371
61475
  try {
@@ -61375,8 +61479,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
61375
61479
  project,
61376
61480
  feature,
61377
61481
  workdir,
61378
- statusPath: join77(outputDir, "features", feature, "status.json"),
61379
- eventsDir: join77(outputDir, "features", feature, "runs"),
61482
+ statusPath: join78(outputDir, "features", feature, "status.json"),
61483
+ eventsDir: join78(outputDir, "features", feature, "runs"),
61380
61484
  registeredAt: new Date().toISOString()
61381
61485
  };
61382
61486
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -61622,7 +61726,7 @@ var init_types9 = __esm(() => {
61622
61726
 
61623
61727
  // src/worktree/dependencies.ts
61624
61728
  import { existsSync as existsSync30 } from "fs";
61625
- import { join as join78 } from "path";
61729
+ import { join as join79 } from "path";
61626
61730
  async function prepareWorktreeDependencies(options) {
61627
61731
  const mode = options.config.execution.worktreeDependencies.mode;
61628
61732
  const resolvedCwd = resolveDependencyCwd(options);
@@ -61636,7 +61740,7 @@ async function prepareWorktreeDependencies(options) {
61636
61740
  }
61637
61741
  }
61638
61742
  function resolveDependencyCwd(options) {
61639
- return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
61743
+ return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
61640
61744
  }
61641
61745
  function resolveInheritedDependencies(options, resolvedCwd) {
61642
61746
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -61646,7 +61750,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
61646
61750
  }
61647
61751
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
61648
61752
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
61649
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join78(directory, filename))));
61753
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
61650
61754
  }
61651
61755
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
61652
61756
  const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
@@ -61710,13 +61814,13 @@ __export(exports_manager, {
61710
61814
  });
61711
61815
  import { existsSync as existsSync31, symlinkSync } from "fs";
61712
61816
  import { mkdir as mkdir15 } from "fs/promises";
61713
- import { join as join79 } from "path";
61817
+ import { join as join80 } from "path";
61714
61818
 
61715
61819
  class WorktreeManager {
61716
61820
  async ensureGitExcludes(projectRoot) {
61717
61821
  const logger = getSafeLogger();
61718
- const infoDir = join79(projectRoot, ".git", "info");
61719
- const excludePath = join79(infoDir, "exclude");
61822
+ const infoDir = join80(projectRoot, ".git", "info");
61823
+ const excludePath = join80(infoDir, "exclude");
61720
61824
  try {
61721
61825
  await mkdir15(infoDir, { recursive: true });
61722
61826
  let existing = "";
@@ -61743,7 +61847,7 @@ ${missing.join(`
61743
61847
  }
61744
61848
  async create(projectRoot, storyId) {
61745
61849
  validateStoryId(storyId);
61746
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
61850
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
61747
61851
  const branchName = `nax/${storyId}`;
61748
61852
  try {
61749
61853
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -61784,9 +61888,9 @@ ${missing.join(`
61784
61888
  }
61785
61889
  throw new Error(`Failed to create worktree: ${String(error48)}`);
61786
61890
  }
61787
- const envSource = join79(projectRoot, ".env");
61891
+ const envSource = join80(projectRoot, ".env");
61788
61892
  if (existsSync31(envSource)) {
61789
- const envTarget = join79(worktreePath, ".env");
61893
+ const envTarget = join80(worktreePath, ".env");
61790
61894
  try {
61791
61895
  symlinkSync(envSource, envTarget, "file");
61792
61896
  } catch (error48) {
@@ -61797,7 +61901,7 @@ ${missing.join(`
61797
61901
  }
61798
61902
  async remove(projectRoot, storyId) {
61799
61903
  validateStoryId(storyId);
61800
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
61904
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
61801
61905
  const branchName = `nax/${storyId}`;
61802
61906
  try {
61803
61907
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -62609,10 +62713,10 @@ var init_merge_conflict_rectify = __esm(() => {
62609
62713
  });
62610
62714
 
62611
62715
  // src/execution/pipeline-result-handler.ts
62612
- import { join as join80 } from "path";
62716
+ import { join as join81 } from "path";
62613
62717
  async function removeWorktreeDirectory(projectRoot, storyId) {
62614
62718
  const logger = getSafeLogger();
62615
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62719
+ const worktreePath = join81(projectRoot, ".nax-wt", storyId);
62616
62720
  try {
62617
62721
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
62618
62722
  cwd: projectRoot,
@@ -62829,7 +62933,7 @@ var init_pipeline_result_handler = __esm(() => {
62829
62933
 
62830
62934
  // src/execution/iteration-runner.ts
62831
62935
  import { existsSync as existsSync32 } from "fs";
62832
- import { join as join81 } from "path";
62936
+ import { join as join82 } from "path";
62833
62937
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
62834
62938
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
62835
62939
  if (ctx.dryRun) {
@@ -62854,7 +62958,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62854
62958
  const storyStartTime = Date.now();
62855
62959
  let effectiveWorkdir = ctx.workdir;
62856
62960
  if (ctx.config.execution.storyIsolation === "worktree") {
62857
- const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
62961
+ const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
62858
62962
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
62859
62963
  if (!worktreeExists) {
62860
62964
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -62874,7 +62978,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62874
62978
  }
62875
62979
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
62876
62980
  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;
62981
+ const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join82(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
62878
62982
  let dependencyContext;
62879
62983
  if (ctx.config.execution.storyIsolation === "worktree") {
62880
62984
  try {
@@ -62901,7 +63005,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62901
63005
  };
62902
63006
  }
62903
63007
  }
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;
63008
+ 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
63009
  const pipelineContext = {
62906
63010
  config: effectiveConfig,
62907
63011
  rootConfig: ctx.config,
@@ -63103,7 +63207,7 @@ __export(exports_parallel_worker, {
63103
63207
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
63104
63208
  _parallelWorkerDeps: () => _parallelWorkerDeps
63105
63209
  });
63106
- import { join as join82 } from "path";
63210
+ import { join as join83 } from "path";
63107
63211
  function buildWorktreePipelineContext(base, _story) {
63108
63212
  return { ...base, prd: structuredClone(base.prd) };
63109
63213
  }
@@ -63126,7 +63230,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
63126
63230
  story,
63127
63231
  stories: [story],
63128
63232
  projectDir: context.projectDir,
63129
- workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
63233
+ workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
63130
63234
  worktreeDependencyContext: dependencyContext,
63131
63235
  routing,
63132
63236
  storyGitRef: storyGitRef ?? undefined
@@ -64013,7 +64117,7 @@ async function writeStatusFile(filePath, status) {
64013
64117
  var init_status_file = () => {};
64014
64118
 
64015
64119
  // src/execution/status-writer.ts
64016
- import { join as join83 } from "path";
64120
+ import { join as join84 } from "path";
64017
64121
 
64018
64122
  class StatusWriter {
64019
64123
  statusFile;
@@ -64132,7 +64236,7 @@ class StatusWriter {
64132
64236
  if (!this._prd)
64133
64237
  return;
64134
64238
  const safeLogger = getSafeLogger();
64135
- const featureStatusPath = join83(featureDir, "status.json");
64239
+ const featureStatusPath = join84(featureDir, "status.json");
64136
64240
  const write = async () => {
64137
64241
  try {
64138
64242
  const base = this.getSnapshot(totalCost, iterations);
@@ -64566,7 +64670,7 @@ __export(exports_run_initialization, {
64566
64670
  initializeRun: () => initializeRun,
64567
64671
  _reconcileDeps: () => _reconcileDeps
64568
64672
  });
64569
- import { join as join84 } from "path";
64673
+ import { join as join85 } from "path";
64570
64674
  async function reconcileState(prd, prdPath, workdir, config2) {
64571
64675
  const logger = getSafeLogger();
64572
64676
  let reconciledCount = 0;
@@ -64583,7 +64687,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
64583
64687
  });
64584
64688
  continue;
64585
64689
  }
64586
- const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
64690
+ const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
64587
64691
  try {
64588
64692
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
64589
64693
  if (!reviewResult.success) {
@@ -64849,6 +64953,17 @@ async function setupRun(options) {
64849
64953
  featureName: options.feature,
64850
64954
  agentStreamEvents: options.agentStreamEvents
64851
64955
  });
64956
+ try {
64957
+ const workspacePackages = await discoverWorkspacePackages(workdir);
64958
+ if (workspacePackages.length > 0) {
64959
+ await runtime.packages.hydrate(workspacePackages);
64960
+ }
64961
+ } catch (err) {
64962
+ getSafeLogger()?.warn("run-setup", "Per-package config hydration failed \u2014 using root config", {
64963
+ storyId: "_setup",
64964
+ error: errorMessage(err)
64965
+ });
64966
+ }
64852
64967
  await runtime.pidRegistry.cleanupStale();
64853
64968
  const cleanupCrashHandlers = installCrashHandlers({
64854
64969
  statusWriter,
@@ -65015,6 +65130,7 @@ async function setupRun(options) {
65015
65130
  var _runSetupDeps;
65016
65131
  var init_run_setup = __esm(() => {
65017
65132
  init_pipeline();
65133
+ init_test_runners();
65018
65134
  init_paths();
65019
65135
  init_errors();
65020
65136
  init_interaction();
@@ -65024,7 +65140,6 @@ var init_run_setup = __esm(() => {
65024
65140
  init_project();
65025
65141
  init_runtime();
65026
65142
  init_session();
65027
- init_resolver();
65028
65143
  init_version();
65029
65144
  init_crash_recovery();
65030
65145
  init_helpers();
@@ -94376,7 +94491,7 @@ __export(exports_curator, {
94376
94491
  });
94377
94492
  import { readdirSync as readdirSync8 } from "fs";
94378
94493
  import { unlink as unlink4 } from "fs/promises";
94379
- import { basename as basename15, join as join86 } from "path";
94494
+ import { basename as basename15, join as join87 } from "path";
94380
94495
  function getProjectKey(config2, projectDir) {
94381
94496
  return config2.name?.trim() || basename15(projectDir);
94382
94497
  }
@@ -94459,7 +94574,7 @@ async function curatorStatus(options) {
94459
94574
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94460
94575
  const projectKey = getProjectKey(config2, resolved.projectDir);
94461
94576
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94462
- const runsDir = join86(outputDir, "runs");
94577
+ const runsDir = join87(outputDir, "runs");
94463
94578
  const runIds = listRunIds(runsDir);
94464
94579
  let runId;
94465
94580
  if (options.run) {
@@ -94476,8 +94591,8 @@ async function curatorStatus(options) {
94476
94591
  runId = runIds[runIds.length - 1];
94477
94592
  }
94478
94593
  console.log(`Run: ${runId}`);
94479
- const runDir = join86(runsDir, runId);
94480
- const observationsPath = join86(runDir, "observations.jsonl");
94594
+ const runDir = join87(runsDir, runId);
94595
+ const observationsPath = join87(runDir, "observations.jsonl");
94481
94596
  const observations = await parseObservations(observationsPath);
94482
94597
  const counts = new Map;
94483
94598
  for (const obs of observations) {
@@ -94487,7 +94602,7 @@ async function curatorStatus(options) {
94487
94602
  for (const [kind, count] of counts.entries()) {
94488
94603
  console.log(` ${kind}: ${count}`);
94489
94604
  }
94490
- const proposalsPath = join86(runDir, "curator-proposals.md");
94605
+ const proposalsPath = join87(runDir, "curator-proposals.md");
94491
94606
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94492
94607
  if (proposalText !== null) {
94493
94608
  console.log("");
@@ -94501,8 +94616,8 @@ async function curatorCommit(options) {
94501
94616
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94502
94617
  const projectKey = getProjectKey(config2, resolved.projectDir);
94503
94618
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94504
- const runDir = join86(outputDir, "runs", options.runId);
94505
- const proposalsPath = join86(runDir, "curator-proposals.md");
94619
+ const runDir = join87(outputDir, "runs", options.runId);
94620
+ const proposalsPath = join87(runDir, "curator-proposals.md");
94506
94621
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
94507
94622
  if (proposalText === null) {
94508
94623
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -94518,7 +94633,7 @@ async function curatorCommit(options) {
94518
94633
  const dropFileState = new Map;
94519
94634
  const skippedDrops = new Set;
94520
94635
  for (const drop2 of drops) {
94521
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94636
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94522
94637
  if (!dropFileState.has(targetPath)) {
94523
94638
  const fileExists2 = await Bun.file(targetPath).exists();
94524
94639
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -94552,7 +94667,7 @@ async function curatorCommit(options) {
94552
94667
  if (skippedDrops.has(drop2)) {
94553
94668
  continue;
94554
94669
  }
94555
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
94670
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
94556
94671
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
94557
94672
  const filtered = filterDropContent(existing, drop2.description);
94558
94673
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -94561,7 +94676,7 @@ async function curatorCommit(options) {
94561
94676
  }
94562
94677
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
94563
94678
  for (const add2 of adds) {
94564
- const targetPath = join86(resolved.projectDir, add2.canonicalFile);
94679
+ const targetPath = join87(resolved.projectDir, add2.canonicalFile);
94565
94680
  const content = buildAddContent(add2);
94566
94681
  await _curatorCmdDeps.appendFile(targetPath, content);
94567
94682
  modifiedFiles.add(targetPath);
@@ -94598,7 +94713,7 @@ async function curatorDryrun(options) {
94598
94713
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94599
94714
  const projectKey = getProjectKey(config2, resolved.projectDir);
94600
94715
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94601
- const runsDir = join86(outputDir, "runs");
94716
+ const runsDir = join87(outputDir, "runs");
94602
94717
  const runIds = listRunIds(runsDir);
94603
94718
  if (runIds.length === 0) {
94604
94719
  console.log("No runs found.");
@@ -94609,7 +94724,7 @@ async function curatorDryrun(options) {
94609
94724
  console.log(`Run ${options.run} not found in ${runsDir}.`);
94610
94725
  return;
94611
94726
  }
94612
- const observationsPath = join86(runsDir, runId, "observations.jsonl");
94727
+ const observationsPath = join87(runsDir, runId, "observations.jsonl");
94613
94728
  const observations = await parseObservations(observationsPath);
94614
94729
  const thresholds = getThresholds(config2);
94615
94730
  const proposals = runHeuristics(observations, thresholds);
@@ -94650,12 +94765,12 @@ async function curatorGc(options) {
94650
94765
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
94651
94766
  const projectKey = getProjectKey(config2, resolved.projectDir);
94652
94767
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94653
- const perRunsDir = join86(outputDir, "runs");
94768
+ const perRunsDir = join87(outputDir, "runs");
94654
94769
  for (const runId of uniqueRunIds) {
94655
94770
  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"));
94771
+ const runDir = join87(perRunsDir, runId);
94772
+ await _curatorCmdDeps.removeFile(join87(runDir, "observations.jsonl"));
94773
+ await _curatorCmdDeps.removeFile(join87(runDir, "curator-proposals.md"));
94659
94774
  }
94660
94775
  }
94661
94776
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -94700,7 +94815,7 @@ var init_curator2 = __esm(() => {
94700
94815
  init_source();
94701
94816
  import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
94702
94817
  import { homedir as homedir3 } from "os";
94703
- import { basename as basename16, join as join87 } from "path";
94818
+ import { basename as basename16, join as join88 } from "path";
94704
94819
 
94705
94820
  // node_modules/commander/esm.mjs
94706
94821
  var import__ = __toESM(require_commander(), 1);
@@ -94724,12 +94839,12 @@ init_errors();
94724
94839
  init_operations();
94725
94840
 
94726
94841
  // src/plan/strategies/context-builder.ts
94727
- import { join as join38 } from "path";
94842
+ import { join as join39 } from "path";
94728
94843
  init_config();
94729
94844
  init_errors();
94730
94845
  init_interaction();
94731
94846
  async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94732
- const naxDir = join38(workdir, ".nax");
94847
+ const naxDir = join39(workdir, ".nax");
94733
94848
  if (!deps.existsSync(naxDir)) {
94734
94849
  throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
94735
94850
  stage: "plan",
@@ -94737,8 +94852,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94737
94852
  });
94738
94853
  }
94739
94854
  validateFeatureName(options.feature);
94740
- const outputDir = join38(naxDir, "features", options.feature);
94741
- const outputPath = join38(outputDir, "prd.json");
94855
+ const outputDir = join39(naxDir, "features", options.feature);
94856
+ const outputPath = join39(outputDir, "prd.json");
94742
94857
  const [specContent, sourceRoots, pkg] = await Promise.all([
94743
94858
  deps.readFile(options.from),
94744
94859
  deps.scanSourceRoots(workdir),
@@ -94753,7 +94868,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94753
94868
  ...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
94754
94869
  ];
94755
94870
  const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
94756
- const packageJson = await deps.readPackageJsonAt(join38(workdir, relativePath, "package.json"));
94871
+ const packageJson = await deps.readPackageJsonAt(join39(workdir, relativePath, "package.json"));
94757
94872
  return buildPackageSummary(relativePath, packageJson);
94758
94873
  }));
94759
94874
  const projectName = detectProjectName(workdir, pkg);
@@ -95356,7 +95471,7 @@ init_interaction();
95356
95471
  init_prd();
95357
95472
  init_runtime();
95358
95473
  import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
95359
- import { basename as basename7, join as join43, resolve as resolve14 } from "path";
95474
+ import { basename as basename7, join as join44, resolve as resolve14 } from "path";
95360
95475
  var _statusFeaturesDeps = {
95361
95476
  projectOutputDir,
95362
95477
  loadConfig
@@ -95370,7 +95485,7 @@ function isPidAlive(pid) {
95370
95485
  }
95371
95486
  }
95372
95487
  async function loadStatusFile(featureDir) {
95373
- const statusPath = join43(featureDir, "status.json");
95488
+ const statusPath = join44(featureDir, "status.json");
95374
95489
  if (!existsSync17(statusPath)) {
95375
95490
  return null;
95376
95491
  }
@@ -95385,7 +95500,7 @@ async function loadProjectStatusFile(projectDir) {
95385
95500
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95386
95501
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95387
95502
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95388
- const statusPath = join43(outputDir, "status.json");
95503
+ const statusPath = join44(outputDir, "status.json");
95389
95504
  if (!existsSync17(statusPath)) {
95390
95505
  return null;
95391
95506
  }
@@ -95397,7 +95512,7 @@ async function loadProjectStatusFile(projectDir) {
95397
95512
  }
95398
95513
  }
95399
95514
  async function getFeatureSummary(featureName, featureDir) {
95400
- const prdPath = join43(featureDir, "prd.json");
95515
+ const prdPath = join44(featureDir, "prd.json");
95401
95516
  if (!existsSync17(prdPath)) {
95402
95517
  return {
95403
95518
  name: featureName,
@@ -95440,7 +95555,7 @@ async function getFeatureSummary(featureName, featureDir) {
95440
95555
  };
95441
95556
  }
95442
95557
  }
95443
- const runsDir = join43(featureDir, "runs");
95558
+ const runsDir = join44(featureDir, "runs");
95444
95559
  if (existsSync17(runsDir)) {
95445
95560
  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
95561
  if (runs.length > 0) {
@@ -95454,7 +95569,7 @@ async function displayAllFeatures(projectDir) {
95454
95569
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95455
95570
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95456
95571
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95457
- const featuresDir = join43(outputDir, "features");
95572
+ const featuresDir = join44(outputDir, "features");
95458
95573
  if (!existsSync17(featuresDir)) {
95459
95574
  console.log(source_default.dim("No features found."));
95460
95575
  return;
@@ -95495,7 +95610,7 @@ async function displayAllFeatures(projectDir) {
95495
95610
  console.log();
95496
95611
  }
95497
95612
  }
95498
- const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join43(featuresDir, name))));
95613
+ const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join44(featuresDir, name))));
95499
95614
  console.log(source_default.bold(`\uD83D\uDCCA Features
95500
95615
  `));
95501
95616
  const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
@@ -95521,7 +95636,7 @@ async function displayAllFeatures(projectDir) {
95521
95636
  console.log();
95522
95637
  }
95523
95638
  async function displayFeatureDetails(featureName, featureDir) {
95524
- const prdPath = join43(featureDir, "prd.json");
95639
+ const prdPath = join44(featureDir, "prd.json");
95525
95640
  if (!existsSync17(prdPath)) {
95526
95641
  console.log(source_default.bold(`
95527
95642
  \uD83D\uDCCA ${featureName}
@@ -95667,7 +95782,7 @@ async function displayFeatureStatus(options = {}) {
95667
95782
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
95668
95783
  const projectKey = config2?.name?.trim() || basename7(projectDir);
95669
95784
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
95670
- featureDir = join43(outputDir, "features", options.feature);
95785
+ featureDir = join44(outputDir, "features", options.feature);
95671
95786
  } else {
95672
95787
  const resolved = resolveProject({ feature: options.feature });
95673
95788
  if (!resolved.featureDir) {
@@ -95687,7 +95802,7 @@ init_errors();
95687
95802
  init_logger2();
95688
95803
  init_runtime();
95689
95804
  import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
95690
- import { basename as basename8, join as join44 } from "path";
95805
+ import { basename as basename8, join as join45 } from "path";
95691
95806
  async function resolveOutputDir2(workdir, override) {
95692
95807
  if (override)
95693
95808
  return override;
@@ -95711,7 +95826,7 @@ async function runsListCommand(options) {
95711
95826
  const logger = getLogger();
95712
95827
  const { feature, workdir } = options;
95713
95828
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
95714
- const runsDir = join44(outputDir, "features", feature, "runs");
95829
+ const runsDir = join45(outputDir, "features", feature, "runs");
95715
95830
  if (!existsSync18(runsDir)) {
95716
95831
  logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
95717
95832
  return;
@@ -95723,7 +95838,7 @@ async function runsListCommand(options) {
95723
95838
  }
95724
95839
  logger.info("cli", `Runs for ${feature}`, { count: files.length });
95725
95840
  for (const file3 of files.sort().reverse()) {
95726
- const logPath = join44(runsDir, file3);
95841
+ const logPath = join45(runsDir, file3);
95727
95842
  const entries = await parseRunLog(logPath);
95728
95843
  const startEvent = entries.find((e) => e.message === "run.start");
95729
95844
  const completeEvent = entries.find((e) => e.message === "run.complete");
@@ -95750,7 +95865,7 @@ async function runsShowCommand(options) {
95750
95865
  const logger = getLogger();
95751
95866
  const { runId, feature, workdir } = options;
95752
95867
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
95753
- const logPath = join44(outputDir, "features", feature, "runs", `${runId}.jsonl`);
95868
+ const logPath = join45(outputDir, "features", feature, "runs", `${runId}.jsonl`);
95754
95869
  if (!existsSync18(logPath)) {
95755
95870
  logger.error("cli", "Run not found", { runId, feature, logPath });
95756
95871
  throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
@@ -95866,7 +95981,7 @@ init_source();
95866
95981
  init_loader();
95867
95982
  init_generator2();
95868
95983
  import { existsSync as existsSync24 } from "fs";
95869
- import { join as join61 } from "path";
95984
+ import { join as join62 } from "path";
95870
95985
  var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
95871
95986
  async function generateCommand(options) {
95872
95987
  const workdir = options.dir ?? process.cwd();
@@ -95909,7 +96024,7 @@ async function generateCommand(options) {
95909
96024
  return;
95910
96025
  }
95911
96026
  if (options.package) {
95912
- const packageDir = join61(workdir, options.package);
96027
+ const packageDir = join62(workdir, options.package);
95913
96028
  if (dryRun) {
95914
96029
  console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
95915
96030
  }
@@ -95929,8 +96044,8 @@ async function generateCommand(options) {
95929
96044
  process.exit(1);
95930
96045
  return;
95931
96046
  }
95932
- const contextPath = options.context ? join61(workdir, options.context) : join61(workdir, ".nax/context.md");
95933
- const outputDir = options.output ? join61(workdir, options.output) : workdir;
96047
+ const contextPath = options.context ? join62(workdir, options.context) : join62(workdir, ".nax/context.md");
96048
+ const outputDir = options.output ? join62(workdir, options.output) : workdir;
95934
96049
  const autoInject = !options.noAutoInject;
95935
96050
  if (!existsSync24(contextPath)) {
95936
96051
  console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
@@ -96036,7 +96151,7 @@ async function generateCommand(options) {
96036
96151
  // src/cli/config-display.ts
96037
96152
  init_loader();
96038
96153
  import { existsSync as existsSync26 } from "fs";
96039
- import { join as join63 } from "path";
96154
+ import { join as join64 } from "path";
96040
96155
 
96041
96156
  // src/cli/config-descriptions.ts
96042
96157
  var FIELD_DESCRIPTIONS = {
@@ -96288,7 +96403,7 @@ function deepEqual(a, b) {
96288
96403
  init_defaults();
96289
96404
  init_loader();
96290
96405
  import { existsSync as existsSync25 } from "fs";
96291
- import { join as join62 } from "path";
96406
+ import { join as join63 } from "path";
96292
96407
  async function loadConfigFile(path18) {
96293
96408
  if (!existsSync25(path18))
96294
96409
  return null;
@@ -96310,7 +96425,7 @@ async function loadProjectConfig() {
96310
96425
  const projectDir = findProjectDir();
96311
96426
  if (!projectDir)
96312
96427
  return null;
96313
- const projectPath = join62(projectDir, "config.json");
96428
+ const projectPath = join63(projectDir, "config.json");
96314
96429
  return await loadConfigFile(projectPath);
96315
96430
  }
96316
96431
 
@@ -96370,7 +96485,7 @@ async function configCommand(config2, options = {}) {
96370
96485
  function determineConfigSources() {
96371
96486
  const globalPath = globalConfigPath();
96372
96487
  const projectDir = findProjectDir();
96373
- const projectPath = projectDir ? join63(projectDir, "config.json") : null;
96488
+ const projectPath = projectDir ? join64(projectDir, "config.json") : null;
96374
96489
  return {
96375
96490
  global: fileExists(globalPath) ? globalPath : null,
96376
96491
  project: projectPath && fileExists(projectPath) ? projectPath : null
@@ -96519,15 +96634,15 @@ init_paths();
96519
96634
  init_profile();
96520
96635
  import { mkdirSync as mkdirSync5 } from "fs";
96521
96636
  import { readdirSync as readdirSync5 } from "fs";
96522
- import { join as join64 } from "path";
96637
+ import { join as join65 } from "path";
96523
96638
  var _profileCLIDeps = {
96524
96639
  env: process.env
96525
96640
  };
96526
96641
  var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
96527
96642
  var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
96528
96643
  async function profileListCommand(startDir) {
96529
- const globalProfilesDir = join64(globalConfigDir(), "profiles");
96530
- const projectProfilesDir = join64(projectConfigDir(startDir), "profiles");
96644
+ const globalProfilesDir = join65(globalConfigDir(), "profiles");
96645
+ const projectProfilesDir = join65(projectConfigDir(startDir), "profiles");
96531
96646
  const globalProfiles = scanProfileDir(globalProfilesDir);
96532
96647
  const projectProfiles = scanProfileDir(projectProfilesDir);
96533
96648
  const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
@@ -96586,7 +96701,7 @@ function maskProfileValues(obj) {
96586
96701
  return result;
96587
96702
  }
96588
96703
  async function profileUseCommand(profileName, startDir) {
96589
- const configPath = join64(projectConfigDir(startDir), "config.json");
96704
+ const configPath = join65(projectConfigDir(startDir), "config.json");
96590
96705
  const configFile = Bun.file(configPath);
96591
96706
  let existing = {};
96592
96707
  if (await configFile.exists()) {
@@ -96605,8 +96720,8 @@ async function profileCurrentCommand(startDir) {
96605
96720
  return resolveProfileName({}, _profileCLIDeps.env, startDir);
96606
96721
  }
96607
96722
  async function profileCreateCommand(profileName, startDir) {
96608
- const profilesDir = join64(projectConfigDir(startDir), "profiles");
96609
- const profilePath = join64(profilesDir, `${profileName}.json`);
96723
+ const profilesDir = join65(projectConfigDir(startDir), "profiles");
96724
+ const profilePath = join65(profilesDir, `${profileName}.json`);
96610
96725
  const profileFile = Bun.file(profilePath);
96611
96726
  if (await profileFile.exists()) {
96612
96727
  throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
@@ -96728,7 +96843,7 @@ async function contextInspectCommand(options) {
96728
96843
  init_canonical_loader();
96729
96844
  init_errors();
96730
96845
  import { mkdir as mkdir11 } from "fs/promises";
96731
- import { basename as basename12, join as join65 } from "path";
96846
+ import { basename as basename12, join as join66 } from "path";
96732
96847
  var _rulesCLIDeps = {
96733
96848
  readFile: async (path18) => Bun.file(path18).text(),
96734
96849
  writeFile: async (path18, content) => {
@@ -96737,7 +96852,7 @@ var _rulesCLIDeps = {
96737
96852
  fileExists: async (path18) => Bun.file(path18).exists(),
96738
96853
  globInDir: (dir) => {
96739
96854
  try {
96740
- return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join65(dir, f));
96855
+ return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join66(dir, f));
96741
96856
  } catch {
96742
96857
  return [];
96743
96858
  }
@@ -96786,7 +96901,7 @@ ${r.content}`).join(`
96786
96901
  `);
96787
96902
  const shimContent = `${header + body}
96788
96903
  `;
96789
- const shimPath = join65(workdir, shimFileName);
96904
+ const shimPath = join66(workdir, shimFileName);
96790
96905
  if (options.dryRun) {
96791
96906
  console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
96792
96907
  return;
@@ -96815,14 +96930,14 @@ function neutralizeContent(content) {
96815
96930
  }
96816
96931
  async function collectMigrationSources(workdir) {
96817
96932
  const sources = [];
96818
- const claudeMdPath = join65(workdir, "CLAUDE.md");
96933
+ const claudeMdPath = join66(workdir, "CLAUDE.md");
96819
96934
  if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
96820
96935
  const content = await _rulesCLIDeps.readFile(claudeMdPath);
96821
96936
  if (content.trim()) {
96822
96937
  sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
96823
96938
  }
96824
96939
  }
96825
- const rulesDir = join65(workdir, ".claude", "rules");
96940
+ const rulesDir = join66(workdir, ".claude", "rules");
96826
96941
  const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
96827
96942
  for (const filePath of ruleFiles) {
96828
96943
  try {
@@ -96842,7 +96957,7 @@ async function rulesMigrateCommand(options) {
96842
96957
  console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
96843
96958
  return;
96844
96959
  }
96845
- const targetDir = join65(workdir, CANONICAL_RULES_DIR);
96960
+ const targetDir = join66(workdir, CANONICAL_RULES_DIR);
96846
96961
  if (!options.dryRun) {
96847
96962
  try {
96848
96963
  await _rulesCLIDeps.mkdir(targetDir);
@@ -96853,7 +96968,7 @@ async function rulesMigrateCommand(options) {
96853
96968
  let written = 0;
96854
96969
  let skipped = 0;
96855
96970
  for (const { sourcePath, targetFileName, content } of sources) {
96856
- const targetPath = join65(targetDir, targetFileName);
96971
+ const targetPath = join66(targetDir, targetFileName);
96857
96972
  if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
96858
96973
  console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
96859
96974
  skipped++;
@@ -96892,7 +97007,7 @@ function collectCanonicalRuleRoots(workdir) {
96892
97007
  const packageRel = normalized.slice(0, idx);
96893
97008
  if (!packageRel)
96894
97009
  continue;
96895
- roots.add(join65(workdir, packageRel));
97010
+ roots.add(join66(workdir, packageRel));
96896
97011
  }
96897
97012
  return [...roots].sort();
96898
97013
  }
@@ -96914,7 +97029,7 @@ init_logger2();
96914
97029
  init_detect2();
96915
97030
  init_workspace();
96916
97031
  init_common();
96917
- import { join as join66 } from "path";
97032
+ import { join as join67 } from "path";
96918
97033
  function resolveEffective(detected, configPatterns) {
96919
97034
  if (configPatterns !== undefined)
96920
97035
  return "config";
@@ -96999,7 +97114,7 @@ async function detectCommand(options) {
96999
97114
  const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
97000
97115
  const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
97001
97116
  const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
97002
- const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
97117
+ const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
97003
97118
  const pkgRaw = await loadRawConfig(pkgConfigPath);
97004
97119
  const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
97005
97120
  const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
@@ -97053,13 +97168,13 @@ async function detectCommand(options) {
97053
97168
  if (rootDetected.confidence === "empty") {
97054
97169
  console.log(source_default.yellow(" root: skipped (empty detection)"));
97055
97170
  } else {
97056
- const rootConfigPath = join66(workdir, ".nax", "config.json");
97171
+ const rootConfigPath = join67(workdir, ".nax", "config.json");
97057
97172
  try {
97058
97173
  const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
97059
97174
  if (status === "skipped") {
97060
97175
  console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
97061
97176
  } else {
97062
- console.log(source_default.green(` root: ${status} \u2192 ${join66(".nax", "config.json")}`));
97177
+ console.log(source_default.green(` root: ${status} \u2192 ${join67(".nax", "config.json")}`));
97063
97178
  }
97064
97179
  } catch (err) {
97065
97180
  console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
@@ -97072,13 +97187,13 @@ async function detectCommand(options) {
97072
97187
  console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
97073
97188
  continue;
97074
97189
  }
97075
- const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
97190
+ const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
97076
97191
  try {
97077
97192
  const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
97078
97193
  if (status === "skipped") {
97079
97194
  console.log(source_default.dim(` ${dir}: skipped (already set)`));
97080
97195
  } else {
97081
- console.log(source_default.green(` ${dir}: ${status} \u2192 ${join66(".nax", "mono", dir, "config.json")}`));
97196
+ console.log(source_default.green(` ${dir}: ${status} \u2192 ${join67(".nax", "mono", dir, "config.json")}`));
97082
97197
  }
97083
97198
  } catch (err) {
97084
97199
  console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
@@ -97096,19 +97211,19 @@ async function detectCommand(options) {
97096
97211
  // src/commands/logs.ts
97097
97212
  init_common();
97098
97213
  import { existsSync as existsSync28 } from "fs";
97099
- import { join as join70 } from "path";
97214
+ import { join as join71 } from "path";
97100
97215
 
97101
97216
  // src/commands/logs-formatter.ts
97102
97217
  init_source();
97103
97218
  init_formatter();
97104
97219
  import { readdirSync as readdirSync7 } from "fs";
97105
- import { join as join69 } from "path";
97220
+ import { join as join70 } from "path";
97106
97221
 
97107
97222
  // src/commands/logs-reader.ts
97108
97223
  init_paths3();
97109
97224
  import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
97110
97225
  import { readdir as readdir4 } from "fs/promises";
97111
- import { join as join68 } from "path";
97226
+ import { join as join69 } from "path";
97112
97227
  var _logsReaderDeps = {
97113
97228
  getRunsDir
97114
97229
  };
@@ -97122,7 +97237,7 @@ async function resolveRunFileFromRegistry(runId) {
97122
97237
  }
97123
97238
  let matched = null;
97124
97239
  for (const entry of entries) {
97125
- const metaPath = join68(runsDir, entry, "meta.json");
97240
+ const metaPath = join69(runsDir, entry, "meta.json");
97126
97241
  try {
97127
97242
  const meta3 = await Bun.file(metaPath).json();
97128
97243
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -97144,14 +97259,14 @@ async function resolveRunFileFromRegistry(runId) {
97144
97259
  return null;
97145
97260
  }
97146
97261
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
97147
- return join68(matched.eventsDir, specificFile ?? files[0]);
97262
+ return join69(matched.eventsDir, specificFile ?? files[0]);
97148
97263
  }
97149
97264
  async function selectRunFile(runsDir) {
97150
97265
  const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97151
97266
  if (files.length === 0) {
97152
97267
  return null;
97153
97268
  }
97154
- return join68(runsDir, files[0]);
97269
+ return join69(runsDir, files[0]);
97155
97270
  }
97156
97271
  async function extractRunSummary(filePath) {
97157
97272
  const file3 = Bun.file(filePath);
@@ -97237,7 +97352,7 @@ Runs:
97237
97352
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
97238
97353
  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
97354
  for (const file3 of files) {
97240
- const filePath = join69(runsDir, file3);
97355
+ const filePath = join70(runsDir, file3);
97241
97356
  const summary = await extractRunSummary(filePath);
97242
97357
  const timestamp = file3.replace(".jsonl", "");
97243
97358
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -97351,7 +97466,7 @@ async function logsCommand(options) {
97351
97466
  return;
97352
97467
  }
97353
97468
  const resolved = resolveProject({ dir: options.dir });
97354
- const naxDir = join70(resolved.projectDir, ".nax");
97469
+ const naxDir = join71(resolved.projectDir, ".nax");
97355
97470
  const configPath = resolved.configPath;
97356
97471
  const configFile = Bun.file(configPath);
97357
97472
  const config2 = await configFile.json();
@@ -97359,8 +97474,8 @@ async function logsCommand(options) {
97359
97474
  if (!featureName) {
97360
97475
  throw new Error("No feature specified in config.json");
97361
97476
  }
97362
- const featureDir = join70(naxDir, "features", featureName);
97363
- const runsDir = join70(featureDir, "runs");
97477
+ const featureDir = join71(naxDir, "features", featureName);
97478
+ const runsDir = join71(featureDir, "runs");
97364
97479
  if (!existsSync28(runsDir)) {
97365
97480
  throw new Error(`No runs directory found for feature: ${featureName}`);
97366
97481
  }
@@ -97386,7 +97501,7 @@ init_prd();
97386
97501
  init_precheck();
97387
97502
  init_common();
97388
97503
  import { existsSync as existsSync29 } from "fs";
97389
- import { join as join71 } from "path";
97504
+ import { join as join72 } from "path";
97390
97505
  async function precheckCommand(options) {
97391
97506
  const resolved = resolveProject({
97392
97507
  dir: options.dir,
@@ -97408,9 +97523,9 @@ async function precheckCommand(options) {
97408
97523
  process.exit(1);
97409
97524
  }
97410
97525
  }
97411
- const naxDir = join71(resolved.projectDir, ".nax");
97412
- const featureDir = join71(naxDir, "features", featureName);
97413
- const prdPath = join71(featureDir, "prd.json");
97526
+ const naxDir = join72(resolved.projectDir, ".nax");
97527
+ const featureDir = join72(naxDir, "features", featureName);
97528
+ const prdPath = join72(featureDir, "prd.json");
97414
97529
  if (!existsSync29(featureDir)) {
97415
97530
  console.error(source_default.red(`Feature not found: ${featureName}`));
97416
97531
  process.exit(1);
@@ -97433,7 +97548,7 @@ async function precheckCommand(options) {
97433
97548
  init_source();
97434
97549
  init_paths3();
97435
97550
  import { readdir as readdir5 } from "fs/promises";
97436
- import { join as join72 } from "path";
97551
+ import { join as join73 } from "path";
97437
97552
  var DEFAULT_LIMIT = 20;
97438
97553
  var _runsCmdDeps = {
97439
97554
  getRunsDir
@@ -97488,7 +97603,7 @@ async function runsCommand(options = {}) {
97488
97603
  }
97489
97604
  const rows = [];
97490
97605
  for (const entry of entries) {
97491
- const metaPath = join72(runsDir, entry, "meta.json");
97606
+ const metaPath = join73(runsDir, entry, "meta.json");
97492
97607
  let meta3;
97493
97608
  try {
97494
97609
  meta3 = await Bun.file(metaPath).json();
@@ -97565,7 +97680,7 @@ async function runsCommand(options = {}) {
97565
97680
 
97566
97681
  // src/commands/unlock.ts
97567
97682
  init_source();
97568
- import { join as join73 } from "path";
97683
+ import { join as join74 } from "path";
97569
97684
  function isProcessAlive2(pid) {
97570
97685
  try {
97571
97686
  process.kill(pid, 0);
@@ -97580,7 +97695,7 @@ function formatLockAge(ageMs) {
97580
97695
  }
97581
97696
  async function unlockCommand(options) {
97582
97697
  const workdir = options.dir ?? process.cwd();
97583
- const lockPath = join73(workdir, "nax.lock");
97698
+ const lockPath = join74(workdir, "nax.lock");
97584
97699
  const lockFile = Bun.file(lockPath);
97585
97700
  const exists = await lockFile.exists();
97586
97701
  if (!exists) {
@@ -98128,6 +98243,7 @@ init_run_regression();
98128
98243
 
98129
98244
  // src/execution/index.ts
98130
98245
  init_story_orchestrator();
98246
+ init_story_orchestrator_logging();
98131
98247
  init_plan_inputs();
98132
98248
  init_build_plan_for_strategy();
98133
98249
  init_post_run();
@@ -106004,7 +106120,7 @@ Next: nax generate --package ${options.package}`));
106004
106120
  }
106005
106121
  return;
106006
106122
  }
106007
- const naxDir = join87(workdir, ".nax");
106123
+ const naxDir = join88(workdir, ".nax");
106008
106124
  if (existsSync35(naxDir) && !options.force) {
106009
106125
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
106010
106126
  return;
@@ -106033,11 +106149,11 @@ Next: nax generate --package ${options.package}`));
106033
106149
  }
106034
106150
  }
106035
106151
  }
106036
- mkdirSync7(join87(naxDir, "features"), { recursive: true });
106037
- mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
106152
+ mkdirSync7(join88(naxDir, "features"), { recursive: true });
106153
+ mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
106038
106154
  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({
106155
+ await Bun.write(join88(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106156
+ await Bun.write(join88(naxDir, "hooks.json"), JSON.stringify({
106041
106157
  hooks: {
106042
106158
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
106043
106159
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -106045,12 +106161,12 @@ Next: nax generate --package ${options.package}`));
106045
106161
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
106046
106162
  }
106047
106163
  }, null, 2));
106048
- await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
106164
+ await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
106049
106165
  *.tmp
106050
106166
  .paused.json
106051
106167
  .nax-verifier-verdict.json
106052
106168
  `);
106053
- await Bun.write(join87(naxDir, "context.md"), `# Project Context
106169
+ await Bun.write(join88(naxDir, "context.md"), `# Project Context
106054
106170
 
106055
106171
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
106056
106172
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -106198,8 +106314,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
106198
106314
  console.error(source_default.red("nax not initialized. Run: nax init"));
106199
106315
  process.exit(1);
106200
106316
  }
106201
- const featureDir = join87(naxDir, "features", options.feature);
106202
- const prdPath = join87(featureDir, "prd.json");
106317
+ const featureDir = join88(naxDir, "features", options.feature);
106318
+ const prdPath = join88(featureDir, "prd.json");
106203
106319
  if (options.plan && options.from) {
106204
106320
  if (existsSync35(prdPath) && !options.force) {
106205
106321
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
@@ -106221,10 +106337,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106221
106337
  }
106222
106338
  }
106223
106339
  try {
106224
- const planLogDir = join87(featureDir, "plan");
106340
+ const planLogDir = join88(featureDir, "plan");
106225
106341
  mkdirSync7(planLogDir, { recursive: true });
106226
106342
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106227
- const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
106343
+ const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
106228
106344
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106229
106345
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106230
106346
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -106270,10 +106386,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106270
106386
  resetLogger();
106271
106387
  const projectKey = config2.name?.trim() || basename16(workdir);
106272
106388
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
106273
- const runsDir = join87(outputDir, "features", options.feature, "runs");
106389
+ const runsDir = join88(outputDir, "features", options.feature, "runs");
106274
106390
  mkdirSync7(runsDir, { recursive: true });
106275
106391
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106276
- const logFilePath = join87(runsDir, `${runId}.jsonl`);
106392
+ const logFilePath = join88(runsDir, `${runId}.jsonl`);
106277
106393
  const isTTY = process.stdout.isTTY ?? false;
106278
106394
  const headlessFlag = options.headless ?? false;
106279
106395
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -106291,7 +106407,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106291
106407
  config2.agent.default = options.agent;
106292
106408
  }
106293
106409
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
106294
- const globalNaxDir = join87(homedir3(), ".nax");
106410
+ const globalNaxDir = join88(homedir3(), ".nax");
106295
106411
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
106296
106412
  const eventEmitter = new PipelineEventEmitter;
106297
106413
  const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
@@ -106311,12 +106427,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
106311
106427
  events: eventEmitter,
106312
106428
  ptyOptions: null,
106313
106429
  agentStreamEvents,
106314
- queueFilePath: join87(workdir, ".queue.txt")
106430
+ queueFilePath: join88(workdir, ".queue.txt")
106315
106431
  });
106316
106432
  } else {
106317
106433
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
106318
106434
  }
106319
- const statusFilePath = join87(outputDir, "status.json");
106435
+ const statusFilePath = join88(outputDir, "status.json");
106320
106436
  let parallel;
106321
106437
  if (options.parallel !== undefined) {
106322
106438
  parallel = Number.parseInt(options.parallel, 10);
@@ -106343,7 +106459,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106343
106459
  skipPrecheck: options.skipPrecheck ?? false,
106344
106460
  agentStreamEvents
106345
106461
  });
106346
- const latestSymlink = join87(runsDir, "latest.jsonl");
106462
+ const latestSymlink = join88(runsDir, "latest.jsonl");
106347
106463
  try {
106348
106464
  if (existsSync35(latestSymlink)) {
106349
106465
  Bun.spawnSync(["rm", latestSymlink]);
@@ -106404,9 +106520,9 @@ features.command("create <name>").description("Create a new feature").option("-d
106404
106520
  console.error(source_default.red("nax not initialized. Run: nax init"));
106405
106521
  process.exit(1);
106406
106522
  }
106407
- const featureDir = join87(naxDir, "features", name);
106523
+ const featureDir = join88(naxDir, "features", name);
106408
106524
  mkdirSync7(featureDir, { recursive: true });
106409
- await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
106525
+ await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
106410
106526
 
106411
106527
  ## Overview
106412
106528
 
@@ -106439,7 +106555,7 @@ features.command("create <name>").description("Create a new feature").option("-d
106439
106555
 
106440
106556
  <!-- What this feature explicitly does NOT cover. -->
106441
106557
  `);
106442
- await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
106558
+ await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
106443
106559
 
106444
106560
  Created: ${new Date().toISOString()}
106445
106561
 
@@ -106465,7 +106581,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106465
106581
  console.error(source_default.red("nax not initialized."));
106466
106582
  process.exit(1);
106467
106583
  }
106468
- const featuresDir = join87(naxDir, "features");
106584
+ const featuresDir = join88(naxDir, "features");
106469
106585
  if (!existsSync35(featuresDir)) {
106470
106586
  console.log(source_default.dim("No features yet."));
106471
106587
  return;
@@ -106480,7 +106596,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
106480
106596
  Features:
106481
106597
  `));
106482
106598
  for (const name of entries) {
106483
- const prdPath = join87(featuresDir, name, "prd.json");
106599
+ const prdPath = join88(featuresDir, name, "prd.json");
106484
106600
  if (existsSync35(prdPath)) {
106485
106601
  const prd = await loadPRD(prdPath);
106486
106602
  const c = countStories(prd);
@@ -106515,10 +106631,10 @@ Use: nax plan -f <feature> --from <spec>`));
106515
106631
  cliOverrides.profile = options.profile;
106516
106632
  }
106517
106633
  const config2 = await loadConfig(workdir, cliOverrides);
106518
- const featureLogDir = join87(naxDir, "features", options.feature, "plan");
106634
+ const featureLogDir = join88(naxDir, "features", options.feature, "plan");
106519
106635
  mkdirSync7(featureLogDir, { recursive: true });
106520
106636
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106521
- const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
106637
+ const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
106522
106638
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106523
106639
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106524
106640
  try {