@nathapp/nax 0.69.0 → 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 +1436 -566
  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()
@@ -17221,6 +17221,12 @@ var init_schemas_review = __esm(() => {
17221
17221
  substantiation: exports_external.object({
17222
17222
  requote: exports_external.boolean().default(true),
17223
17223
  maxRequotes: exports_external.number().int().min(0).default(5)
17224
+ }).optional(),
17225
+ nonBlockingFix: exports_external.object({
17226
+ enabled: exports_external.boolean().default(false),
17227
+ scope: exports_external.enum(["source", "both"]).default("both"),
17228
+ regressionAttempts: exports_external.number().int().min(0).default(1),
17229
+ verifierGuard: exports_external.boolean().default(true)
17224
17230
  }).optional()
17225
17231
  });
17226
17232
  ReviewConfigSchema = exports_external.object({
@@ -17407,7 +17413,7 @@ var init_schemas3 = __esm(() => {
17407
17413
  autoApproveVerifier: true,
17408
17414
  strategy: "auto",
17409
17415
  sessionTiers: {
17410
- testWriter: "balanced",
17416
+ testWriter: "fast",
17411
17417
  verifier: "fast"
17412
17418
  },
17413
17419
  testWriterAllowedPaths: ["src/index.ts", "src/**/index.ts"],
@@ -18940,6 +18946,14 @@ ${errors3.join(`
18940
18946
  }
18941
18947
  return result.data;
18942
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
+ }
18943
18957
  async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
18944
18958
  const logger = getLogger();
18945
18959
  const resolvedRootConfigPath = resolve3(rootConfigPath);
@@ -19004,6 +19018,106 @@ var init_loader = __esm(() => {
19004
19018
  init_schema();
19005
19019
  _rootConfigCache = new Map;
19006
19020
  });
19021
+
19022
+ // src/config/validate.ts
19023
+ function validateConfig(config2) {
19024
+ const errors3 = [];
19025
+ if (config2.version !== 1) {
19026
+ errors3.push(`Invalid version: expected 1, got ${config2.version}`);
19027
+ }
19028
+ const requiredTiers = ["fast", "balanced", "powerful"];
19029
+ if (!config2.models) {
19030
+ errors3.push("models mapping is required");
19031
+ } else {
19032
+ const defaultAgent = config2.agent?.default ?? "claude";
19033
+ const agentModels = config2.models[defaultAgent];
19034
+ if (!agentModels) {
19035
+ errors3.push(`models.${defaultAgent} is required (default agent has no model map)`);
19036
+ } else {
19037
+ for (const tier of requiredTiers) {
19038
+ const entry = agentModels[tier];
19039
+ if (!entry) {
19040
+ errors3.push(`models.${defaultAgent}.${tier} is required`);
19041
+ } else if (typeof entry === "string") {
19042
+ if (entry.trim() === "") {
19043
+ errors3.push(`models.${defaultAgent}.${tier} must be a non-empty model identifier`);
19044
+ }
19045
+ } else {
19046
+ if (!entry.provider || entry.provider.trim() === "") {
19047
+ errors3.push(`models.${defaultAgent}.${tier}.provider must be non-empty`);
19048
+ }
19049
+ if (!entry.model || entry.model.trim() === "") {
19050
+ errors3.push(`models.${defaultAgent}.${tier}.model must be non-empty`);
19051
+ }
19052
+ }
19053
+ }
19054
+ }
19055
+ }
19056
+ if (config2.execution.maxIterations <= 0) {
19057
+ errors3.push(`maxIterations must be > 0, got ${config2.execution.maxIterations}`);
19058
+ }
19059
+ if (config2.execution.costLimit <= 0) {
19060
+ errors3.push(`costLimit must be > 0, got ${config2.execution.costLimit}`);
19061
+ }
19062
+ if (config2.execution.sessionTimeoutSeconds <= 0) {
19063
+ errors3.push(`sessionTimeoutSeconds must be > 0, got ${config2.execution.sessionTimeoutSeconds}`);
19064
+ }
19065
+ const agentDefault = config2.agent?.default;
19066
+ if (!agentDefault || agentDefault.trim() === "") {
19067
+ errors3.push("agent.default must be non-empty");
19068
+ }
19069
+ if (!config2.autoMode.escalation.tierOrder || config2.autoMode.escalation.tierOrder.length === 0) {
19070
+ errors3.push("escalation.tierOrder must have at least one tier");
19071
+ } else {
19072
+ for (const tc of config2.autoMode.escalation.tierOrder) {
19073
+ if (tc.attempts < 1 || tc.attempts > 20) {
19074
+ errors3.push(`escalation.tierOrder: tier "${tc.tier}" attempts must be 1-20, got ${tc.attempts}`);
19075
+ }
19076
+ }
19077
+ }
19078
+ if (config2.models && config2.agent?.fallback?.map) {
19079
+ const modelKeys = Object.keys(config2.models);
19080
+ const fallbackAgents = new Set;
19081
+ for (const [primary, candidates] of Object.entries(config2.agent.fallback.map)) {
19082
+ fallbackAgents.add(primary);
19083
+ for (const c of candidates)
19084
+ fallbackAgents.add(c);
19085
+ }
19086
+ for (const agent of fallbackAgents) {
19087
+ if (!modelKeys.includes(agent)) {
19088
+ errors3.push(`agent.fallback.map: agent "${agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
19089
+ } else {
19090
+ for (const tier of requiredTiers) {
19091
+ if (!config2.models[agent]?.[tier]) {
19092
+ errors3.push(`models.${agent}.${tier} is required (fallback agent "${agent}" in agent.fallback.map)`);
19093
+ }
19094
+ }
19095
+ }
19096
+ }
19097
+ }
19098
+ if (config2.models && config2.autoMode?.escalation?.tierOrder) {
19099
+ const modelKeys = Object.keys(config2.models);
19100
+ for (const tc of config2.autoMode.escalation.tierOrder) {
19101
+ if (tc.agent !== undefined && !modelKeys.includes(tc.agent)) {
19102
+ errors3.push(`autoMode.escalation.tierOrder: tier "${tc.tier}" agent "${tc.agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
19103
+ }
19104
+ }
19105
+ }
19106
+ const defaultAgentKey = config2.agent?.default ?? "claude";
19107
+ const configuredTiers = Object.keys(config2.models[defaultAgentKey] ?? {});
19108
+ const complexities = ["simple", "medium", "complex", "expert"];
19109
+ for (const complexity of complexities) {
19110
+ const tier = config2.autoMode.complexityRouting[complexity];
19111
+ if (!configuredTiers.includes(tier)) {
19112
+ errors3.push(`complexityRouting.${complexity} must be one of: ${configuredTiers.join(", ")} (got '${tier}')`);
19113
+ }
19114
+ }
19115
+ return {
19116
+ valid: errors3.length === 0,
19117
+ errors: errors3
19118
+ };
19119
+ }
19120
+
19007
19121
  // src/config/selector.ts
19008
19122
  function pickSelector(name, ...keys) {
19009
19123
  return {
@@ -19286,10 +19400,81 @@ var init_test_strategy = __esm(() => {
19286
19400
  });
19287
19401
 
19288
19402
  // src/config/index.ts
19403
+ var exports_config = {};
19404
+ __export(exports_config, {
19405
+ verifyConfigSelector: () => verifyConfigSelector,
19406
+ validateFilePath: () => validateFilePath,
19407
+ validateDirectory: () => validateDirectory,
19408
+ validateConfig: () => validateConfig,
19409
+ testPatternConfigSelector: () => testPatternConfigSelector,
19410
+ tddConfigSelector: () => tddConfigSelector,
19411
+ routingConfigSelector: () => routingConfigSelector,
19412
+ reviewConfigSelector: () => reviewConfigSelector,
19413
+ resolveTestStrategy: () => resolveTestStrategy,
19414
+ resolveProfileName: () => resolveProfileName,
19415
+ resolveModelForAgent: () => resolveModelForAgent,
19416
+ resolveModel: () => resolveModel,
19417
+ resolveConfiguredModel: () => resolveConfiguredModel,
19418
+ reshapeSelector: () => reshapeSelector,
19419
+ rectifyConfigSelector: () => rectifyConfigSelector,
19420
+ rectificationGateConfigSelector: () => rectificationGateConfigSelector,
19421
+ qualityConfigSelector: () => qualityConfigSelector,
19422
+ promptLoaderConfigSelector: () => promptLoaderConfigSelector,
19423
+ projectConfigDir: () => projectConfigDir,
19424
+ precheckConfigSelector: () => precheckConfigSelector,
19425
+ planConfigSelector: () => planConfigSelector,
19426
+ pickSelector: () => pickSelector,
19427
+ mergePackageConfig: () => mergePackageConfig,
19428
+ loadProfileEnv: () => loadProfileEnv,
19429
+ loadProfile: () => loadProfile,
19430
+ loadPackageOverride: () => loadPackageOverride,
19431
+ loadConfigForWorkdir: () => loadConfigForWorkdir,
19432
+ loadConfig: () => loadConfig,
19433
+ llmRoutingConfigSelector: () => llmRoutingConfigSelector,
19434
+ listProfiles: () => listProfiles,
19435
+ isWithinDirectory: () => isWithinDirectory,
19436
+ isThreeSessionStrategy: () => isThreeSessionStrategy,
19437
+ isSingleSessionTestOwningStrategy: () => isSingleSessionTestOwningStrategy,
19438
+ interactionConfigSelector: () => interactionConfigSelector,
19439
+ globalConfigPath: () => globalConfigPath,
19440
+ globalConfigDir: () => globalConfigDir,
19441
+ getAcQualityRules: () => getAcQualityRules,
19442
+ findProjectDir: () => findProjectDir,
19443
+ executionGatesConfigSelector: () => executionGatesConfigSelector,
19444
+ deepMergeConfig: () => deepMergeConfig,
19445
+ decomposeConfigSelector: () => decomposeConfigSelector,
19446
+ debateConfigSelector: () => debateConfigSelector,
19447
+ createConfigLoader: () => createConfigLoader,
19448
+ contextToolRuntimeConfigSelector: () => contextToolRuntimeConfigSelector,
19449
+ autofixConfigSelector: () => autofixConfigSelector,
19450
+ agentManagerConfigSelector: () => agentManagerConfigSelector,
19451
+ acceptanceGenConfigSelector: () => acceptanceGenConfigSelector,
19452
+ acceptanceFixConfigSelector: () => acceptanceFixConfigSelector,
19453
+ acceptanceConfigSelector: () => acceptanceConfigSelector,
19454
+ VALID_TEST_STRATEGIES: () => VALID_TEST_STRATEGIES,
19455
+ TddConfigSchema: () => TddConfigSchema,
19456
+ THREE_SESSION_STRATEGIES: () => THREE_SESSION_STRATEGIES,
19457
+ TEST_STRATEGY_GUIDE: () => TEST_STRATEGY_GUIDE,
19458
+ SPEC_ANCHOR_RULES: () => SPEC_ANCHOR_RULES,
19459
+ SINGLE_SESSION_TEST_OWNING_STRATEGIES: () => SINGLE_SESSION_TEST_OWNING_STRATEGIES,
19460
+ PlanConfigSchema: () => PlanConfigSchema,
19461
+ NaxConfigSchema: () => NaxConfigSchema,
19462
+ ModelTierSchema: () => ModelTierSchema,
19463
+ MAX_DIRECTORY_DEPTH: () => MAX_DIRECTORY_DEPTH,
19464
+ GROUPING_RULES: () => GROUPING_RULES,
19465
+ DebateConfigSchema: () => DebateConfigSchema,
19466
+ DESCRIPTION_QUALITY_RULES: () => DESCRIPTION_QUALITY_RULES,
19467
+ DEFAULT_CONFIG: () => DEFAULT_CONFIG,
19468
+ ConfiguredModelSchema: () => ConfiguredModelSchema,
19469
+ COMPLEXITY_GUIDE: () => COMPLEXITY_GUIDE,
19470
+ AcceptanceConfigSchema: () => AcceptanceConfigSchema,
19471
+ AC_QUALITY_RULES: () => AC_QUALITY_RULES
19472
+ });
19289
19473
  var init_config = __esm(() => {
19290
19474
  init_schema();
19291
19475
  init_schemas_model();
19292
19476
  init_schemas_debate();
19477
+ init_schemas_execution();
19293
19478
  init_loader();
19294
19479
  init_path_security();
19295
19480
  init_paths();
@@ -23786,6 +23971,7 @@ var init_detect = __esm(() => {
23786
23971
  });
23787
23972
 
23788
23973
  // src/test-runners/detect/workspace.ts
23974
+ import { join as join6 } from "path";
23789
23975
  async function expandWorkspaceGlob(workdir, pattern) {
23790
23976
  const dirs = [];
23791
23977
  try {
@@ -23859,15 +24045,31 @@ async function detectTurboOrNx(workdir) {
23859
24045
  }
23860
24046
  async function detectNaxMonoLayout(workdir) {
23861
24047
  const dirs = [];
23862
- try {
23863
- const glob = new Bun.Glob(".nax/mono/*/config.json");
23864
- for await (const entry of glob.scan({ cwd: workdir })) {
23865
- const parts = entry.split("/");
23866
- if (parts.length >= 4) {
23867
- const pkgDir = parts.slice(2, -1).join("/");
23868
- 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;
23869
24062
  }
24063
+ if (isDirectory) {
24064
+ await walk(entryPath, [...relativeParts, entry]);
24065
+ }
24066
+ }
24067
+ if (hasConfig && relativeParts.length > 0) {
24068
+ dirs.push(relativeParts.join("/"));
23870
24069
  }
24070
+ }
24071
+ try {
24072
+ await walk(monoRoot, []);
23871
24073
  } catch {}
23872
24074
  return dirs;
23873
24075
  }
@@ -23911,7 +24113,8 @@ var init_workspace = __esm(() => {
23911
24113
  return f.text();
23912
24114
  },
23913
24115
  spawn: Bun.spawn,
23914
- 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()
23915
24118
  };
23916
24119
  _workspaceCache = new Map;
23917
24120
  });
@@ -23985,7 +24188,7 @@ var init_detector2 = __esm(() => {
23985
24188
  });
23986
24189
 
23987
24190
  // src/test-runners/resolver.ts
23988
- 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";
23989
24192
  function buildResolved(globs, resolution) {
23990
24193
  return {
23991
24194
  globs,
@@ -24037,7 +24240,7 @@ async function resolveTestFilePatterns(config2, workdir, packageDir, options) {
24037
24240
  validateGlobs(rootPatterns, "resolver");
24038
24241
  return buildResolved(rootPatterns, "root-config");
24039
24242
  }
24040
- const detectionWorkdir = packageDir ? join6(workdir, packageDir) : workdir;
24243
+ const detectionWorkdir = packageDir ? join7(workdir, packageDir) : workdir;
24041
24244
  const detected = await _resolverDeps.detectTestFilePatterns(detectionWorkdir);
24042
24245
  if (detected.confidence !== "empty" && detected.patterns.length > 0) {
24043
24246
  getSafeLogger()?.info("resolver", "Test patterns auto-detected", {
@@ -24616,7 +24819,7 @@ var init_git = __esm(() => {
24616
24819
  });
24617
24820
 
24618
24821
  // src/utils/path-filters.ts
24619
- import { join as join7, relative as relative2 } from "path";
24822
+ import { join as join8, relative as relative2 } from "path";
24620
24823
  function basename3(path) {
24621
24824
  const stripped = path.startsWith("./") ? path.slice(2) : path;
24622
24825
  const idx = stripped.lastIndexOf("/");
@@ -24706,8 +24909,8 @@ async function resolveNaxIgnorePatterns(repoRoot, packageDir) {
24706
24909
  const normalizedRepoRoot = normalizePath(repoRoot);
24707
24910
  const normalizedPackageDir = packageDir ? normalizePath(packageDir) : normalizedRepoRoot;
24708
24911
  const packagePrefix = normalizedPackageDir !== normalizedRepoRoot ? normalizePath(relative2(repoRoot, packageDir ?? repoRoot)) : null;
24709
- const rootFile = join7(repoRoot, NAX_IGNORE_FILENAME);
24710
- 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);
24711
24914
  const rootPatterns = await readIgnorePatterns(rootFile);
24712
24915
  const packagePatterns = packageDir && packageDir !== repoRoot ? await readIgnorePatterns(packageFile) : [];
24713
24916
  return [
@@ -24776,7 +24979,7 @@ var init_path_filters = __esm(() => {
24776
24979
  });
24777
24980
 
24778
24981
  // src/verification/smart-runner.ts
24779
- import { join as join8, relative as relative3 } from "path";
24982
+ import { join as join9, relative as relative3 } from "path";
24780
24983
  function clearGitRootCache() {
24781
24984
  _gitRootCache.clear();
24782
24985
  }
@@ -24894,7 +25097,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
24894
25097
  const lines = stdout.trim().split(`
24895
25098
  `).filter(Boolean);
24896
25099
  const effectiveRepoRoot = repoRoot ?? workdir;
24897
- const packageDir = packagePrefix ? join8(effectiveRepoRoot, packagePrefix) : undefined;
25100
+ const packageDir = packagePrefix ? join9(effectiveRepoRoot, packagePrefix) : undefined;
24898
25101
  const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(effectiveRepoRoot, packageDir);
24899
25102
  let effectivePrefix = packagePrefix;
24900
25103
  if (packagePrefix && repoRoot) {
@@ -24923,7 +25126,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
24923
25126
  return [];
24924
25127
  const lines = stdout.trim().split(`
24925
25128
  `).filter(Boolean);
24926
- const packageDir = packagePrefix ? join8(repoRoot, packagePrefix) : undefined;
25129
+ const packageDir = packagePrefix ? join9(repoRoot, packagePrefix) : undefined;
24927
25130
  const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
24928
25131
  const gitRoot = await getGitRootMemo(workdir);
24929
25132
  const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative3(gitRoot, repoRoot) : "";
@@ -24931,7 +25134,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
24931
25134
  const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
24932
25135
  const scoped = filterNaxInternalPaths(scopedRaw, ignoreMatchers);
24933
25136
  const stripped = extraPrefix ? scoped.map((f) => f.slice(`${extraPrefix}/`.length)) : scoped;
24934
- 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));
24935
25138
  } catch {
24936
25139
  return [];
24937
25140
  }
@@ -25063,6 +25266,7 @@ __export(exports_test_runners, {
25063
25266
  formatFailureSummary: () => formatFailureSummary,
25064
25267
  findPackageDir: () => findPackageDir,
25065
25268
  extractTestDirs: () => extractTestDirs,
25269
+ discoverWorkspacePackages: () => discoverWorkspacePackages,
25066
25270
  detectTestFilePatterns: () => detectTestFilePatterns,
25067
25271
  detectManifestFrameworksFromPackageJson: () => detectManifestFrameworksFromPackageJson,
25068
25272
  detectFramework: () => detectFramework,
@@ -25084,6 +25288,7 @@ var init_test_runners = __esm(() => {
25084
25288
  init_detector2();
25085
25289
  init_resolver();
25086
25290
  init_parser();
25291
+ init_workspace();
25087
25292
  init_ac_parser();
25088
25293
  init_scoped_selection();
25089
25294
  });
@@ -25135,7 +25340,7 @@ var init_project = __esm(() => {
25135
25340
 
25136
25341
  // src/utils/path-security.ts
25137
25342
  import { realpathSync as realpathSync2 } from "fs";
25138
- 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";
25139
25344
  function safeRealpathForComparison(p) {
25140
25345
  try {
25141
25346
  return realpathSync2(p);
@@ -25144,7 +25349,7 @@ function safeRealpathForComparison(p) {
25144
25349
  if (parent === p)
25145
25350
  return normalize3(p);
25146
25351
  const resolvedParent = safeRealpathForComparison(parent);
25147
- return join9(resolvedParent, p.split("/").pop() ?? "");
25352
+ return join10(resolvedParent, p.split("/").pop() ?? "");
25148
25353
  }
25149
25354
  }
25150
25355
  function isRelativeAndSafe(filePath) {
@@ -25172,7 +25377,7 @@ function validateModulePath(modulePath, allowedRoots) {
25172
25377
  } else {
25173
25378
  for (let i = 0;i < allowedRoots.length; i++) {
25174
25379
  const originalRoot = resolve5(allowedRoots[i]);
25175
- const absoluteInput = resolve5(join9(originalRoot, modulePath));
25380
+ const absoluteInput = resolve5(join10(originalRoot, modulePath));
25176
25381
  const resolved = safeRealpathForComparison(absoluteInput);
25177
25382
  const resolvedRoot = resolvedRoots[i];
25178
25383
  if (resolved.startsWith(`${resolvedRoot}/`) || resolved === resolvedRoot) {
@@ -25189,7 +25394,7 @@ var init_path_security2 = () => {};
25189
25394
 
25190
25395
  // src/context/engine/providers/code-neighbor.ts
25191
25396
  import { createHash as createHash3 } from "crypto";
25192
- 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";
25193
25398
  function isExcludedPath(file3, ignoreMatchers) {
25194
25399
  for (const prefix of EXCLUDED_DIR_PREFIXES2) {
25195
25400
  if (file3.startsWith(prefix) || file3.includes(`/${prefix}`))
@@ -25327,7 +25532,7 @@ async function readCached(absolutePath, cache) {
25327
25532
  async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, siblingTestContext) {
25328
25533
  const neighbors = new Set;
25329
25534
  let anyTruncated = false;
25330
- const ownAbsPath = join10(workdir, filePath);
25535
+ const ownAbsPath = join11(workdir, filePath);
25331
25536
  if (await _codeNeighborDeps.fileExists(ownAbsPath)) {
25332
25537
  const ownContent = await readCached(ownAbsPath, contentCache);
25333
25538
  if (ownContent !== null && ownContent.length > 0) {
@@ -25349,7 +25554,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
25349
25554
  break outer;
25350
25555
  if (srcFile === filePath)
25351
25556
  continue;
25352
- const content = await readCached(join10(scanWorkdir, srcFile), contentCache);
25557
+ const content = await readCached(join11(scanWorkdir, srcFile), contentCache);
25353
25558
  if (content?.includes(fileBaseName)) {
25354
25559
  for (const spec of parseImportSpecifiers(content)) {
25355
25560
  const resolved = resolveImport(spec, srcFile, scanWorkdir);
@@ -25365,7 +25570,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
25365
25570
  const candidates = deriveSiblingTestCandidates(filePath, siblingTestContext.globs);
25366
25571
  let chosen = null;
25367
25572
  for (const candidate of candidates) {
25368
- if (await _codeNeighborDeps.fileExists(join10(workdir, candidate))) {
25573
+ if (await _codeNeighborDeps.fileExists(join11(workdir, candidate))) {
25369
25574
  chosen = candidate;
25370
25575
  break;
25371
25576
  }
@@ -25389,7 +25594,7 @@ async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRo
25389
25594
  const relPkgDirs = await _codeNeighborDeps.discoverWorkspacePackages(repoRoot);
25390
25595
  if (relPkgDirs.length === 0)
25391
25596
  return [repoRoot];
25392
- return relPkgDirs.map((rel) => join10(repoRoot, rel)).filter((abs) => abs !== packageDir);
25597
+ return relPkgDirs.map((rel) => join11(repoRoot, rel)).filter((abs) => abs !== packageDir);
25393
25598
  } catch {
25394
25599
  return [repoRoot];
25395
25600
  }
@@ -26686,7 +26891,7 @@ var init_orchestrator = __esm(() => {
26686
26891
  });
26687
26892
 
26688
26893
  // src/context/rules/canonical-loader.ts
26689
- import { basename as basename4, join as join11 } from "path";
26894
+ import { basename as basename4, join as join12 } from "path";
26690
26895
  function parseRuleAllowMarker(line) {
26691
26896
  const allowed = new Set;
26692
26897
  RULE_ALLOW_MARKER.lastIndex = 0;
@@ -26821,7 +27026,7 @@ function applyCanonicalRulesBudget(rules, budgetTokens) {
26821
27026
  }
26822
27027
  async function loadCanonicalRules(workdir, options = {}) {
26823
27028
  const logger = _canonicalLoaderDeps.getLogger();
26824
- const rulesDir = join11(workdir, CANONICAL_RULES_DIR);
27029
+ const rulesDir = join12(workdir, CANONICAL_RULES_DIR);
26825
27030
  const allFilePaths = _canonicalLoaderDeps.globInDir(rulesDir);
26826
27031
  const filePaths = allFilePaths.filter((filePath) => {
26827
27032
  const normalized = filePath.replaceAll("\\", "/");
@@ -26922,7 +27127,7 @@ var init_canonical_loader = __esm(() => {
26922
27127
  for (const rel of files) {
26923
27128
  const depth = rel.split("/").length - 1;
26924
27129
  if (depth <= 1) {
26925
- kept.push(join11(dir, rel));
27130
+ kept.push(join12(dir, rel));
26926
27131
  } else {
26927
27132
  ignored.push(rel);
26928
27133
  }
@@ -27204,7 +27409,7 @@ var init_session_scratch = __esm(() => {
27204
27409
 
27205
27410
  // src/context/engine/providers/static-rules.ts
27206
27411
  import { createHash as createHash8 } from "crypto";
27207
- import { join as join12, relative as relative5 } from "path";
27412
+ import { join as join13, relative as relative5 } from "path";
27208
27413
  function contentHash85(content) {
27209
27414
  return createHash8("sha256").update(content).digest("hex").slice(0, 8);
27210
27415
  }
@@ -27412,7 +27617,7 @@ ${rule.content}`,
27412
27617
  const existingCandidates = [];
27413
27618
  for (const fileName of LEGACY_CANDIDATE_FILES) {
27414
27619
  try {
27415
- if (await _staticRulesDeps.fileExists(join12(rootDir, fileName))) {
27620
+ if (await _staticRulesDeps.fileExists(join13(rootDir, fileName))) {
27416
27621
  existingCandidates.push(fileName);
27417
27622
  }
27418
27623
  } catch {}
@@ -27428,11 +27633,11 @@ ${rule.content}`,
27428
27633
  for (const fileName of LEGACY_CANDIDATE_FILES) {
27429
27634
  legacySources.push({
27430
27635
  sourceId: fileName,
27431
- filePath: join12(rootDir, fileName),
27636
+ filePath: join13(rootDir, fileName),
27432
27637
  heading: fileName
27433
27638
  });
27434
27639
  }
27435
- const rulesDir = join12(rootDir, LEGACY_RULES_DIR);
27640
+ const rulesDir = join13(rootDir, LEGACY_RULES_DIR);
27436
27641
  const nestedRulePaths = _staticRulesDeps.globInDir(rulesDir);
27437
27642
  for (const filePath of nestedRulePaths) {
27438
27643
  const normalized = normalizePath2(filePath);
@@ -27491,7 +27696,7 @@ var init_static_rules = __esm(() => {
27491
27696
  fileExists: async (path) => Bun.file(path).exists(),
27492
27697
  globInDir: (dir) => {
27493
27698
  try {
27494
- 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));
27495
27700
  } catch {
27496
27701
  return [];
27497
27702
  }
@@ -27830,7 +28035,7 @@ function validateStory(raw, index, allIds) {
27830
28035
  routing: {
27831
28036
  complexity,
27832
28037
  testStrategy,
27833
- reasoning: "validated from LLM output",
28038
+ reasoning: typeof routing.reasoning === "string" && routing.reasoning.trim().length > 0 ? routing.reasoning.trim() : "validated from LLM output",
27834
28039
  ...noTestJustification !== undefined ? { noTestJustification } : {}
27835
28040
  },
27836
28041
  ...workdir !== undefined ? { workdir } : {},
@@ -28381,7 +28586,7 @@ var init_orchestrator_factory = __esm(() => {
28381
28586
  });
28382
28587
 
28383
28588
  // src/context/engine/providers/plugin-loader.ts
28384
- 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";
28385
28590
  function isInitialisable(p) {
28386
28591
  return typeof p.init === "function";
28387
28592
  }
@@ -28409,7 +28614,7 @@ function resolveModuleSpecifier(specifier, workdir) {
28409
28614
  }
28410
28615
  if (specifier.startsWith("./") || specifier.startsWith("../")) {
28411
28616
  const resolvedWorkdir = resolve7(workdir);
28412
- const resolved = resolve7(join13(workdir, specifier));
28617
+ const resolved = resolve7(join14(workdir, specifier));
28413
28618
  if (resolved !== resolvedWorkdir && !resolved.startsWith(`${resolvedWorkdir}/`)) {
28414
28619
  throw new Error(`Plugin module path escapes project workdir: "${specifier}" resolves to "${resolved}" (workdir: "${resolvedWorkdir}")`);
28415
28620
  }
@@ -28555,15 +28760,15 @@ var init_available_budget = __esm(() => {
28555
28760
 
28556
28761
  // src/context/engine/manifest-store.ts
28557
28762
  import { mkdir as mkdir2 } from "fs/promises";
28558
- 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";
28559
28764
  function contextStoryDir(projectDir, featureId, storyId) {
28560
- return join14(projectDir, ".nax", "features", featureId, "stories", storyId);
28765
+ return join15(projectDir, ".nax", "features", featureId, "stories", storyId);
28561
28766
  }
28562
28767
  function contextManifestPath(projectDir, featureId, storyId, stage) {
28563
- return join14(contextStoryDir(projectDir, featureId, storyId), `context-manifest-${stage}.json`);
28768
+ return join15(contextStoryDir(projectDir, featureId, storyId), `context-manifest-${stage}.json`);
28564
28769
  }
28565
28770
  function rebuildManifestPath(projectDir, featureId, storyId) {
28566
- return join14(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
28771
+ return join15(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
28567
28772
  }
28568
28773
  function toStoredPath(projectDir, pathValue) {
28569
28774
  const relativePath = isAbsolute6(pathValue) ? relative7(projectDir, pathValue) : pathValue;
@@ -28619,7 +28824,7 @@ async function loadContextManifests(projectDir, storyId, featureId) {
28619
28824
  const storyDir = contextStoryDir(projectDir, feature, storyId);
28620
28825
  const manifestFiles = await _manifestStoreDeps.listManifestFiles(storyDir);
28621
28826
  for (const fileName of manifestFiles) {
28622
- const fullPath = join14(storyDir, fileName);
28827
+ const fullPath = join15(storyDir, fileName);
28623
28828
  if (!await _manifestStoreDeps.fileExists(fullPath))
28624
28829
  continue;
28625
28830
  try {
@@ -28644,7 +28849,7 @@ var init_manifest_store = __esm(() => {
28644
28849
  fileExists: (path2) => Bun.file(path2).exists(),
28645
28850
  readFile: (path2) => Bun.file(path2).text(),
28646
28851
  listFeatureDirs: async (projectDir) => {
28647
- const baseDir = join14(projectDir, ".nax", "features");
28852
+ const baseDir = join15(projectDir, ".nax", "features");
28648
28853
  try {
28649
28854
  const dirs = [];
28650
28855
  for await (const entry of new Bun.Glob("*").scan({ cwd: baseDir, absolute: false })) {
@@ -28671,7 +28876,7 @@ var init_manifest_store = __esm(() => {
28671
28876
 
28672
28877
  // src/context/engine/stage-assembler.ts
28673
28878
  import { readdir } from "fs/promises";
28674
- 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";
28675
28880
  function dedupeScratchDirs(dirs) {
28676
28881
  return [...new Set(dirs.filter((dir) => Boolean(dir)))];
28677
28882
  }
@@ -28680,7 +28885,7 @@ function toAbsolutePath2(projectDir, pathValue) {
28680
28885
  }
28681
28886
  async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId, ttlMs) {
28682
28887
  const logger = getLogger();
28683
- const sessionsRoot = join15(projectDir, ".nax", "features", featureName, "sessions");
28888
+ const sessionsRoot = join16(projectDir, ".nax", "features", featureName, "sessions");
28684
28889
  let entries;
28685
28890
  try {
28686
28891
  entries = await _stageAssemblerDeps.readdir(sessionsRoot);
@@ -28690,7 +28895,7 @@ async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId
28690
28895
  const cutoff = _stageAssemblerDeps.now() - ttlMs;
28691
28896
  const found = [];
28692
28897
  for (const entry of entries) {
28693
- const descriptorPath = join15(sessionsRoot, entry, "descriptor.json");
28898
+ const descriptorPath = join16(sessionsRoot, entry, "descriptor.json");
28694
28899
  try {
28695
28900
  const parsed = await _stageAssemblerDeps.readDescriptor(descriptorPath);
28696
28901
  if (!parsed || parsed.storyId !== storyId || !parsed.scratchDir)
@@ -30355,13 +30560,13 @@ var exports_loader = {};
30355
30560
  __export(exports_loader, {
30356
30561
  loadOverride: () => loadOverride
30357
30562
  });
30358
- import { join as join16 } from "path";
30563
+ import { join as join17 } from "path";
30359
30564
  async function loadOverride(role, workdir, config2) {
30360
30565
  const overridePath = config2.prompts?.overrides?.[role];
30361
30566
  if (!overridePath) {
30362
30567
  return null;
30363
30568
  }
30364
- const absolutePath = join16(workdir, overridePath);
30569
+ const absolutePath = join17(workdir, overridePath);
30365
30570
  const file3 = Bun.file(absolutePath);
30366
30571
  if (!await file3.exists()) {
30367
30572
  return null;
@@ -32641,12 +32846,14 @@ var init_adversarial_review = __esm(() => {
32641
32846
  });
32642
32847
  const { accepted, dropped } = filterByAcQuote(substantiated, input.story.acceptanceCriteria);
32643
32848
  const blocking = accepted.filter((f) => isBlockingSeverity(f.severity, threshold));
32849
+ const advisory = accepted.filter((f) => !isBlockingSeverity(f.severity, threshold));
32644
32850
  const passed = parsed.passed && blocking.length === 0;
32645
32851
  return {
32646
32852
  ...parsed,
32647
32853
  passed,
32648
32854
  findings: accepted,
32649
32855
  normalizedFindings: toAdversarialReviewFindings(blocking),
32856
+ advisoryFindings: toAdversarialReviewFindings(advisory),
32650
32857
  acDropped: dropped
32651
32858
  };
32652
32859
  }
@@ -33026,11 +33233,11 @@ var init_prepare_inputs = __esm(() => {
33026
33233
  });
33027
33234
 
33028
33235
  // src/utils/nax-project-root.ts
33029
- 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";
33030
33237
  async function findNaxProjectRoot(startDir) {
33031
33238
  let dir = resolve10(startDir);
33032
33239
  for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
33033
- if (await _naxProjectRootDeps.exists(join17(dir, ".nax", "config.json"))) {
33240
+ if (await _naxProjectRootDeps.exists(join18(dir, ".nax", "config.json"))) {
33034
33241
  return dir;
33035
33242
  }
33036
33243
  const parent = dirname6(dir);
@@ -33051,7 +33258,7 @@ var init_nax_project_root = __esm(() => {
33051
33258
 
33052
33259
  // src/review/review-audit.ts
33053
33260
  import { mkdir as mkdir3 } from "fs/promises";
33054
- import { join as join18 } from "path";
33261
+ import { join as join19 } from "path";
33055
33262
  function auditKey(reviewer, storyId) {
33056
33263
  return `${reviewer}:${storyId ?? "_feature"}`;
33057
33264
  }
@@ -33084,15 +33291,15 @@ function toPersistedEntry(entry, epochMs) {
33084
33291
  async function persistReviewAudit(entry) {
33085
33292
  let resolvedDir;
33086
33293
  if (entry.outputDir) {
33087
- resolvedDir = join18(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
33294
+ resolvedDir = join19(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
33088
33295
  } else {
33089
33296
  const projectRoot = entry.projectDir ?? await _reviewAuditDeps.findNaxProjectRoot(entry.workdir);
33090
- resolvedDir = join18(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
33297
+ resolvedDir = join19(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
33091
33298
  }
33092
33299
  await _reviewAuditDeps.mkdir(resolvedDir);
33093
33300
  const epochMs = _reviewAuditDeps.now();
33094
33301
  const filename = `${epochMs}-${entry.sessionName}.json`;
33095
- await _reviewAuditDeps.writeFile(join18(resolvedDir, filename), toPersistedEntry(entry, epochMs));
33302
+ await _reviewAuditDeps.writeFile(join19(resolvedDir, filename), toPersistedEntry(entry, epochMs));
33096
33303
  }
33097
33304
  function createNoOpReviewAuditor() {
33098
33305
  return {
@@ -33693,6 +33900,17 @@ function createDrainDeadline(deadlineMs) {
33693
33900
  }
33694
33901
  async function runQualityCommand(opts) {
33695
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
+ }
33696
33914
  const startTime = Date.now();
33697
33915
  const logger = getSafeLogger();
33698
33916
  logger?.info("quality", `Running ${commandName}`, { storyId, commandName, command, workdir });
@@ -33807,7 +34025,7 @@ __export(exports_command_resolver, {
33807
34025
  resolveQualityTestCommands: () => resolveQualityTestCommands,
33808
34026
  _commandResolverDeps: () => _commandResolverDeps
33809
34027
  });
33810
- import { join as join19 } from "path";
34028
+ import { join as join20 } from "path";
33811
34029
  async function resolveQualityTestCommands(config2, workdir, storyWorkdir) {
33812
34030
  const rawTestCommand = config2.review?.commands?.test ?? config2.quality?.commands?.test;
33813
34031
  const rawScopedTemplate = config2.quality?.commands?.testScoped;
@@ -33834,7 +34052,7 @@ var init_command_resolver = __esm(() => {
33834
34052
  _commandResolverDeps = {
33835
34053
  readPackageName: async (dir) => {
33836
34054
  try {
33837
- const content = await Bun.file(join19(dir, "package.json")).json();
34055
+ const content = await Bun.file(join20(dir, "package.json")).json();
33838
34056
  return typeof content.name === "string" ? content.name : null;
33839
34057
  } catch {
33840
34058
  return null;
@@ -34283,7 +34501,7 @@ ${outputFormat}`, overridable: false }
34283
34501
  });
34284
34502
 
34285
34503
  // src/operations/plan-refine.ts
34286
- import { join as join20 } from "path";
34504
+ import { join as join21 } from "path";
34287
34505
  function hasToken(text, tokens) {
34288
34506
  const lower = text.toLowerCase();
34289
34507
  return tokens.some((token) => lower.includes(token));
@@ -34375,7 +34593,7 @@ async function normalizeStoryFiles(story, workdir, fileExists, upstreamProduced)
34375
34593
  for (const entry of contextFiles) {
34376
34594
  const filePath = typeof entry === "string" ? entry : entry.path;
34377
34595
  const factId = typeof entry === "string" ? undefined : entry.factId;
34378
- if (expected.has(filePath) || await fileExists(join20(workdir, filePath))) {
34596
+ if (expected.has(filePath) || await fileExists(join21(workdir, filePath))) {
34379
34597
  kept.push(entry);
34380
34598
  continue;
34381
34599
  }
@@ -35527,11 +35745,11 @@ function extractTestCode(output) {
35527
35745
 
35528
35746
  // src/acceptance/generator.ts
35529
35747
  import { existsSync as existsSync5 } from "fs";
35530
- import { join as join21 } from "path";
35748
+ import { join as join22 } from "path";
35531
35749
  function resolvePytestBin(packageDir) {
35532
35750
  if (packageDir) {
35533
35751
  for (const venvDir of [".venv", "venv", "env"]) {
35534
- const candidate = join21(packageDir, venvDir, "bin", "pytest");
35752
+ const candidate = join22(packageDir, venvDir, "bin", "pytest");
35535
35753
  if (existsSync5(candidate))
35536
35754
  return candidate;
35537
35755
  }
@@ -37117,6 +37335,7 @@ var init_write_test = __esm(() => {
37117
37335
  stage: "run",
37118
37336
  session: { role: "test-writer", lifetime: "warm" },
37119
37337
  config: tddConfigSelector,
37338
+ model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.testWriter,
37120
37339
  keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "test-writer"),
37121
37340
  build(input, _ctx) {
37122
37341
  if (input.promptMarkdown?.trim()) {
@@ -37177,6 +37396,7 @@ var init_implement = __esm(() => {
37177
37396
  stage: "run",
37178
37397
  session: { role: "implementer", lifetime: "warm" },
37179
37398
  config: tddConfigSelector,
37399
+ model: (input) => input.story.routing?.modelTier,
37180
37400
  keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "implementer"),
37181
37401
  build(input, _ctx) {
37182
37402
  if (input.promptMarkdown?.trim()) {
@@ -37574,6 +37794,7 @@ var init_verify = __esm(() => {
37574
37794
  stage: "verify",
37575
37795
  session: { role: "verifier", lifetime: "fresh" },
37576
37796
  config: tddConfigSelector,
37797
+ model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.verifier,
37577
37798
  retry: makeParseRetryStrategy({
37578
37799
  validate: (parsed) => {
37579
37800
  if (!parsed || typeof parsed !== "object")
@@ -38140,14 +38361,14 @@ var init_plan_critic_llm = __esm(() => {
38140
38361
 
38141
38362
  // src/context/greenfield.ts
38142
38363
  import { readdir as readdir2 } from "fs/promises";
38143
- import { join as join22 } from "path";
38364
+ import { join as join23 } from "path";
38144
38365
  async function scanForTestFiles(dir, testPatterns, isRootCall = true) {
38145
38366
  const results = [];
38146
38367
  const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
38147
38368
  try {
38148
38369
  const entries = await readdir2(dir, { withFileTypes: true });
38149
38370
  for (const entry of entries) {
38150
- const fullPath = join22(dir, entry.name);
38371
+ const fullPath = join23(dir, entry.name);
38151
38372
  if (entry.isDirectory()) {
38152
38373
  if (ignoreDirs.has(entry.name))
38153
38374
  continue;
@@ -38352,13 +38573,13 @@ __export(exports_runners, {
38352
38573
  _regressionRunnerDeps: () => _regressionRunnerDeps
38353
38574
  });
38354
38575
  import { existsSync as existsSync6 } from "fs";
38355
- import { join as join23 } from "path";
38576
+ import { join as join24 } from "path";
38356
38577
  async function verifyAssets(workingDirectory, expectedFiles) {
38357
38578
  if (!expectedFiles || expectedFiles.length === 0)
38358
38579
  return { success: true, missingFiles: [] };
38359
38580
  const missingFiles = [];
38360
38581
  for (const file3 of expectedFiles) {
38361
- if (!existsSync6(join23(workingDirectory, file3)))
38582
+ if (!existsSync6(join24(workingDirectory, file3)))
38362
38583
  missingFiles.push(file3);
38363
38584
  }
38364
38585
  if (missingFiles.length > 0) {
@@ -38484,7 +38705,7 @@ var init_full_suite_gate = __esm(() => {
38484
38705
  _fullSuiteGateDeps = {
38485
38706
  resolveGateContext: async (input, ctx) => {
38486
38707
  const { resolveQualityTestCommands: resolveQualityTestCommands2 } = await Promise.resolve().then(() => (init_command_resolver(), exports_command_resolver));
38487
- const config2 = ctx.runtime.configLoader.current();
38708
+ const config2 = ctx.packageView.config;
38488
38709
  const fullSuiteTimeout = config2.execution?.regressionGate?.timeoutSeconds ?? config2.execution?.rectification?.fullSuiteTimeoutSeconds ?? 300;
38489
38710
  const { testCommand: resolvedTestCmd } = await resolveQualityTestCommands2(config2, input.workdir, input.story.workdir);
38490
38711
  if (!resolvedTestCmd) {
@@ -38533,7 +38754,7 @@ var init_full_suite_gate = __esm(() => {
38533
38754
  config: fullSuiteGateConfigSelector,
38534
38755
  async execute(input, ctx, deps = _fullSuiteGateDeps) {
38535
38756
  const logger = getLogger();
38536
- const ctxConfig = ctx.config;
38757
+ const ctxConfig = ctx.packageView.config;
38537
38758
  const enabled = ctxConfig?.execution?.regressionGate?.enabled ?? true;
38538
38759
  if (!enabled) {
38539
38760
  logger.info("verify[regression]", "Regression gate disabled \u2014 skipping full-suite run", {
@@ -38804,7 +39025,7 @@ var init_apply_test_edit_declarations = __esm(() => {
38804
39025
  });
38805
39026
 
38806
39027
  // src/operations/validate-mock-structure-files.ts
38807
- import { join as join24 } from "path";
39028
+ import { join as join25 } from "path";
38808
39029
  async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
38809
39030
  const fileExists = deps?.fileExists ?? defaultFileExists;
38810
39031
  const valid = [];
@@ -38817,7 +39038,7 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
38817
39038
  const files = d.files ?? [d.file];
38818
39039
  let allValid = true;
38819
39040
  for (const file3 of files) {
38820
- const absolutePath = join24(packageDir, file3);
39041
+ const absolutePath = join25(packageDir, file3);
38821
39042
  const exists = await fileExists(absolutePath);
38822
39043
  if (!exists) {
38823
39044
  allValid = false;
@@ -38840,6 +39061,159 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
38840
39061
  var defaultFileExists = (p) => Bun.file(p).exists();
38841
39062
  var init_validate_mock_structure_files = () => {};
38842
39063
 
39064
+ // src/prompts/builders/setup-builder.ts
39065
+ function formatPackageFacts(pkg) {
39066
+ const lines = [` Package: ${pkg.relativeDir || "(root)"}`];
39067
+ if (pkg.testFramework)
39068
+ lines.push(` Test framework: ${pkg.testFramework}`);
39069
+ if (pkg.testFilePatterns.length > 0) {
39070
+ lines.push(` Test patterns: ${pkg.testFilePatterns.slice(0, 4).join(", ")}`);
39071
+ }
39072
+ if (pkg.missingScripts.length > 0) {
39073
+ lines.push(` Missing scripts: ${pkg.missingScripts.join(", ")}`);
39074
+ } else {
39075
+ lines.push(" Missing scripts: (none \u2014 all canonical scripts present)");
39076
+ }
39077
+ return lines.join(`
39078
+ `);
39079
+ }
39080
+
39081
+ class SetupPromptBuilder {
39082
+ build(analysis) {
39083
+ const isMonoRepo = analysis.shape === "mono";
39084
+ const packageFacts = analysis.packages.map(formatPackageFacts).join(`
39085
+
39086
+ `);
39087
+ return {
39088
+ role: {
39089
+ id: "setup-role",
39090
+ content: "You are an expert nax configuration generator. Generate a valid nax config JSON based on the repository analysis provided. Only reference scripts that actually exist \u2014 any script listed under 'Missing scripts' must NOT appear in quality.commands.",
39091
+ overridable: false
39092
+ },
39093
+ task: {
39094
+ id: "setup-task",
39095
+ content: [
39096
+ `Generate a nax configuration for this ${isMonoRepo ? "monorepo" : "single-package"} repository.`,
39097
+ "",
39098
+ "Repository facts:",
39099
+ `- Shape: ${analysis.shape}`,
39100
+ `- Package manager: ${analysis.pmRunPrefix}`,
39101
+ `- DLX runner: ${analysis.pmDlx}`,
39102
+ `- Orchestrator: ${analysis.orchestrator}`,
39103
+ "",
39104
+ "Per-package facts:",
39105
+ packageFacts,
39106
+ "",
39107
+ "IMPORTANT: A script listed under 'Missing scripts' does NOT exist in that package's package.json.",
39108
+ "Do NOT include commands for missing scripts in quality.commands.",
39109
+ "",
39110
+ isMonoRepo ? 'Respond with a JSON code block: { "config": <root NaxConfig>, "monoConfigs": [{ "relativeDir": "<pkg>", "config": <partial NaxConfig> }] }' : 'Respond with a JSON code block: { "config": <NaxConfig> }'
39111
+ ].join(`
39112
+ `),
39113
+ overridable: false
39114
+ }
39115
+ };
39116
+ }
39117
+ }
39118
+
39119
+ // src/operations/setup-generate.ts
39120
+ function throwSetupPlanError(message) {
39121
+ throw new NaxError(`[setup-generate] ${message}`, "SETUP_PLAN_INVALID");
39122
+ }
39123
+ function validateSetupOutput(parsed) {
39124
+ const config2 = parsed?.config ?? parsed;
39125
+ return NaxConfigSchema.safeParse(config2).success;
39126
+ }
39127
+ function crossCheckCommands(config2, analysis) {
39128
+ const rootPkg = analysis.packages.find((p) => p.relativeDir === "") ?? analysis.packages[0];
39129
+ const missing = new Set(rootPkg?.missingScripts ?? []);
39130
+ if (missing.size === 0)
39131
+ return { config: config2, gaps: [] };
39132
+ const quality = config2.quality;
39133
+ if (!quality?.commands)
39134
+ return { config: config2, gaps: [] };
39135
+ const gaps = [];
39136
+ const commands = {};
39137
+ for (const [key, value] of Object.entries(quality.commands)) {
39138
+ if (missing.has(key)) {
39139
+ gaps.push(`Script "${key}" in quality.commands.${key} is missing from package.json`);
39140
+ } else {
39141
+ commands[key] = value;
39142
+ }
39143
+ }
39144
+ if (gaps.length === 0)
39145
+ return { config: config2, gaps: [] };
39146
+ return { config: { ...config2, quality: { ...quality, commands } }, gaps };
39147
+ }
39148
+ function buildMonoConfigs(parsed, analysis) {
39149
+ if (analysis.shape !== "mono")
39150
+ return [];
39151
+ const rawMonoConfigs = parsed.monoConfigs ?? [];
39152
+ return analysis.packages.map((pkg) => {
39153
+ const rawMono = rawMonoConfigs.find((m) => m.relativeDir === pkg.relativeDir);
39154
+ const validated = rawMono ? NaxConfigSchema.safeParse(rawMono.config) : undefined;
39155
+ if (rawMono && !validated?.success) {
39156
+ getLogger().warn("setup-generate", "Per-package config failed schema validation \u2014 using empty config", {
39157
+ storyId: "setup",
39158
+ relativeDir: pkg.relativeDir
39159
+ });
39160
+ }
39161
+ const config2 = (validated?.success ? validated.data : undefined) ?? {};
39162
+ return { relativeDir: pkg.relativeDir, config: config2 };
39163
+ });
39164
+ }
39165
+ var MAX_SETUP_LLM_ATTEMPTS = 2, SetupPlanError, setupRetryStrategy, setupGenerateOp;
39166
+ var init_setup_generate = __esm(() => {
39167
+ init_retry();
39168
+ init_schemas3();
39169
+ init_errors();
39170
+ init_logger2();
39171
+ SetupPlanError = class SetupPlanError extends ParseValidationError {
39172
+ code = "SETUP_PLAN_INVALID";
39173
+ };
39174
+ setupRetryStrategy = makeParseRetryStrategy({
39175
+ reviewerKind: "setup-generate",
39176
+ maxAttempts: MAX_SETUP_LLM_ATTEMPTS,
39177
+ validate: validateSetupOutput,
39178
+ prompts: {
39179
+ invalid: () => "The response was not valid JSON or failed schema validation. Please respond with a valid JSON object.",
39180
+ truncated: () => "The response was truncated. Please provide the complete JSON config."
39181
+ },
39182
+ exhaustedFallback: () => {
39183
+ throwSetupPlanError("LLM failed to generate a valid setup plan after exhausting retries");
39184
+ }
39185
+ });
39186
+ setupGenerateOp = {
39187
+ kind: "run",
39188
+ name: "setup-generate",
39189
+ stage: "setup",
39190
+ session: { role: "setup", lifetime: "fresh" },
39191
+ noFallback: true,
39192
+ config: ["quality"],
39193
+ retry: setupRetryStrategy,
39194
+ build(analysis, _ctx) {
39195
+ return new SetupPromptBuilder().build(analysis);
39196
+ },
39197
+ parse(output, analysis, _ctx) {
39198
+ let parsedRaw;
39199
+ try {
39200
+ parsedRaw = parseLLMJson(output);
39201
+ } catch {
39202
+ throw new SetupPlanError("Failed to parse LLM output as JSON");
39203
+ }
39204
+ const parsedObj = parsedRaw;
39205
+ const rawConfig = parsedObj?.config ?? parsedRaw;
39206
+ const result = NaxConfigSchema.safeParse(rawConfig);
39207
+ if (!result.success) {
39208
+ throw new SetupPlanError(`Config failed NaxConfigSchema: ${result.error.message}`);
39209
+ }
39210
+ const { config: config2, gaps } = crossCheckCommands(result.data, analysis);
39211
+ const monoConfigs = buildMonoConfigs(parsedObj ?? { config: rawConfig }, analysis);
39212
+ return { config: config2, monoConfigs, gaps };
39213
+ }
39214
+ };
39215
+ });
39216
+
38843
39217
  // src/operations/declaration-sink.ts
38844
39218
  function makeDeclarationSink() {
38845
39219
  return { testEdits: [], mockHandoffs: [] };
@@ -38886,9 +39260,9 @@ var init_mechanical_lintfix_strategy = __esm(() => {
38886
39260
  stage: "rectification",
38887
39261
  config: qualityConfigSelector,
38888
39262
  async execute(input, ctx, deps = _mechanicalLintFixDeps) {
38889
- const ctxConfig = ctx.config;
38890
- const broad = ctxConfig?.quality?.commands?.lintFix;
38891
- 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;
38892
39266
  const command = buildCommand(broad, scoped2, input.scopeFiles);
38893
39267
  if (!command)
38894
39268
  return { applied: true, exitCode: 0 };
@@ -38897,7 +39271,7 @@ var init_mechanical_lintfix_strategy = __esm(() => {
38897
39271
  command,
38898
39272
  workdir: input.workdir,
38899
39273
  storyId: input.storyId,
38900
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39274
+ stripEnvVars: quality?.stripEnvVars ?? []
38901
39275
  });
38902
39276
  return { applied: true, exitCode: result.exitCode };
38903
39277
  }
@@ -38945,9 +39319,9 @@ var init_mechanical_formatfix_strategy = __esm(() => {
38945
39319
  stage: "rectification",
38946
39320
  config: qualityConfigSelector,
38947
39321
  async execute(input, ctx, deps = _mechanicalFormatFixDeps) {
38948
- const ctxConfig = ctx.config;
38949
- const broad = ctxConfig?.quality?.commands?.formatFix;
38950
- 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;
38951
39325
  const command = buildCommand2(broad, scoped2, input.scopeFiles);
38952
39326
  if (!command)
38953
39327
  return { applied: true, exitCode: 0 };
@@ -38956,7 +39330,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
38956
39330
  command,
38957
39331
  workdir: input.workdir,
38958
39332
  storyId: input.storyId,
38959
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39333
+ stripEnvVars: quality?.stripEnvVars ?? []
38960
39334
  });
38961
39335
  return { applied: true, exitCode: result.exitCode };
38962
39336
  }
@@ -38967,6 +39341,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
38967
39341
  var _lintCheckDeps, lintCheckOp;
38968
39342
  var init_lint_check = __esm(() => {
38969
39343
  init_config();
39344
+ init_logger2();
38970
39345
  init_runner();
38971
39346
  init_lint_parsing();
38972
39347
  _lintCheckDeps = {
@@ -38979,21 +39354,25 @@ var init_lint_check = __esm(() => {
38979
39354
  stage: "review",
38980
39355
  config: qualityConfigSelector,
38981
39356
  async execute(input, ctx, deps = _lintCheckDeps) {
38982
- const ctxConfig = ctx.config;
38983
- const command = ctxConfig?.quality?.commands?.lint;
38984
- if (ctxConfig !== undefined && !command) {
38985
- 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 };
38986
39365
  }
38987
39366
  const start = Date.now();
38988
39367
  const result = await deps.runQualityCommand({
38989
39368
  commandName: "lint",
38990
- command: command ?? "",
39369
+ command,
38991
39370
  workdir: input.workdir,
38992
39371
  storyId: input.storyId,
38993
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39372
+ stripEnvVars: quality?.stripEnvVars ?? []
38994
39373
  });
38995
39374
  if (result.exitCode === 0) {
38996
- return { success: true, findings: [], durationMs: Date.now() - start };
39375
+ return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
38997
39376
  }
38998
39377
  const parsed = deps.parseLintOutput(result.output, "auto", { workdir: input.workdir });
38999
39378
  return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
@@ -39217,6 +39596,7 @@ var init_typecheck_parsing = __esm(() => {
39217
39596
  var _typecheckCheckDeps, typecheckCheckOp;
39218
39597
  var init_typecheck_check = __esm(() => {
39219
39598
  init_config();
39599
+ init_logger2();
39220
39600
  init_runner();
39221
39601
  init_typecheck_parsing();
39222
39602
  _typecheckCheckDeps = {
@@ -39229,21 +39609,25 @@ var init_typecheck_check = __esm(() => {
39229
39609
  stage: "review",
39230
39610
  config: qualityConfigSelector,
39231
39611
  async execute(input, ctx, deps = _typecheckCheckDeps) {
39232
- const ctxConfig = ctx.config;
39233
- const command = ctxConfig?.quality?.commands?.typecheck;
39234
- if (ctxConfig !== undefined && !command) {
39235
- 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 };
39236
39620
  }
39237
39621
  const start = Date.now();
39238
39622
  const result = await deps.runQualityCommand({
39239
39623
  commandName: "typecheck",
39240
- command: command ?? "",
39624
+ command,
39241
39625
  workdir: input.workdir,
39242
39626
  storyId: input.storyId,
39243
- stripEnvVars: ctxConfig?.quality?.stripEnvVars ?? []
39627
+ stripEnvVars: quality?.stripEnvVars ?? []
39244
39628
  });
39245
39629
  if (result.exitCode === 0) {
39246
- return { success: true, findings: [], durationMs: Date.now() - start };
39630
+ return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39247
39631
  }
39248
39632
  const parsed = deps.parseTypecheckOutput(result.output, "auto", { workdir: input.workdir });
39249
39633
  return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
@@ -39272,12 +39656,16 @@ var init_verify_scoped = __esm(() => {
39272
39656
  config: qualityConfigSelector,
39273
39657
  async execute(input, ctx, deps = _verifyScopedDeps) {
39274
39658
  const logger = getLogger();
39275
- const ctxConfig = ctx.config;
39276
- const baseCommand = ctxConfig?.quality?.commands?.test;
39277
- 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
+ });
39278
39666
  return {
39279
39667
  success: true,
39280
- status: "passed",
39668
+ status: "skipped",
39281
39669
  findings: [],
39282
39670
  durationMs: 0,
39283
39671
  passCount: 0,
@@ -39289,11 +39677,11 @@ var init_verify_scoped = __esm(() => {
39289
39677
  workdir: input.workdir,
39290
39678
  storyId: input.storyId,
39291
39679
  storyGitRef: input.storyGitRef,
39292
- testCommand: baseCommand ?? "",
39293
- testScopedTemplate: ctxConfig.quality?.commands?.testScoped,
39294
- smartRunnerConfig: ctxConfig.execution?.smartTestRunner,
39295
- scopeTestThreshold: ctxConfig.quality?.scopeTestThreshold,
39296
- 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,
39297
39685
  naxIgnoreIndex: input.naxIgnoreIndex
39298
39686
  });
39299
39687
  if (selection.isFullSuite && regressionMode === "deferred" && !selection.isMonorepoOrchestrator && !selection.thresholdFallback) {
@@ -39320,7 +39708,7 @@ var init_verify_scoped = __esm(() => {
39320
39708
  command: selection.effectiveCommand
39321
39709
  });
39322
39710
  }
39323
- const scopedTimeout = ctxConfig.execution?.regressionGate?.timeoutSeconds ?? 600;
39711
+ const scopedTimeout = quality.execution?.regressionGate?.timeoutSeconds ?? 600;
39324
39712
  logger.info("verify[scoped]", "Running scoped tests", {
39325
39713
  storyId: input.storyId,
39326
39714
  packageDir: input.packageDir,
@@ -39333,14 +39721,14 @@ var init_verify_scoped = __esm(() => {
39333
39721
  const result = await deps.regression({
39334
39722
  workdir: input.workdir,
39335
39723
  command: selection.effectiveCommand,
39336
- timeoutSeconds: ctxConfig.execution?.regressionGate?.timeoutSeconds ?? 600,
39337
- forceExit: ctxConfig.quality?.forceExit,
39338
- detectOpenHandles: ctxConfig.quality?.detectOpenHandles,
39339
- detectOpenHandlesRetries: ctxConfig.quality?.detectOpenHandlesRetries,
39340
- gracePeriodMs: ctxConfig.quality?.gracePeriodMs,
39341
- drainTimeoutMs: ctxConfig.quality?.drainTimeoutMs,
39342
- shell: ctxConfig.quality?.shell,
39343
- 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
39344
39732
  });
39345
39733
  const durationMs = Date.now() - start;
39346
39734
  const parsed = result.output ? deps.parseTestOutput(result.output) : { passed: 0, failed: 0, failures: [] };
@@ -39441,6 +39829,7 @@ var init_operations = __esm(() => {
39441
39829
  init_autofix_test_writer_strategy();
39442
39830
  init_apply_test_edit_declarations();
39443
39831
  init_validate_mock_structure_files();
39832
+ init_setup_generate();
39444
39833
  init__finding_to_check();
39445
39834
  init_mechanical_lintfix_strategy();
39446
39835
  init_mechanical_formatfix_strategy();
@@ -40111,7 +40500,7 @@ var init_lint_parsing = __esm(() => {
40111
40500
  });
40112
40501
 
40113
40502
  // src/review/scoped-lint.ts
40114
- import { join as join25, relative as relative10 } from "path";
40503
+ import { join as join26, relative as relative10 } from "path";
40115
40504
  function shellQuotePath4(path5) {
40116
40505
  return `'${path5.replaceAll("'", "'\\''")}'`;
40117
40506
  }
@@ -40159,7 +40548,7 @@ function uniqueFiles(files) {
40159
40548
  async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
40160
40549
  const inScope = [];
40161
40550
  for (const relPath of files) {
40162
- const absPath = join25(workdir, relPath);
40551
+ const absPath = join26(workdir, relPath);
40163
40552
  const exists = await _scopedLintDeps.fileExists(absPath);
40164
40553
  if (!exists)
40165
40554
  continue;
@@ -43533,7 +43922,7 @@ var init_call = __esm(() => {
43533
43922
 
43534
43923
  // src/runtime/cost-aggregator.ts
43535
43924
  import { mkdirSync as mkdirSync2 } from "fs";
43536
- import { join as join26 } from "path";
43925
+ import { join as join27 } from "path";
43537
43926
  function makeCorrelationId() {
43538
43927
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
43539
43928
  }
@@ -43724,7 +44113,7 @@ class CostAggregator {
43724
44113
  if (events.length === 0 && errors3.length === 0)
43725
44114
  return;
43726
44115
  mkdirSync2(this._drainDir, { recursive: true });
43727
- const path5 = join26(this._drainDir, `${this._runId}.jsonl`);
44116
+ const path5 = join27(this._drainDir, `${this._runId}.jsonl`);
43728
44117
  const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
43729
44118
  await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
43730
44119
  `)}
@@ -43764,7 +44153,7 @@ var init_cost_aggregator = __esm(() => {
43764
44153
  // src/runtime/prompt-auditor.ts
43765
44154
  import { appendFileSync } from "fs";
43766
44155
  import { mkdir as mkdir4 } from "fs/promises";
43767
- import { join as join27 } from "path";
44156
+ import { join as join28 } from "path";
43768
44157
  function createNoOpPromptAuditor() {
43769
44158
  return {
43770
44159
  record() {},
@@ -43830,8 +44219,8 @@ class PromptAuditor {
43830
44219
  _jsonlPath;
43831
44220
  _featureDir;
43832
44221
  constructor(runId, flushDir, featureName) {
43833
- this._featureDir = join27(flushDir, featureName);
43834
- this._jsonlPath = join27(this._featureDir, `${runId}.jsonl`);
44222
+ this._featureDir = join28(flushDir, featureName);
44223
+ this._jsonlPath = join28(this._featureDir, `${runId}.jsonl`);
43835
44224
  }
43836
44225
  record(entry) {
43837
44226
  this._enqueue(entry);
@@ -43880,7 +44269,7 @@ class PromptAuditor {
43880
44269
  const auditEntry = entry;
43881
44270
  const filename = deriveTxtFilename(auditEntry);
43882
44271
  try {
43883
- await _promptAuditorDeps.write(join27(this._featureDir, filename), buildTxtContent(auditEntry));
44272
+ await _promptAuditorDeps.write(join28(this._featureDir, filename), buildTxtContent(auditEntry));
43884
44273
  } catch (err) {
43885
44274
  throw tagAuditError(err, "txt");
43886
44275
  }
@@ -44015,17 +44404,44 @@ function createPackageView(config2, packageDir, repoRoot) {
44015
44404
  }
44016
44405
  function createPackageRegistry(loader, repoRoot) {
44017
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
+ }
44018
44418
  function resolve12(packageDir) {
44019
- const key = packageDir ?? "";
44419
+ const key = toRelativeKey(packageDir);
44020
44420
  const cached2 = cache.get(key);
44021
44421
  if (cached2 !== undefined) {
44022
44422
  return cached2;
44023
44423
  }
44024
- const config2 = loader.current();
44424
+ const config2 = mergedConfigs.get(key) ?? loader.current();
44025
44425
  const view = createPackageView(config2, key, repoRoot);
44026
44426
  cache.set(key, view);
44027
44427
  return view;
44028
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
+ }
44029
44445
  return {
44030
44446
  all() {
44031
44447
  return [...cache.values()];
@@ -44033,9 +44449,13 @@ function createPackageRegistry(loader, repoRoot) {
44033
44449
  resolve: resolve12,
44034
44450
  repo() {
44035
44451
  return resolve12(undefined);
44036
- }
44452
+ },
44453
+ hydrate
44037
44454
  };
44038
44455
  }
44456
+ var init_packages = __esm(() => {
44457
+ init_config();
44458
+ });
44039
44459
 
44040
44460
  // src/runtime/agent-stream-events.ts
44041
44461
  class AgentStreamEventBus {
@@ -44655,7 +45075,8 @@ var init_session_role = __esm(() => {
44655
45075
  "fix-gen",
44656
45076
  "auto",
44657
45077
  "synthesis",
44658
- "judge"
45078
+ "judge",
45079
+ "setup"
44659
45080
  ];
44660
45081
  });
44661
45082
 
@@ -44992,7 +45413,7 @@ var init_pid_registry = __esm(() => {
44992
45413
  // src/session/manager-deps.ts
44993
45414
  import { randomUUID as randomUUID3 } from "crypto";
44994
45415
  import { mkdir as mkdir5 } from "fs/promises";
44995
- 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";
44996
45417
  function resolveProjectDirFromScratchDir(scratchDir) {
44997
45418
  const marker = `${sep2}.nax${sep2}features${sep2}`;
44998
45419
  const markerIdx = scratchDir.lastIndexOf(marker);
@@ -45013,7 +45434,7 @@ var init_manager_deps = __esm(() => {
45013
45434
  now: () => new Date().toISOString(),
45014
45435
  nowMs: () => Date.now(),
45015
45436
  uuid: () => randomUUID3(),
45016
- sessionScratchDir: (projectDir, featureName, sessionId) => join28(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45437
+ sessionScratchDir: (projectDir, featureName, sessionId) => join29(projectDir, ".nax", "features", featureName, "sessions", sessionId),
45017
45438
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
45018
45439
  await mkdir5(scratchDir, { recursive: true });
45019
45440
  const { handle: _handle, ...persistable } = descriptor;
@@ -45024,7 +45445,7 @@ var init_manager_deps = __esm(() => {
45024
45445
  persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
45025
45446
  }
45026
45447
  }
45027
- 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));
45028
45449
  }
45029
45450
  };
45030
45451
  });
@@ -45775,7 +46196,7 @@ __export(exports_runtime, {
45775
46196
  CostAggregator: () => CostAggregator,
45776
46197
  AgentStreamEventBus: () => AgentStreamEventBus
45777
46198
  });
45778
- import { basename as basename5, join as join29 } from "path";
46199
+ import { basename as basename5, join as join30 } from "path";
45779
46200
  function createRuntime(config2, workdir, opts) {
45780
46201
  const runId = crypto.randomUUID();
45781
46202
  const controller = new AbortController;
@@ -45791,10 +46212,10 @@ function createRuntime(config2, workdir, opts) {
45791
46212
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
45792
46213
  const globalDir = globalOutputDir();
45793
46214
  const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
45794
- const costDir = join29(outputDir, "cost");
46215
+ const costDir = join30(outputDir, "cost");
45795
46216
  const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
45796
46217
  const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
45797
- const auditDir = config2.agent?.promptAudit?.dir ?? join29(outputDir, "prompt-audit");
46218
+ const auditDir = config2.agent?.promptAudit?.dir ?? join30(outputDir, "prompt-audit");
45798
46219
  let promptAuditor;
45799
46220
  if (opts?.promptAuditor) {
45800
46221
  promptAuditor = opts.promptAuditor;
@@ -45900,6 +46321,7 @@ var init_runtime = __esm(() => {
45900
46321
  init_prompt_auditor();
45901
46322
  init_review_audit();
45902
46323
  init_paths2();
46324
+ init_packages();
45903
46325
  init_dispatch_events();
45904
46326
  init_agent_stream_events();
45905
46327
  init_middleware();
@@ -45917,6 +46339,7 @@ var init_runtime = __esm(() => {
45917
46339
  init_cost_aggregator();
45918
46340
  init_dispatch_events();
45919
46341
  init_middleware();
46342
+ init_packages();
45920
46343
  init_paths2();
45921
46344
  init_prompt_auditor();
45922
46345
  init_session_run_hop();
@@ -45945,9 +46368,9 @@ async function allSettledBounded(tasks, limit) {
45945
46368
 
45946
46369
  // src/context/injector.ts
45947
46370
  import { existsSync as existsSync8 } from "fs";
45948
- import { join as join30 } from "path";
46371
+ import { join as join31 } from "path";
45949
46372
  async function detectNode(workdir) {
45950
- const pkgPath = join30(workdir, "package.json");
46373
+ const pkgPath = join31(workdir, "package.json");
45951
46374
  if (!existsSync8(pkgPath))
45952
46375
  return null;
45953
46376
  try {
@@ -45964,7 +46387,7 @@ async function detectNode(workdir) {
45964
46387
  }
45965
46388
  }
45966
46389
  async function detectGo(workdir) {
45967
- const goMod = join30(workdir, "go.mod");
46390
+ const goMod = join31(workdir, "go.mod");
45968
46391
  if (!existsSync8(goMod))
45969
46392
  return null;
45970
46393
  try {
@@ -45988,7 +46411,7 @@ async function detectGo(workdir) {
45988
46411
  }
45989
46412
  }
45990
46413
  async function detectRust(workdir) {
45991
- const cargoPath = join30(workdir, "Cargo.toml");
46414
+ const cargoPath = join31(workdir, "Cargo.toml");
45992
46415
  if (!existsSync8(cargoPath))
45993
46416
  return null;
45994
46417
  try {
@@ -46004,8 +46427,8 @@ async function detectRust(workdir) {
46004
46427
  }
46005
46428
  }
46006
46429
  async function detectPython(workdir) {
46007
- const pyproject = join30(workdir, "pyproject.toml");
46008
- const requirements = join30(workdir, "requirements.txt");
46430
+ const pyproject = join31(workdir, "pyproject.toml");
46431
+ const requirements = join31(workdir, "requirements.txt");
46009
46432
  if (!existsSync8(pyproject) && !existsSync8(requirements))
46010
46433
  return null;
46011
46434
  try {
@@ -46024,7 +46447,7 @@ async function detectPython(workdir) {
46024
46447
  }
46025
46448
  }
46026
46449
  async function detectPhp(workdir) {
46027
- const composerPath = join30(workdir, "composer.json");
46450
+ const composerPath = join31(workdir, "composer.json");
46028
46451
  if (!existsSync8(composerPath))
46029
46452
  return null;
46030
46453
  try {
@@ -46037,7 +46460,7 @@ async function detectPhp(workdir) {
46037
46460
  }
46038
46461
  }
46039
46462
  async function detectRuby(workdir) {
46040
- const gemfile = join30(workdir, "Gemfile");
46463
+ const gemfile = join31(workdir, "Gemfile");
46041
46464
  if (!existsSync8(gemfile))
46042
46465
  return null;
46043
46466
  try {
@@ -46049,9 +46472,9 @@ async function detectRuby(workdir) {
46049
46472
  }
46050
46473
  }
46051
46474
  async function detectJvm(workdir) {
46052
- const pom = join30(workdir, "pom.xml");
46053
- const gradle = join30(workdir, "build.gradle");
46054
- 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");
46055
46478
  if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
46056
46479
  return null;
46057
46480
  try {
@@ -46059,7 +46482,7 @@ async function detectJvm(workdir) {
46059
46482
  const content2 = await Bun.file(pom).text();
46060
46483
  const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
46061
46484
  const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
46062
- const lang2 = existsSync8(join30(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46485
+ const lang2 = existsSync8(join31(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
46063
46486
  return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
46064
46487
  }
46065
46488
  const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
@@ -46313,7 +46736,7 @@ var init_windsurf = __esm(() => {
46313
46736
 
46314
46737
  // src/context/generator.ts
46315
46738
  import { existsSync as existsSync9 } from "fs";
46316
- import { join as join31, relative as relative12 } from "path";
46739
+ import { join as join32, relative as relative12 } from "path";
46317
46740
  async function loadContextContent(options, config2) {
46318
46741
  if (!_generatorDeps.existsSync(options.contextPath)) {
46319
46742
  throw new Error(`Context file not found: ${options.contextPath}`);
@@ -46331,7 +46754,7 @@ async function generateFor(agent, options, config2) {
46331
46754
  try {
46332
46755
  const context = await loadContextContent(options, config2);
46333
46756
  const content = generator.generate(context);
46334
- const outputPath = join31(options.outputDir, generator.outputFile);
46757
+ const outputPath = join32(options.outputDir, generator.outputFile);
46335
46758
  validateFilePath(outputPath, options.outputDir);
46336
46759
  if (!options.dryRun) {
46337
46760
  await _generatorDeps.writeFile(outputPath, content);
@@ -46349,7 +46772,7 @@ async function generateAll(options, config2, agentFilter) {
46349
46772
  for (const [agentKey, generator] of entries) {
46350
46773
  try {
46351
46774
  const content = generator.generate(context);
46352
- const outputPath = join31(options.outputDir, generator.outputFile);
46775
+ const outputPath = join32(options.outputDir, generator.outputFile);
46353
46776
  validateFilePath(outputPath, options.outputDir);
46354
46777
  if (!options.dryRun) {
46355
46778
  await _generatorDeps.writeFile(outputPath, content);
@@ -46369,7 +46792,7 @@ async function discoverPackages(repoRoot) {
46369
46792
  const glob = new Bun.Glob(pattern);
46370
46793
  for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
46371
46794
  const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
46372
- const pkgAbsolute = join31(repoRoot, pkgRelative);
46795
+ const pkgAbsolute = join32(repoRoot, pkgRelative);
46373
46796
  if (!seen.has(pkgAbsolute)) {
46374
46797
  seen.add(pkgAbsolute);
46375
46798
  packages.push(pkgAbsolute);
@@ -46401,14 +46824,14 @@ async function discoverWorkspacePackages2(repoRoot) {
46401
46824
  }
46402
46825
  }
46403
46826
  }
46404
- const turboPath = join31(repoRoot, "turbo.json");
46827
+ const turboPath = join32(repoRoot, "turbo.json");
46405
46828
  try {
46406
46829
  const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
46407
46830
  if (Array.isArray(turbo.packages)) {
46408
46831
  await resolveGlobs(turbo.packages);
46409
46832
  }
46410
46833
  } catch {}
46411
- const pkgPath = join31(repoRoot, "package.json");
46834
+ const pkgPath = join32(repoRoot, "package.json");
46412
46835
  try {
46413
46836
  const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
46414
46837
  const ws = pkg.workspaces;
@@ -46416,7 +46839,7 @@ async function discoverWorkspacePackages2(repoRoot) {
46416
46839
  if (patterns.length > 0)
46417
46840
  await resolveGlobs(patterns);
46418
46841
  } catch {}
46419
- const pnpmPath = join31(repoRoot, "pnpm-workspace.yaml");
46842
+ const pnpmPath = join32(repoRoot, "pnpm-workspace.yaml");
46420
46843
  try {
46421
46844
  const raw = await _generatorDeps.readTextFile(pnpmPath);
46422
46845
  const lines = raw.split(`
@@ -46442,7 +46865,7 @@ async function discoverWorkspacePackages2(repoRoot) {
46442
46865
  async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
46443
46866
  const resolvedRepoRoot = repoRoot ?? packageDir;
46444
46867
  const relativePkgPath = relative12(resolvedRepoRoot, packageDir);
46445
- const contextPath = join31(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
46868
+ const contextPath = join32(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
46446
46869
  if (!_generatorDeps.existsSync(contextPath)) {
46447
46870
  return [
46448
46871
  {
@@ -46510,7 +46933,7 @@ var init_generator2 = __esm(() => {
46510
46933
  });
46511
46934
 
46512
46935
  // src/analyze/scanner.ts
46513
- import { join as join32 } from "path";
46936
+ import { join as join33 } from "path";
46514
46937
  function resolveFrameworkAndRunner(language, pkg) {
46515
46938
  if (language === "go")
46516
46939
  return { framework: "", testRunner: "go-test" };
@@ -46532,7 +46955,7 @@ async function scanSourceRoots(workdir) {
46532
46955
  });
46533
46956
  try {
46534
46957
  const language = await deps.detectLanguage(workdir);
46535
- const pkg = await deps.readPackageJson(join32(workdir, "package.json"));
46958
+ const pkg = await deps.readPackageJson(join33(workdir, "package.json"));
46536
46959
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
46537
46960
  return [{ path: ".", language, framework, testRunner }];
46538
46961
  } catch {
@@ -46550,9 +46973,9 @@ async function scanSourceRoots(workdir) {
46550
46973
  packages = packages.slice(0, MAX_SOURCE_ROOTS);
46551
46974
  }
46552
46975
  return Promise.all(packages.map(async (pkgPath) => {
46553
- const pkgDir = pkgPath === "." ? workdir : join32(workdir, pkgPath);
46976
+ const pkgDir = pkgPath === "." ? workdir : join33(workdir, pkgPath);
46554
46977
  const language = await deps.detectLanguage(pkgDir);
46555
- const pkg = await deps.readPackageJson(join32(pkgDir, "package.json"));
46978
+ const pkg = await deps.readPackageJson(join33(pkgDir, "package.json"));
46556
46979
  const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
46557
46980
  return { path: pkgPath, language, framework, testRunner };
46558
46981
  }));
@@ -46585,7 +47008,7 @@ var init_analyze = __esm(() => {
46585
47008
  });
46586
47009
 
46587
47010
  // src/debate/pre-phase/grounder.ts
46588
- import { join as join33 } from "path";
47011
+ import { join as join34 } from "path";
46589
47012
  async function buildCodebaseContext(workdir) {
46590
47013
  const roots = await _grounderDeps.scanSourceRoots(workdir);
46591
47014
  return buildSourceRootsSection(normalizeRoots(workdir, roots));
@@ -46597,7 +47020,7 @@ function normalizeRoots(workdir, roots) {
46597
47020
  }));
46598
47021
  }
46599
47022
  async function writeManifestArtifact(ctx, manifest) {
46600
- 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");
46601
47024
  await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
46602
47025
  }
46603
47026
  var _grounderDeps, grounderStrategy = async (ctx) => {
@@ -46940,7 +47363,7 @@ function formatSpecDeltas(blockers, manifest) {
46940
47363
 
46941
47364
  // src/debate/verifiers/checks.ts
46942
47365
  import { existsSync as defaultExistsSync } from "fs";
46943
- import { join as join34 } from "path";
47366
+ import { join as join35 } from "path";
46944
47367
  function checkFilesExist(prd, workdir, deps) {
46945
47368
  const existsSync10 = deps?.existsSync ?? defaultExistsSync;
46946
47369
  const findings = [];
@@ -46950,7 +47373,7 @@ function checkFilesExist(prd, workdir, deps) {
46950
47373
  for (const entry of story.contextFiles) {
46951
47374
  const filePath = typeof entry === "string" ? entry : entry.path;
46952
47375
  const factId = typeof entry === "string" ? undefined : entry.factId;
46953
- const absPath = join34(workdir, filePath);
47376
+ const absPath = join35(workdir, filePath);
46954
47377
  if (existsSync10(absPath))
46955
47378
  continue;
46956
47379
  if (factId) {
@@ -47061,7 +47484,7 @@ var init_checks3 = () => {};
47061
47484
 
47062
47485
  // src/debate/verifiers/plan-checklist.ts
47063
47486
  import { existsSync as existsSync10 } from "fs";
47064
- import { join as join35 } from "path";
47487
+ import { join as join36 } from "path";
47065
47488
  function parsePrd(output) {
47066
47489
  if (!output)
47067
47490
  return null;
@@ -47072,7 +47495,7 @@ function parsePrd(output) {
47072
47495
  }
47073
47496
  }
47074
47497
  async function loadManifest(ctx) {
47075
- 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");
47076
47499
  const raw = await _planChecklistDeps.readFile(manifestPath);
47077
47500
  if (!raw)
47078
47501
  return null;
@@ -47085,7 +47508,7 @@ async function loadManifest(ctx) {
47085
47508
  }
47086
47509
  }
47087
47510
  async function emitSpecDeltas(ctx, blockers, manifest) {
47088
- 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");
47089
47512
  const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
47090
47513
  await _planChecklistDeps.write(artifactPath, content);
47091
47514
  return artifactPath;
@@ -47431,7 +47854,7 @@ var init_runner_plan_helpers = __esm(() => {
47431
47854
  });
47432
47855
 
47433
47856
  // src/debate/runner-plan.ts
47434
- import { join as join36 } from "path";
47857
+ import { join as join37 } from "path";
47435
47858
  async function runPlan(ctx, taskContext, outputFormat, opts) {
47436
47859
  const logger = _debateSessionDeps.getSafeLogger();
47437
47860
  const config2 = ctx.stageConfig;
@@ -47490,7 +47913,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
47490
47913
  sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
47491
47914
  proposers: ctx.stageConfig.proposers
47492
47915
  });
47493
- 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`));
47494
47917
  const successful = [];
47495
47918
  let rebuttalList;
47496
47919
  if (selectorKind === "verifier-pick") {
@@ -49733,9 +50156,9 @@ function validateFeatureName(feature) {
49733
50156
 
49734
50157
  // src/plan/critic.ts
49735
50158
  import { mkdir as mkdir6 } from "fs/promises";
49736
- import { dirname as dirname7, join as join39 } from "path";
50159
+ import { dirname as dirname7, join as join40 } from "path";
49737
50160
  async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
49738
- 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");
49739
50162
  await mkdir6(dirname7(path7), { recursive: true });
49740
50163
  await Bun.write(path7, formatSpecDeltas(findings, manifest));
49741
50164
  return path7;
@@ -50948,9 +51371,9 @@ __export(exports_plan_decompose, {
50948
51371
  runReplanLoop: () => runReplanLoop,
50949
51372
  planDecomposeCommand: () => planDecomposeCommand
50950
51373
  });
50951
- import { join as join40 } from "path";
51374
+ import { join as join41 } from "path";
50952
51375
  async function planDecomposeCommand(workdir, config2, options) {
50953
- const prdPath = join40(workdir, ".nax", "features", options.feature, "prd.json");
51376
+ const prdPath = join41(workdir, ".nax", "features", options.feature, "prd.json");
50954
51377
  if (!_planDeps.existsSync(prdPath)) {
50955
51378
  throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
50956
51379
  stage: "decompose",
@@ -51124,7 +51547,7 @@ var init_plan_decompose = __esm(() => {
51124
51547
 
51125
51548
  // src/cli/plan-runtime.ts
51126
51549
  import { existsSync as existsSync15 } from "fs";
51127
- import { join as join41 } from "path";
51550
+ import { join as join42 } from "path";
51128
51551
  function isRuntimeWithAgentManager(value) {
51129
51552
  return typeof value === "object" && value !== null && "agentManager" in value;
51130
51553
  }
@@ -51176,7 +51599,7 @@ var init_plan_runtime = __esm(() => {
51176
51599
  writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
51177
51600
  scanSourceRoots: (workdir) => scanSourceRoots(workdir),
51178
51601
  createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
51179
- readPackageJson: (workdir) => Bun.file(join41(workdir, "package.json")).json().catch(() => null),
51602
+ readPackageJson: (workdir) => Bun.file(join42(workdir, "package.json")).json().catch(() => null),
51180
51603
  spawnSync: (cmd, opts) => {
51181
51604
  const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
51182
51605
  return { stdout: result.stdout, exitCode: result.exitCode };
@@ -51561,7 +51984,7 @@ var init_metrics = __esm(() => {
51561
51984
 
51562
51985
  // src/commands/common.ts
51563
51986
  import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
51564
- import { join as join42, resolve as resolve13 } from "path";
51987
+ import { join as join43, resolve as resolve13 } from "path";
51565
51988
  function resolveProject(options = {}) {
51566
51989
  const { dir, feature } = options;
51567
51990
  let projectRoot;
@@ -51569,12 +51992,12 @@ function resolveProject(options = {}) {
51569
51992
  let configPath;
51570
51993
  if (dir) {
51571
51994
  projectRoot = realpathSync3(resolve13(dir));
51572
- naxDir = join42(projectRoot, ".nax");
51995
+ naxDir = join43(projectRoot, ".nax");
51573
51996
  if (!existsSync16(naxDir)) {
51574
51997
  throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
51575
51998
  Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
51576
51999
  }
51577
- configPath = join42(naxDir, "config.json");
52000
+ configPath = join43(naxDir, "config.json");
51578
52001
  if (!existsSync16(configPath)) {
51579
52002
  throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
51580
52003
  Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
@@ -51582,17 +52005,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
51582
52005
  } else {
51583
52006
  const found = findProjectRoot(process.cwd());
51584
52007
  if (!found) {
51585
- const cwdNaxDir = join42(process.cwd(), ".nax");
52008
+ const cwdNaxDir = join43(process.cwd(), ".nax");
51586
52009
  if (existsSync16(cwdNaxDir)) {
51587
- const cwdConfigPath = join42(cwdNaxDir, "config.json");
52010
+ const cwdConfigPath = join43(cwdNaxDir, "config.json");
51588
52011
  throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
51589
52012
  Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
51590
52013
  }
51591
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() });
51592
52015
  }
51593
52016
  projectRoot = found;
51594
- naxDir = join42(projectRoot, ".nax");
51595
- configPath = join42(naxDir, "config.json");
52017
+ naxDir = join43(projectRoot, ".nax");
52018
+ configPath = join43(naxDir, "config.json");
51596
52019
  }
51597
52020
  let featureDir;
51598
52021
  if (feature) {
@@ -51601,8 +52024,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
51601
52024
  } catch (error48) {
51602
52025
  throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
51603
52026
  }
51604
- const featuresDir = join42(naxDir, "features");
51605
- featureDir = join42(featuresDir, feature);
52027
+ const featuresDir = join43(naxDir, "features");
52028
+ featureDir = join43(featuresDir, feature);
51606
52029
  if (!existsSync16(featureDir)) {
51607
52030
  const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
51608
52031
  const availableMsg = availableFeatures.length > 0 ? `
@@ -51635,7 +52058,7 @@ async function resolveProjectAsync(options = {}) {
51635
52058
  }
51636
52059
  const isPlainName = !dir.includes("/") && !dir.includes("\\");
51637
52060
  if (isPlainName) {
51638
- const registryIdentityPath = join42(globalConfigDir(), dir, ".identity");
52061
+ const registryIdentityPath = join43(globalConfigDir(), dir, ".identity");
51639
52062
  const identityFile = Bun.file(registryIdentityPath);
51640
52063
  if (await identityFile.exists()) {
51641
52064
  try {
@@ -51667,12 +52090,12 @@ function findProjectRoot(startDir) {
51667
52090
  let current = resolve13(startDir);
51668
52091
  let depth = 0;
51669
52092
  while (depth < MAX_DIRECTORY_DEPTH) {
51670
- const naxDir = join42(current, ".nax");
51671
- const configPath = join42(naxDir, "config.json");
52093
+ const naxDir = join43(current, ".nax");
52094
+ const configPath = join43(naxDir, "config.json");
51672
52095
  if (existsSync16(configPath)) {
51673
52096
  return realpathSync3(current);
51674
52097
  }
51675
- const parent = join42(current, "..");
52098
+ const parent = join43(current, "..");
51676
52099
  if (parent === current) {
51677
52100
  break;
51678
52101
  }
@@ -52831,10 +53254,10 @@ var init_effectiveness = __esm(() => {
52831
53254
 
52832
53255
  // src/execution/progress.ts
52833
53256
  import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
52834
- import { join as join45 } from "path";
53257
+ import { join as join46 } from "path";
52835
53258
  async function appendProgress(featureDir, storyId, status, message) {
52836
53259
  await mkdir7(featureDir, { recursive: true });
52837
- const progressPath = join45(featureDir, "progress.txt");
53260
+ const progressPath = join46(featureDir, "progress.txt");
52838
53261
  const timestamp = new Date().toISOString();
52839
53262
  const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
52840
53263
  `;
@@ -53028,7 +53451,7 @@ var init_completion = __esm(() => {
53028
53451
 
53029
53452
  // src/constitution/loader.ts
53030
53453
  import { existsSync as existsSync19 } from "fs";
53031
- import { join as join46 } from "path";
53454
+ import { join as join47 } from "path";
53032
53455
  function truncateToTokens(text, maxTokens) {
53033
53456
  const maxChars = maxTokens * 3;
53034
53457
  if (text.length <= maxChars) {
@@ -53050,7 +53473,7 @@ async function loadConstitution(projectDir, config2) {
53050
53473
  }
53051
53474
  let combinedContent = "";
53052
53475
  if (!config2.skipGlobal) {
53053
- const globalPath = join46(globalConfigDir(), config2.path);
53476
+ const globalPath = join47(globalConfigDir(), config2.path);
53054
53477
  if (existsSync19(globalPath)) {
53055
53478
  const validatedPath = validateFilePath(globalPath, globalConfigDir());
53056
53479
  const globalFile = Bun.file(validatedPath);
@@ -53060,7 +53483,7 @@ async function loadConstitution(projectDir, config2) {
53060
53483
  }
53061
53484
  }
53062
53485
  }
53063
- const projectPath = join46(projectDir, config2.path);
53486
+ const projectPath = join47(projectDir, config2.path);
53064
53487
  if (existsSync19(projectPath)) {
53065
53488
  const validatedPath = validateFilePath(projectPath, projectDir);
53066
53489
  const projectFile = Bun.file(validatedPath);
@@ -53532,6 +53955,166 @@ var init_context2 = __esm(() => {
53532
53955
  };
53533
53956
  });
53534
53957
 
53958
+ // src/tdd/rollback.ts
53959
+ async function rollbackToRef(workdir, ref) {
53960
+ const logger = getLogger();
53961
+ logger.warn("tdd", "Rolling back git changes", { ref });
53962
+ const resetProc = _rollbackDeps.spawn(["git", "reset", "--hard", ref], {
53963
+ cwd: workdir,
53964
+ stdout: "pipe",
53965
+ stderr: "pipe"
53966
+ });
53967
+ const exitCode = await resetProc.exited;
53968
+ if (exitCode !== 0) {
53969
+ const stderr = await new Response(resetProc.stderr).text();
53970
+ logger.error("tdd", "Failed to rollback git changes", { ref, stderr });
53971
+ throw new Error(`Git rollback failed: ${stderr}`);
53972
+ }
53973
+ const cleanProc = _rollbackDeps.spawn(["git", "clean", "-fd"], {
53974
+ cwd: workdir,
53975
+ stdout: "pipe",
53976
+ stderr: "pipe"
53977
+ });
53978
+ const cleanExitCode = await cleanProc.exited;
53979
+ if (cleanExitCode !== 0) {
53980
+ const stderr = await new Response(cleanProc.stderr).text();
53981
+ logger.warn("tdd", "Failed to clean untracked files", { stderr });
53982
+ }
53983
+ logger.info("tdd", "Successfully rolled back git changes", { ref });
53984
+ }
53985
+ async function captureSnapshotRef(workdir, storyId) {
53986
+ await _rollbackDeps.autoCommitIfDirty(workdir, "non-blocking-fix-snapshot", "snapshot", storyId);
53987
+ const proc = _rollbackDeps.spawn(["git", "rev-parse", "HEAD"], { cwd: workdir, stdout: "pipe", stderr: "pipe" });
53988
+ const sha = (await new Response(proc.stdout).text()).trim();
53989
+ const exitCode = await proc.exited;
53990
+ if (exitCode !== 0) {
53991
+ throw new NaxError("git rev-parse HEAD failed in non-blocking-fix snapshot", "SNAPSHOT_REF_FAILED", {
53992
+ storyId,
53993
+ workdir,
53994
+ stage: "non-blocking-fix-snapshot"
53995
+ });
53996
+ }
53997
+ return sha;
53998
+ }
53999
+ var _rollbackDeps;
54000
+ var init_rollback = __esm(() => {
54001
+ init_errors();
54002
+ init_logger2();
54003
+ init_git();
54004
+ _rollbackDeps = {
54005
+ spawn: Bun.spawn,
54006
+ autoCommitIfDirty
54007
+ };
54008
+ });
54009
+
54010
+ // src/execution/non-blocking-fix.ts
54011
+ function shouldRunNonBlockingFix(cfg, advisoryCount) {
54012
+ return cfg?.enabled === true && advisoryCount > 0;
54013
+ }
54014
+ function nonBlockingExcludePhases() {
54015
+ return REVIEW_PHASE_KINDS;
54016
+ }
54017
+ function nonBlockingExtraPhases(cfg) {
54018
+ return cfg.scope === "both" && cfg.verifierGuard ? ["verifier"] : [];
54019
+ }
54020
+ async function runNonBlockingFix(args, _deps = DEFAULT_DEPS) {
54021
+ const logger = getSafeLogger();
54022
+ if (!shouldRunNonBlockingFix(args.cfg, args.advisoryFindings.length)) {
54023
+ return { ran: false, kept: false, restored: false };
54024
+ }
54025
+ const phaseOutputsSnapshot = { ...args.phaseOutputs };
54026
+ const restoreRef = await _deps.captureSnapshotRef(args.workdir, args.storyId);
54027
+ const maxAttempts = 1 + args.cfg.regressionAttempts;
54028
+ let exhausted = false;
54029
+ try {
54030
+ const result = await args.runRectify(maxAttempts);
54031
+ exhausted = result.rectificationExhausted === true;
54032
+ } catch (err) {
54033
+ logger?.warn("non-blocking-fix", "best-effort pass threw \u2014 restoring", {
54034
+ storyId: args.storyId,
54035
+ error: err instanceof Error ? err.message : String(err)
54036
+ });
54037
+ exhausted = true;
54038
+ }
54039
+ if (!exhausted) {
54040
+ logger?.info("non-blocking-fix", "best-effort fix kept", { storyId: args.storyId });
54041
+ return { ran: true, kept: true, restored: false };
54042
+ }
54043
+ await _deps.rollbackToRef(args.workdir, restoreRef);
54044
+ for (const key of Object.keys(args.phaseOutputs))
54045
+ delete args.phaseOutputs[key];
54046
+ Object.assign(args.phaseOutputs, phaseOutputsSnapshot);
54047
+ logger?.info("non-blocking-fix", "best-effort fix exhausted \u2014 restored to adversarial-passed", {
54048
+ storyId: args.storyId
54049
+ });
54050
+ return { ran: true, kept: false, restored: true };
54051
+ }
54052
+ var REVIEW_PHASE_KINDS, DEFAULT_DEPS;
54053
+ var init_non_blocking_fix = __esm(() => {
54054
+ init_logger2();
54055
+ init_rollback();
54056
+ REVIEW_PHASE_KINDS = ["semantic-review", "adversarial-review"];
54057
+ DEFAULT_DEPS = { captureSnapshotRef, rollbackToRef };
54058
+ });
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
+
53535
54118
  // src/execution/story-orchestrator.ts
53536
54119
  async function refreshReviewInputForDispatch(opName, input) {
53537
54120
  if (opName !== "semantic-review" && opName !== "adversarial-review")
@@ -53592,15 +54175,6 @@ async function refreshReviewInputForDispatch(opName, input) {
53592
54175
  return fallback;
53593
54176
  }
53594
54177
  }
53595
- function formatPhaseResultMessage(opName, success2, stage) {
53596
- if (opName === "greenfield-gate") {
53597
- 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";
53598
- }
53599
- if (stage === "rectification") {
53600
- return `Rectification strategy completed: ${opName}`;
53601
- }
53602
- return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
53603
- }
53604
54178
  function isSlot(value) {
53605
54179
  return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
53606
54180
  }
@@ -53796,46 +54370,6 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
53796
54370
  logger?.info("review", "Running adversarial check", { storyId });
53797
54371
  }
53798
54372
  }
53799
- function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
53800
- if (output === null || output === undefined || typeof output !== "object")
53801
- return null;
53802
- const r = output;
53803
- const success2 = r.success === true || r.passed === true;
53804
- const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
53805
- const status = typeof r.status === "string" ? r.status : undefined;
53806
- const data = { storyId, phase: opName, durationMs };
53807
- if (findingsCount !== undefined)
53808
- data.findingsCount = findingsCount;
53809
- if (status !== undefined)
53810
- data.status = status;
53811
- if (typeof r.failureCategory === "string")
53812
- data.failureCategory = r.failureCategory;
53813
- if (typeof r.reviewReason === "string")
53814
- data.reviewReason = r.reviewReason;
53815
- return { success: success2, data };
53816
- }
53817
- function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
53818
- if (isTddPhase)
53819
- return;
53820
- if (opName === "semantic-review" || opName === "adversarial-review")
53821
- return;
53822
- const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
53823
- if (!built)
53824
- return;
53825
- const { success: success2 } = built;
53826
- const data = { ...built.data, ...progressData };
53827
- const logger = getSafeLogger();
53828
- const message = formatPhaseResultMessage(opName, success2, stage);
53829
- if (stage === "rectification") {
53830
- logger?.info("story-orchestrator", message, data);
53831
- return;
53832
- }
53833
- if (success2) {
53834
- logger?.info("story-orchestrator", message, data);
53835
- } else {
53836
- logger?.warn("story-orchestrator", message, data);
53837
- }
53838
- }
53839
54373
  function logUnifiedReviewPhaseResult(storyId, opName, output) {
53840
54374
  const logger = getSafeLogger();
53841
54375
  const payload = toReviewDecisionPayload(opName, output);
@@ -53971,16 +54505,17 @@ function withIncreasingFailuresBail(strategies, enabled, consecutiveIncreases) {
53971
54505
  }
53972
54506
  }));
53973
54507
  }
53974
- async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
54508
+ async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides) {
53975
54509
  const rectification = state.rectification;
53976
- const validationPhases = collectRectificationPhases(state);
54510
+ const baseValidationPhases = collectRectificationPhases(state);
54511
+ const validationPhases = overrides?.excludePhaseKinds ? baseValidationPhases.filter((p) => !overrides.excludePhaseKinds?.includes(p.kind)) : baseValidationPhases;
53977
54512
  if (!rectification || validationPhases.length === 0) {
53978
54513
  return {};
53979
54514
  }
53980
54515
  if (ctx.runtime.signal?.aborted) {
53981
54516
  return {};
53982
54517
  }
53983
- const initialFindings = gatherRectificationFindings(phaseOutputs, validationPhases, state);
54518
+ const initialFindings = overrides?.initialFindings ? [...overrides.initialFindings] : gatherRectificationFindings(phaseOutputs, validationPhases, state);
53984
54519
  if (initialFindings.length === 0) {
53985
54520
  return {};
53986
54521
  }
@@ -53995,14 +54530,16 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53995
54530
  const cycle = {
53996
54531
  findings: [...initialFindings],
53997
54532
  iterations: [],
53998
- strategies: withIncreasingFailuresBail(rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
53999
- config: { maxAttemptsTotal: rectification.maxAttempts, validatorRetries: 1 },
54533
+ strategies: withIncreasingFailuresBail(overrides?.strategies ?? rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
54534
+ config: { maxAttemptsTotal: overrides?.maxAttempts ?? rectification.maxAttempts, validatorRetries: 1 },
54000
54535
  validate: async (_validateCtx, opts) => {
54001
54536
  if (ctx.runtime.signal?.aborted)
54002
54537
  return { findings: [], shortCircuited: false };
54003
54538
  const lite = (opts?.mode ?? "full") === "lite";
54004
54539
  const selected = phasesToRevalidate(opts?.strategiesRun, validationPhases);
54005
- const phases = lite ? orderGateLast(selected) : selected;
54540
+ const extra = overrides?.extraRevalidationKinds ? validationPhases.filter((p) => overrides.extraRevalidationKinds?.includes(p.kind) && !selected.some((s) => s.kind === p.kind)) : [];
54541
+ const selectedWithExtra = [...selected, ...extra];
54542
+ const phases = lite ? orderGateLast(selectedWithExtra) : selectedWithExtra;
54006
54543
  getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
54007
54544
  storyId: ctx.storyId,
54008
54545
  mode: opts?.mode ?? "full",
@@ -54154,6 +54691,25 @@ class ExecutionPlan {
54154
54691
  }
54155
54692
  }
54156
54693
  }
54694
+ const advCfg = this.state.adversarialReview ? this.state.nonBlockingFix : undefined;
54695
+ const advisoryOut = phaseOutputs["adversarial-review"];
54696
+ const advisoryFindings = advisoryOut?.advisoryFindings ?? [];
54697
+ if (advCfg && this.state.rectification && this.ctx.storyId && shouldRunNonBlockingFix(advCfg, advisoryFindings.length)) {
54698
+ await runNonBlockingFix({
54699
+ workdir: this.ctx.packageDir,
54700
+ storyId: this.ctx.storyId,
54701
+ advisoryFindings,
54702
+ cfg: advCfg,
54703
+ phaseOutputs,
54704
+ runRectify: (maxAttempts) => runRectification(this.ctx, this.state, phaseCosts, phaseOutputs, {
54705
+ initialFindings: advisoryFindings,
54706
+ strategies: this.state.nonBlockingFixStrategies ?? [],
54707
+ excludePhaseKinds: nonBlockingExcludePhases(),
54708
+ extraRevalidationKinds: nonBlockingExtraPhases(advCfg),
54709
+ maxAttempts
54710
+ })
54711
+ });
54712
+ }
54157
54713
  const verifierName = this.state.verifier?.slot.op.name;
54158
54714
  const gateName = this.state.fullSuiteGate?.slot.op.name;
54159
54715
  const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
@@ -54246,6 +54802,11 @@ class StoryOrchestratorBuilder {
54246
54802
  this.state.rectification = opts;
54247
54803
  return this;
54248
54804
  }
54805
+ addNonBlockingFix(cfg, strategies) {
54806
+ this.state.nonBlockingFix = cfg;
54807
+ this.state.nonBlockingFixStrategies = strategies;
54808
+ return this;
54809
+ }
54249
54810
  build(ctx, opts = {}) {
54250
54811
  if (!this.state.implementer) {
54251
54812
  throw new NaxError("StoryOrchestratorBuilder.build(): addImplementer() must be called before build()", "ORCHESTRATOR_NO_IMPLEMENTER", { stage: "execution" });
@@ -54263,6 +54824,8 @@ var init_story_orchestrator = __esm(() => {
54263
54824
  init_event_bus();
54264
54825
  init_prepare_inputs();
54265
54826
  init_git();
54827
+ init_non_blocking_fix();
54828
+ init_story_orchestrator_logging();
54266
54829
  _storyOrchestratorDeps = {
54267
54830
  callOp,
54268
54831
  runFixCycle,
@@ -54327,7 +54890,7 @@ var init_story_orchestrator = __esm(() => {
54327
54890
  });
54328
54891
 
54329
54892
  // src/execution/build-plan-for-strategy.ts
54330
- import { join as join47 } from "path";
54893
+ import { join as join48 } from "path";
54331
54894
  function requiresInitialRefCapture(strategy) {
54332
54895
  return isThreeSessionStrategy(strategy);
54333
54896
  }
@@ -54373,13 +54936,14 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
54373
54936
  }
54374
54937
  if (shouldRunRectification(config2) && inputs.rectification) {
54375
54938
  const sink = makeDeclarationSink();
54376
- const packageDir = join47(ctx.packageDir, story.workdir ?? "");
54939
+ const packageDir = join48(ctx.packageDir, story.workdir ?? "");
54377
54940
  const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
54378
54941
  const strategies = [];
54379
- 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) {
54380
54944
  strategies.push(makeMechanicalLintFixStrategy());
54381
54945
  }
54382
- if (config2.quality.commands.formatFix || config2.quality.commands.formatFixScoped) {
54946
+ if (pkgQuality?.commands?.formatFix || pkgQuality?.commands?.formatFixScoped) {
54383
54947
  strategies.push(makeMechanicalFormatFixStrategy());
54384
54948
  }
54385
54949
  if (inputs.fullSuiteGate && (isThreeSession || regressionMode === "per-story")) {
@@ -54415,6 +54979,22 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
54415
54979
  };
54416
54980
  builder.addRectification(rectOpts);
54417
54981
  }
54982
+ const nbf = config2.review?.adversarial?.nonBlockingFix;
54983
+ const nbStrategies = [];
54984
+ if (nbf?.enabled && inputs.adversarialReview) {
54985
+ const nbSink = makeDeclarationSink();
54986
+ if (nbf.scope === "source") {
54987
+ nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
54988
+ includeAdversarialReview: true
54989
+ }));
54990
+ } else {
54991
+ nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
54992
+ includeAdversarialReview: false
54993
+ }), makeAutofixTestWriterStrategy(story, config2, nbSink));
54994
+ }
54995
+ nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2));
54996
+ builder.addNonBlockingFix(nbf, nbStrategies);
54997
+ }
54418
54998
  return builder.build(ctx, { isThreeSession });
54419
54999
  }
54420
55000
  var init_build_plan_for_strategy = __esm(() => {
@@ -54678,41 +55258,6 @@ var init_execution_helpers = __esm(() => {
54678
55258
  init_errors();
54679
55259
  });
54680
55260
 
54681
- // src/tdd/rollback.ts
54682
- async function rollbackToRef(workdir, ref) {
54683
- const logger = getLogger();
54684
- logger.warn("tdd", "Rolling back git changes", { ref });
54685
- const resetProc = _rollbackDeps.spawn(["git", "reset", "--hard", ref], {
54686
- cwd: workdir,
54687
- stdout: "pipe",
54688
- stderr: "pipe"
54689
- });
54690
- const exitCode = await resetProc.exited;
54691
- if (exitCode !== 0) {
54692
- const stderr = await new Response(resetProc.stderr).text();
54693
- logger.error("tdd", "Failed to rollback git changes", { ref, stderr });
54694
- throw new Error(`Git rollback failed: ${stderr}`);
54695
- }
54696
- const cleanProc = _rollbackDeps.spawn(["git", "clean", "-fd"], {
54697
- cwd: workdir,
54698
- stdout: "pipe",
54699
- stderr: "pipe"
54700
- });
54701
- const cleanExitCode = await cleanProc.exited;
54702
- if (cleanExitCode !== 0) {
54703
- const stderr = await new Response(cleanProc.stderr).text();
54704
- logger.warn("tdd", "Failed to clean untracked files", { stderr });
54705
- }
54706
- logger.info("tdd", "Successfully rolled back git changes", { ref });
54707
- }
54708
- var _rollbackDeps;
54709
- var init_rollback = __esm(() => {
54710
- init_logger2();
54711
- _rollbackDeps = {
54712
- spawn: Bun.spawn
54713
- };
54714
- });
54715
-
54716
55261
  // src/execution/session-manager-runtime.ts
54717
55262
  async function closePhysicalSession(descriptor, agentGetFn, force) {
54718
55263
  if (!descriptor.handle)
@@ -55925,7 +56470,7 @@ function buildFrontmatter(story, ctx, role) {
55925
56470
  }
55926
56471
 
55927
56472
  // src/cli/prompts-tdd.ts
55928
- import { join as join48 } from "path";
56473
+ import { join as join49 } from "path";
55929
56474
  async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
55930
56475
  const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
55931
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(),
@@ -55944,7 +56489,7 @@ ${frontmatter}---
55944
56489
 
55945
56490
  ${session.prompt}`;
55946
56491
  if (outputDir) {
55947
- const promptFile = join48(outputDir, `${story.id}.${session.role}.md`);
56492
+ const promptFile = join49(outputDir, `${story.id}.${session.role}.md`);
55948
56493
  await Bun.write(promptFile, fullOutput);
55949
56494
  logger.info("cli", "Written TDD prompt file", {
55950
56495
  storyId: story.id,
@@ -55960,7 +56505,7 @@ ${"=".repeat(80)}`);
55960
56505
  }
55961
56506
  }
55962
56507
  if (outputDir && ctx.contextMarkdown) {
55963
- const contextFile = join48(outputDir, `${story.id}.context.md`);
56508
+ const contextFile = join49(outputDir, `${story.id}.context.md`);
55964
56509
  const frontmatter = buildFrontmatter(story, ctx);
55965
56510
  const contextOutput = `---
55966
56511
  ${frontmatter}---
@@ -55975,16 +56520,16 @@ var init_prompts_tdd = __esm(() => {
55975
56520
 
55976
56521
  // src/cli/prompts-main.ts
55977
56522
  import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
55978
- import { join as join49 } from "path";
56523
+ import { join as join50 } from "path";
55979
56524
  async function promptsCommand(options) {
55980
56525
  const logger = getLogger();
55981
56526
  const { feature, workdir, config: config2, storyId, outputDir } = options;
55982
- const naxDir = join49(workdir, ".nax");
56527
+ const naxDir = join50(workdir, ".nax");
55983
56528
  if (!existsSync20(naxDir)) {
55984
56529
  throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
55985
56530
  }
55986
- const featureDir = join49(naxDir, "features", feature);
55987
- const prdPath = join49(featureDir, "prd.json");
56531
+ const featureDir = join50(naxDir, "features", feature);
56532
+ const prdPath = join50(featureDir, "prd.json");
55988
56533
  if (!existsSync20(prdPath)) {
55989
56534
  throw new Error(`Feature "${feature}" not found or missing prd.json`);
55990
56535
  }
@@ -56051,10 +56596,10 @@ ${frontmatter}---
56051
56596
 
56052
56597
  ${ctx.prompt}`;
56053
56598
  if (outputDir) {
56054
- const promptFile = join49(outputDir, `${story.id}.prompt.md`);
56599
+ const promptFile = join50(outputDir, `${story.id}.prompt.md`);
56055
56600
  await Bun.write(promptFile, fullOutput);
56056
56601
  if (ctx.contextMarkdown) {
56057
- const contextFile = join49(outputDir, `${story.id}.context.md`);
56602
+ const contextFile = join50(outputDir, `${story.id}.context.md`);
56058
56603
  const contextOutput = `---
56059
56604
  ${frontmatter}---
56060
56605
 
@@ -56090,12 +56635,12 @@ var init_prompts_main = __esm(() => {
56090
56635
 
56091
56636
  // src/cli/prompts-init.ts
56092
56637
  import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
56093
- import { join as join50 } from "path";
56638
+ import { join as join51 } from "path";
56094
56639
  async function promptsInitCommand(options) {
56095
56640
  const { workdir, force = false, autoWireConfig = true } = options;
56096
- const templatesDir = join50(workdir, ".nax", "templates");
56641
+ const templatesDir = join51(workdir, ".nax", "templates");
56097
56642
  mkdirSync4(templatesDir, { recursive: true });
56098
- 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)));
56099
56644
  if (existingFiles.length > 0 && !force) {
56100
56645
  _promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
56101
56646
  Pass --force to overwrite existing templates.`);
@@ -56103,7 +56648,7 @@ async function promptsInitCommand(options) {
56103
56648
  }
56104
56649
  const written = [];
56105
56650
  for (const template of TEMPLATE_ROLES) {
56106
- const filePath = join50(templatesDir, template.file);
56651
+ const filePath = join51(templatesDir, template.file);
56107
56652
  const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
56108
56653
  const content = TEMPLATE_HEADER + roleBody;
56109
56654
  await Bun.write(filePath, content);
@@ -56119,7 +56664,7 @@ async function promptsInitCommand(options) {
56119
56664
  return written;
56120
56665
  }
56121
56666
  async function autoWirePromptsConfig(workdir) {
56122
- const configPath = join50(workdir, "nax.config.json");
56667
+ const configPath = join51(workdir, "nax.config.json");
56123
56668
  if (!existsSync21(configPath)) {
56124
56669
  const exampleConfig = JSON.stringify({
56125
56670
  prompts: {
@@ -56287,9 +56832,14 @@ __export(exports_init_context, {
56287
56832
  generateContextTemplate: () => generateContextTemplate,
56288
56833
  _initContextDeps: () => _initContextDeps
56289
56834
  });
56290
- import { existsSync as existsSync22 } from "fs";
56291
- import { mkdir as mkdir8 } from "fs/promises";
56292
- import { basename as basename9, join as join51 } from "path";
56835
+ import { basename as basename9, join as join52 } from "path";
56836
+ async function bunFileExists(path13) {
56837
+ return Bun.file(path13).exists();
56838
+ }
56839
+ async function bunMkdirp(path13) {
56840
+ const proc = Bun.spawn(["mkdir", "-p", path13]);
56841
+ await proc.exited;
56842
+ }
56293
56843
  async function findFiles(dir, maxFiles = 200) {
56294
56844
  try {
56295
56845
  const proc = Bun.spawnSync([
@@ -56317,8 +56867,8 @@ async function findFiles(dir, maxFiles = 200) {
56317
56867
  return [];
56318
56868
  }
56319
56869
  async function readPackageManifest(projectRoot) {
56320
- const packageJsonPath = join51(projectRoot, "package.json");
56321
- if (!existsSync22(packageJsonPath)) {
56870
+ const packageJsonPath = join52(projectRoot, "package.json");
56871
+ if (!await bunFileExists(packageJsonPath)) {
56322
56872
  return null;
56323
56873
  }
56324
56874
  try {
@@ -56335,8 +56885,8 @@ async function readPackageManifest(projectRoot) {
56335
56885
  }
56336
56886
  }
56337
56887
  async function readReadmeSnippet(projectRoot) {
56338
- const readmePath = join51(projectRoot, "README.md");
56339
- if (!existsSync22(readmePath)) {
56888
+ const readmePath = join52(projectRoot, "README.md");
56889
+ if (!await bunFileExists(readmePath)) {
56340
56890
  return null;
56341
56891
  }
56342
56892
  try {
@@ -56353,8 +56903,8 @@ async function detectEntryPoints(projectRoot) {
56353
56903
  const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
56354
56904
  const found = [];
56355
56905
  for (const candidate of candidates) {
56356
- const path13 = join51(projectRoot, candidate);
56357
- if (existsSync22(path13)) {
56906
+ const path13 = join52(projectRoot, candidate);
56907
+ if (await bunFileExists(path13)) {
56358
56908
  found.push(candidate);
56359
56909
  }
56360
56910
  }
@@ -56364,8 +56914,8 @@ async function detectConfigFiles(projectRoot) {
56364
56914
  const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
56365
56915
  const found = [];
56366
56916
  for (const candidate of candidates) {
56367
- const path13 = join51(projectRoot, candidate);
56368
- if (existsSync22(path13)) {
56917
+ const path13 = join52(projectRoot, candidate);
56918
+ if (await bunFileExists(path13)) {
56369
56919
  found.push(candidate);
56370
56920
  }
56371
56921
  }
@@ -56495,10 +57045,10 @@ Keep it under 2000 tokens. Use markdown formatting. Be specific to the detected
56495
57045
  `;
56496
57046
  try {
56497
57047
  const result = await _initContextDeps.callLLM(prompt);
56498
- logger.info("init", "Generated context.md with LLM");
57048
+ logger.info("init", "Generated context.md with LLM", { storyId: "init-context" });
56499
57049
  return result;
56500
57050
  } catch (err) {
56501
- logger.warn("init", `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}`);
57051
+ logger.warn("init", `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}`, { storyId: "init-context" });
56502
57052
  return generateContextTemplate(scan);
56503
57053
  }
56504
57054
  }
@@ -56525,29 +57075,35 @@ function generatePackageContextTemplate(packagePath) {
56525
57075
  }
56526
57076
  async function initPackage(repoRoot, packagePath, force = false) {
56527
57077
  const logger = getLogger();
56528
- const naxDir = join51(repoRoot, ".nax", "mono", packagePath);
56529
- const contextPath = join51(naxDir, "context.md");
56530
- if (existsSync22(contextPath) && !force) {
56531
- logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
57078
+ const naxDir = join52(repoRoot, ".nax", "mono", packagePath);
57079
+ const contextPath = join52(naxDir, "context.md");
57080
+ if (await bunFileExists(contextPath) && !force) {
57081
+ logger.info("init", "Package context.md already exists (use --force to overwrite)", {
57082
+ storyId: "init-context",
57083
+ path: contextPath
57084
+ });
56532
57085
  return;
56533
57086
  }
56534
- if (!existsSync22(naxDir)) {
56535
- await mkdir8(naxDir, { recursive: true });
57087
+ if (!await bunFileExists(naxDir)) {
57088
+ await bunMkdirp(naxDir);
56536
57089
  }
56537
57090
  const content = generatePackageContextTemplate(packagePath);
56538
57091
  await Bun.write(contextPath, content);
56539
- logger.info("init", "Created package context.md", { path: contextPath });
57092
+ logger.info("init", "Created package context.md", { storyId: "init-context", path: contextPath });
56540
57093
  }
56541
57094
  async function initContext(projectRoot, options = {}) {
56542
57095
  const logger = getLogger();
56543
- const naxDir = join51(projectRoot, ".nax");
56544
- const contextPath = join51(naxDir, "context.md");
56545
- if (existsSync22(contextPath) && !options.force) {
56546
- logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
57096
+ const naxDir = join52(projectRoot, ".nax");
57097
+ const contextPath = join52(naxDir, "context.md");
57098
+ if (await bunFileExists(contextPath) && !options.force) {
57099
+ logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
57100
+ storyId: "init-context",
57101
+ path: contextPath
57102
+ });
56547
57103
  return;
56548
57104
  }
56549
- if (!existsSync22(naxDir)) {
56550
- await mkdir8(naxDir, { recursive: true });
57105
+ if (!await bunFileExists(naxDir)) {
57106
+ await bunMkdirp(naxDir);
56551
57107
  }
56552
57108
  const scan = await scanProject(projectRoot);
56553
57109
  let content;
@@ -56557,7 +57113,10 @@ async function initContext(projectRoot, options = {}) {
56557
57113
  content = generateContextTemplate(scan);
56558
57114
  }
56559
57115
  await Bun.write(contextPath, content);
56560
- logger.info("init", "Generated .nax/context.md template from project scan", { path: contextPath });
57116
+ logger.info("init", "Generated .nax/context.md template from project scan", {
57117
+ storyId: "init-context",
57118
+ path: contextPath
57119
+ });
56561
57120
  }
56562
57121
  var _initContextDeps;
56563
57122
  var init_init_context = __esm(() => {
@@ -56570,11 +57129,11 @@ var init_init_context = __esm(() => {
56570
57129
  });
56571
57130
 
56572
57131
  // src/cli/init-detect.ts
56573
- import { existsSync as existsSync23, readFileSync } from "fs";
56574
- import { join as join52 } from "path";
57132
+ import { existsSync as existsSync22, readFileSync } from "fs";
57133
+ import { join as join53 } from "path";
56575
57134
  function readPackageJson(projectRoot) {
56576
- const pkgPath = join52(projectRoot, "package.json");
56577
- if (!existsSync23(pkgPath))
57135
+ const pkgPath = join53(projectRoot, "package.json");
57136
+ if (!existsSync22(pkgPath))
56578
57137
  return;
56579
57138
  try {
56580
57139
  return JSON.parse(readFileSync(pkgPath, "utf-8"));
@@ -56616,41 +57175,41 @@ function detectStack(projectRoot) {
56616
57175
  };
56617
57176
  }
56618
57177
  function detectRuntime(projectRoot) {
56619
- if (existsSync23(join52(projectRoot, "bun.lockb")) || existsSync23(join52(projectRoot, "bunfig.toml"))) {
57178
+ if (existsSync22(join53(projectRoot, "bun.lockb")) || existsSync22(join53(projectRoot, "bunfig.toml"))) {
56620
57179
  return "bun";
56621
57180
  }
56622
- if (existsSync23(join52(projectRoot, "package-lock.json")) || existsSync23(join52(projectRoot, "yarn.lock")) || existsSync23(join52(projectRoot, "pnpm-lock.yaml"))) {
57181
+ if (existsSync22(join53(projectRoot, "package-lock.json")) || existsSync22(join53(projectRoot, "yarn.lock")) || existsSync22(join53(projectRoot, "pnpm-lock.yaml"))) {
56623
57182
  return "node";
56624
57183
  }
56625
57184
  return "unknown";
56626
57185
  }
56627
57186
  function detectLanguage2(projectRoot) {
56628
- if (existsSync23(join52(projectRoot, "tsconfig.json")))
57187
+ if (existsSync22(join53(projectRoot, "tsconfig.json")))
56629
57188
  return "typescript";
56630
- if (existsSync23(join52(projectRoot, "pyproject.toml")) || existsSync23(join52(projectRoot, "setup.py"))) {
57189
+ if (existsSync22(join53(projectRoot, "pyproject.toml")) || existsSync22(join53(projectRoot, "setup.py"))) {
56631
57190
  return "python";
56632
57191
  }
56633
- if (existsSync23(join52(projectRoot, "Cargo.toml")))
57192
+ if (existsSync22(join53(projectRoot, "Cargo.toml")))
56634
57193
  return "rust";
56635
- if (existsSync23(join52(projectRoot, "go.mod")))
57194
+ if (existsSync22(join53(projectRoot, "go.mod")))
56636
57195
  return "go";
56637
57196
  return "unknown";
56638
57197
  }
56639
57198
  function detectLinter(projectRoot) {
56640
- if (existsSync23(join52(projectRoot, "biome.json")) || existsSync23(join52(projectRoot, "biome.jsonc"))) {
57199
+ if (existsSync22(join53(projectRoot, "biome.json")) || existsSync22(join53(projectRoot, "biome.jsonc"))) {
56641
57200
  return "biome";
56642
57201
  }
56643
- if (existsSync23(join52(projectRoot, ".eslintrc.json")) || existsSync23(join52(projectRoot, ".eslintrc.js")) || existsSync23(join52(projectRoot, "eslint.config.js"))) {
57202
+ if (existsSync22(join53(projectRoot, ".eslintrc.json")) || existsSync22(join53(projectRoot, ".eslintrc.js")) || existsSync22(join53(projectRoot, "eslint.config.js"))) {
56644
57203
  return "eslint";
56645
57204
  }
56646
57205
  return "unknown";
56647
57206
  }
56648
57207
  function detectMonorepo(projectRoot) {
56649
- if (existsSync23(join52(projectRoot, "turbo.json")))
57208
+ if (existsSync22(join53(projectRoot, "turbo.json")))
56650
57209
  return "turborepo";
56651
- if (existsSync23(join52(projectRoot, "nx.json")))
57210
+ if (existsSync22(join53(projectRoot, "nx.json")))
56652
57211
  return "nx";
56653
- if (existsSync23(join52(projectRoot, "pnpm-workspace.yaml")))
57212
+ if (existsSync22(join53(projectRoot, "pnpm-workspace.yaml")))
56654
57213
  return "pnpm-workspaces";
56655
57214
  const pkg = readPackageJson(projectRoot);
56656
57215
  if (pkg?.workspaces)
@@ -56792,9 +57351,9 @@ __export(exports_init, {
56792
57351
  checkInitCollision: () => checkInitCollision,
56793
57352
  _initDeps: () => _initDeps
56794
57353
  });
56795
- import { existsSync as existsSync24 } from "fs";
56796
- import { mkdir as mkdir9 } from "fs/promises";
56797
- import { join as join53 } from "path";
57354
+ import { existsSync as existsSync23 } from "fs";
57355
+ import { mkdir as mkdir8 } from "fs/promises";
57356
+ import { join as join54 } from "path";
56798
57357
  function validateProjectName(name) {
56799
57358
  if (!name)
56800
57359
  return { valid: false, error: "name must be non-empty" };
@@ -56830,9 +57389,9 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
56830
57389
  }
56831
57390
  async function updateGitignore(projectRoot) {
56832
57391
  const logger = getLogger();
56833
- const gitignorePath = join53(projectRoot, ".gitignore");
57392
+ const gitignorePath = join54(projectRoot, ".gitignore");
56834
57393
  let existing = "";
56835
- if (existsSync24(gitignorePath)) {
57394
+ if (existsSync23(gitignorePath)) {
56836
57395
  existing = await Bun.file(gitignorePath).text();
56837
57396
  }
56838
57397
  const missingEntries = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
@@ -56912,28 +57471,28 @@ function buildConstitution(stack) {
56912
57471
  async function initGlobal() {
56913
57472
  const logger = getLogger();
56914
57473
  const globalDir = globalConfigDir();
56915
- if (!existsSync24(globalDir)) {
56916
- await mkdir9(globalDir, { recursive: true });
57474
+ if (!existsSync23(globalDir)) {
57475
+ await mkdir8(globalDir, { recursive: true });
56917
57476
  logger.info("init", "Created global config directory", { path: globalDir });
56918
57477
  }
56919
- const configPath = join53(globalDir, "config.json");
56920
- if (!existsSync24(configPath)) {
57478
+ const configPath = join54(globalDir, "config.json");
57479
+ if (!existsSync23(configPath)) {
56921
57480
  await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
56922
57481
  `);
56923
57482
  logger.info("init", "Created global config", { path: configPath });
56924
57483
  } else {
56925
57484
  logger.info("init", "Global config already exists", { path: configPath });
56926
57485
  }
56927
- const constitutionPath = join53(globalDir, "constitution.md");
56928
- if (!existsSync24(constitutionPath)) {
57486
+ const constitutionPath = join54(globalDir, "constitution.md");
57487
+ if (!existsSync23(constitutionPath)) {
56929
57488
  await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
56930
57489
  logger.info("init", "Created global constitution", { path: constitutionPath });
56931
57490
  } else {
56932
57491
  logger.info("init", "Global constitution already exists", { path: constitutionPath });
56933
57492
  }
56934
- const hooksDir = join53(globalDir, "hooks");
56935
- if (!existsSync24(hooksDir)) {
56936
- await mkdir9(hooksDir, { recursive: true });
57493
+ const hooksDir = join54(globalDir, "hooks");
57494
+ if (!existsSync23(hooksDir)) {
57495
+ await mkdir8(hooksDir, { recursive: true });
56937
57496
  logger.info("init", "Created global hooks directory", { path: hooksDir });
56938
57497
  } else {
56939
57498
  logger.info("init", "Global hooks directory already exists", { path: hooksDir });
@@ -56964,7 +57523,7 @@ async function initProject(projectRoot, options) {
56964
57523
  if (detectedName && !options?.force) {
56965
57524
  const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
56966
57525
  if (collision.collision && collision.existing) {
56967
- const configPath2 = join53(projectDir, "config.json");
57526
+ const configPath2 = join54(projectDir, "config.json");
56968
57527
  throw new NaxError([
56969
57528
  `Project name collision: "${detectedName}"`,
56970
57529
  ` This project: ${projectRoot}`,
@@ -56977,8 +57536,8 @@ async function initProject(projectRoot, options) {
56977
57536
  `), "INIT_NAME_COLLISION", { stage: "init", name: detectedName });
56978
57537
  }
56979
57538
  }
56980
- if (!existsSync24(projectDir)) {
56981
- await mkdir9(projectDir, { recursive: true });
57539
+ if (!existsSync23(projectDir)) {
57540
+ await mkdir8(projectDir, { recursive: true });
56982
57541
  logger.info("init", "Created project config directory", { path: projectDir });
56983
57542
  }
56984
57543
  const stack = detectStack(projectRoot);
@@ -56992,8 +57551,8 @@ async function initProject(projectRoot, options) {
56992
57551
  linter: stack.linter,
56993
57552
  monorepo: stack.monorepo
56994
57553
  });
56995
- const configPath = join53(projectDir, "config.json");
56996
- if (!existsSync24(configPath)) {
57554
+ const configPath = join54(projectDir, "config.json");
57555
+ if (!existsSync23(configPath)) {
56997
57556
  await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
56998
57557
  `);
56999
57558
  logger.info("init", "Created project config", { path: configPath });
@@ -57001,16 +57560,16 @@ async function initProject(projectRoot, options) {
57001
57560
  logger.info("init", "Project config already exists", { path: configPath });
57002
57561
  }
57003
57562
  await initContext(projectRoot, { ai: options?.ai, force: options?.force });
57004
- const constitutionPath = join53(projectDir, "constitution.md");
57005
- if (!existsSync24(constitutionPath) || options?.force) {
57563
+ const constitutionPath = join54(projectDir, "constitution.md");
57564
+ if (!existsSync23(constitutionPath) || options?.force) {
57006
57565
  await Bun.write(constitutionPath, buildConstitution(stack));
57007
57566
  logger.info("init", "Created project constitution", { path: constitutionPath });
57008
57567
  } else {
57009
57568
  logger.info("init", "Project constitution already exists", { path: constitutionPath });
57010
57569
  }
57011
- const hooksDir = join53(projectDir, "hooks");
57012
- if (!existsSync24(hooksDir)) {
57013
- await mkdir9(hooksDir, { recursive: true });
57570
+ const hooksDir = join54(projectDir, "hooks");
57571
+ if (!existsSync23(hooksDir)) {
57572
+ await mkdir8(hooksDir, { recursive: true });
57014
57573
  logger.info("init", "Created project hooks directory", { path: hooksDir });
57015
57574
  } else {
57016
57575
  logger.info("init", "Project hooks directory already exists", { path: hooksDir });
@@ -57069,6 +57628,286 @@ var init_init2 = __esm(() => {
57069
57628
  };
57070
57629
  });
57071
57630
 
57631
+ // src/cli/setup-analyze.ts
57632
+ import { join as join55 } from "path";
57633
+ async function detectPackageManager(workdir) {
57634
+ const { fileExists } = _analyzeRepoDeps;
57635
+ if (await fileExists(join55(workdir, "bun.lock")))
57636
+ return { pmRunPrefix: "bun run", pmDlx: "bunx" };
57637
+ if (await fileExists(join55(workdir, "bun.lockb")))
57638
+ return { pmRunPrefix: "bun run", pmDlx: "bunx" };
57639
+ if (await fileExists(join55(workdir, "package-lock.json")))
57640
+ return { pmRunPrefix: "npm run", pmDlx: "npx" };
57641
+ if (await fileExists(join55(workdir, "yarn.lock")))
57642
+ return { pmRunPrefix: "yarn", pmDlx: "yarn dlx" };
57643
+ if (await fileExists(join55(workdir, "pnpm-lock.yaml")))
57644
+ return { pmRunPrefix: "pnpm run", pmDlx: "pnpx" };
57645
+ return { pmRunPrefix: "npm run", pmDlx: "npx" };
57646
+ }
57647
+ async function detectOrchestrator(workdir) {
57648
+ const { fileExists } = _analyzeRepoDeps;
57649
+ if (await fileExists(join55(workdir, "turbo.json")))
57650
+ return "turbo";
57651
+ if (await fileExists(join55(workdir, "nx.json")))
57652
+ return "nx";
57653
+ return "none";
57654
+ }
57655
+ async function getMissingScripts(packageDir) {
57656
+ const pkg = await _analyzeRepoDeps.readJson(join55(packageDir, "package.json"));
57657
+ const scripts = pkg?.scripts ?? {};
57658
+ return CANONICAL_SCRIPTS.filter((s) => !(s in scripts));
57659
+ }
57660
+ async function buildPackageFacts(workdir, relativeDir, testPatternMap) {
57661
+ const packageDir = relativeDir === "" ? workdir : join55(workdir, relativeDir);
57662
+ const [profile, missingScripts] = await Promise.all([
57663
+ _analyzeRepoDeps.detectProjectProfile(packageDir, {}),
57664
+ getMissingScripts(packageDir)
57665
+ ]);
57666
+ const detection = testPatternMap[relativeDir] ?? {
57667
+ patterns: [],
57668
+ confidence: "empty",
57669
+ sources: []
57670
+ };
57671
+ return {
57672
+ relativeDir,
57673
+ testFramework: profile.testFramework,
57674
+ testFilePatterns: detection.patterns,
57675
+ missingScripts
57676
+ };
57677
+ }
57678
+ async function analyzeRepo(workdir) {
57679
+ const { discoverWorkspacePackages: discover, detectTestFilePatternsForWorkspace: detectPatterns } = _analyzeRepoDeps;
57680
+ const [pmInfo, orchestrator, packageDirs] = await Promise.all([
57681
+ detectPackageManager(workdir),
57682
+ detectOrchestrator(workdir),
57683
+ discover(workdir)
57684
+ ]);
57685
+ const shape = packageDirs.length > 0 ? "mono" : "single";
57686
+ const dirsToAnalyze = shape === "single" ? [""] : packageDirs;
57687
+ const testPatternMap = await detectPatterns(workdir, packageDirs);
57688
+ const packages = await Promise.all(dirsToAnalyze.map((dir) => buildPackageFacts(workdir, dir, testPatternMap)));
57689
+ return {
57690
+ shape,
57691
+ packages,
57692
+ pmRunPrefix: pmInfo.pmRunPrefix,
57693
+ pmDlx: pmInfo.pmDlx,
57694
+ orchestrator
57695
+ };
57696
+ }
57697
+ var CANONICAL_SCRIPTS, _analyzeRepoDeps;
57698
+ var init_setup_analyze = __esm(() => {
57699
+ init_project();
57700
+ init_detect2();
57701
+ init_workspace();
57702
+ CANONICAL_SCRIPTS = ["build", "test", "lint", "type-check", "lint:fix"];
57703
+ _analyzeRepoDeps = {
57704
+ fileExists: async (path13) => Bun.file(path13).exists(),
57705
+ readJson: async (path13) => {
57706
+ try {
57707
+ const f = Bun.file(path13);
57708
+ if (!await f.exists())
57709
+ return null;
57710
+ return JSON.parse(await f.text());
57711
+ } catch {
57712
+ return null;
57713
+ }
57714
+ },
57715
+ discoverWorkspacePackages,
57716
+ detectProjectProfile,
57717
+ detectTestFilePatternsForWorkspace
57718
+ };
57719
+ });
57720
+
57721
+ // src/cli/setup-fill.ts
57722
+ import { join as join56 } from "path";
57723
+ async function addScriptToPackageJson(pkgJsonPath, key, value) {
57724
+ const pkg = await _fillScriptsDeps.readJson(pkgJsonPath) ?? {};
57725
+ const scripts = pkg.scripts ?? {};
57726
+ if (key in scripts)
57727
+ return;
57728
+ const updated = { ...pkg, scripts: { ...scripts, [key]: value } };
57729
+ await _fillScriptsDeps.writeFile(pkgJsonPath, JSON.stringify(updated, null, 2));
57730
+ }
57731
+ async function addTurboTask(turboJsonPath, taskKey) {
57732
+ const turbo = await _fillScriptsDeps.readJson(turboJsonPath) ?? {};
57733
+ const field = "pipeline" in turbo ? "pipeline" : "tasks";
57734
+ const existing = turbo[field] ?? {};
57735
+ if (taskKey in existing)
57736
+ return;
57737
+ const updated = { ...turbo, [field]: { ...existing, [taskKey]: {} } };
57738
+ await _fillScriptsDeps.writeFile(turboJsonPath, JSON.stringify(updated, null, 2));
57739
+ }
57740
+ async function fillScripts(workdir, analysis) {
57741
+ const { shape, packages, orchestrator } = analysis;
57742
+ if (shape === "single") {
57743
+ const rootPkg = packages[0];
57744
+ if (rootPkg?.missingScripts.includes(TYPE_CHECK_KEY)) {
57745
+ await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57746
+ }
57747
+ return;
57748
+ }
57749
+ for (const pkg of packages) {
57750
+ if (!pkg.missingScripts.includes(TYPE_CHECK_KEY))
57751
+ continue;
57752
+ await addScriptToPackageJson(join56(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
57753
+ }
57754
+ if (orchestrator === "turbo" && packages.some((p) => p.missingScripts.includes(TYPE_CHECK_KEY))) {
57755
+ await addTurboTask(join56(workdir, "turbo.json"), TYPE_CHECK_KEY);
57756
+ await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_TURBO_PASSTHROUGH);
57757
+ }
57758
+ }
57759
+ var TYPE_CHECK_KEY = "type-check", TYPE_CHECK_SCRIPT = "tsc --noEmit -p tsconfig.json", TYPE_CHECK_TURBO_PASSTHROUGH = "turbo run type-check", _fillScriptsDeps;
57760
+ var init_setup_fill = __esm(() => {
57761
+ _fillScriptsDeps = {
57762
+ readJson: async (path13) => {
57763
+ try {
57764
+ const f = Bun.file(path13);
57765
+ if (!await f.exists())
57766
+ return null;
57767
+ return JSON.parse(await f.text());
57768
+ } catch {
57769
+ return null;
57770
+ }
57771
+ },
57772
+ writeFile: async (path13, content) => {
57773
+ await Bun.write(path13, content);
57774
+ }
57775
+ };
57776
+ });
57777
+
57778
+ // src/cli/setup-llm.ts
57779
+ var generateSetupPlan = (ctx, analysis) => callOp(ctx, setupGenerateOp, analysis);
57780
+ var init_setup_llm = __esm(() => {
57781
+ init_operations();
57782
+ init_setup_generate();
57783
+ });
57784
+
57785
+ // src/cli/setup-verify.ts
57786
+ async function runSetupGate(workdir, config2) {
57787
+ const logger = getLogger();
57788
+ const quality = config2.quality;
57789
+ const testCmd = quality?.commands?.test;
57790
+ if (!testCmd) {
57791
+ logger.info("setup-verify", "No test command configured \u2014 skipping verification gate", {
57792
+ storyId: "setup"
57793
+ });
57794
+ return 0;
57795
+ }
57796
+ logger.info("setup-verify", "Running verification gate", { storyId: "setup", cmd: testCmd });
57797
+ const parts = testCmd.trim().split(/\s+/).filter(Boolean);
57798
+ if (parts.length === 0)
57799
+ return 0;
57800
+ const proc = _setupVerifyDeps.spawn(parts, { cwd: workdir });
57801
+ return await proc.exited;
57802
+ }
57803
+ var _setupVerifyDeps;
57804
+ var init_setup_verify = __esm(() => {
57805
+ init_logger2();
57806
+ _setupVerifyDeps = {
57807
+ spawn: Bun.spawn.bind(Bun)
57808
+ };
57809
+ });
57810
+
57811
+ // src/cli/setup.ts
57812
+ var exports_setup = {};
57813
+ __export(exports_setup, {
57814
+ setupCommand: () => setupCommand,
57815
+ _setupDeps: () => _setupDeps
57816
+ });
57817
+ import { join as join57 } from "path";
57818
+ async function setupCommand(options = {}) {
57819
+ const workdir = options.dir ?? process.cwd();
57820
+ const naxDir = join57(workdir, ".nax");
57821
+ const naxConfigPath = join57(naxDir, "config.json");
57822
+ const exists = await _setupDeps.fileExists(naxConfigPath);
57823
+ if (exists && !options.force) {
57824
+ _setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
57825
+ return 1;
57826
+ }
57827
+ const analysis = await _setupDeps.analyzeRepo(workdir);
57828
+ const { ctx, close } = await _setupDeps.buildCallContext(workdir, options.agent);
57829
+ let plan;
57830
+ try {
57831
+ try {
57832
+ plan = await _setupDeps.generateSetupPlan(ctx, analysis);
57833
+ } catch (err) {
57834
+ if (err instanceof NaxError && err.code === "SETUP_PLAN_INVALID") {
57835
+ _setupDeps.stderr(`[setup] ${err.message}`);
57836
+ return 1;
57837
+ }
57838
+ throw err;
57839
+ }
57840
+ if (options.dryRun) {
57841
+ _setupDeps.stdout(`[setup] Dry run \u2014 planned root config:
57842
+ ${JSON.stringify(plan.config, null, 2)}`);
57843
+ return 0;
57844
+ }
57845
+ for (const gap of plan.gaps) {
57846
+ _setupDeps.stderr(`[setup] gap: ${gap}`);
57847
+ }
57848
+ if (options.fillScripts) {
57849
+ await _setupDeps.fillScripts(workdir, analysis);
57850
+ }
57851
+ await _setupDeps.mkdir(naxDir);
57852
+ await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
57853
+ for (const mc of plan.monoConfigs) {
57854
+ const monoDir = join57(naxDir, "mono", mc.relativeDir);
57855
+ await _setupDeps.mkdir(monoDir);
57856
+ await _setupDeps.writeFile(join57(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
57857
+ }
57858
+ const gateResult = await _setupDeps.runGate(workdir, plan.config);
57859
+ if (gateResult !== 0) {
57860
+ return gateResult;
57861
+ }
57862
+ return 0;
57863
+ } finally {
57864
+ await close();
57865
+ }
57866
+ }
57867
+ var _setupDeps;
57868
+ var init_setup = __esm(() => {
57869
+ init_errors();
57870
+ init_setup_analyze();
57871
+ init_setup_fill();
57872
+ init_setup_llm();
57873
+ init_setup_verify();
57874
+ _setupDeps = {
57875
+ analyzeRepo,
57876
+ fillScripts,
57877
+ buildCallContext: async (workdir, agentName) => {
57878
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
57879
+ const { createRuntime: createRuntime2 } = await Promise.resolve().then(() => (init_runtime(), exports_runtime));
57880
+ const config2 = await loadConfig2(workdir);
57881
+ const rt = createRuntime2(config2, workdir);
57882
+ return {
57883
+ ctx: {
57884
+ runtime: rt,
57885
+ packageView: rt.packages.resolve(),
57886
+ packageDir: workdir,
57887
+ agentName: agentName ?? rt.agentManager.getDefault()
57888
+ },
57889
+ close: () => rt.close()
57890
+ };
57891
+ },
57892
+ generateSetupPlan: (ctx, analysis) => generateSetupPlan(ctx, analysis),
57893
+ runGate: (workdir, config2) => runSetupGate(workdir, config2),
57894
+ fileExists: (path13) => Bun.file(path13).exists(),
57895
+ writeFile: (path13, content) => Bun.write(path13, content).then(() => {}),
57896
+ mkdir: async (path13) => {
57897
+ const proc = Bun.spawn(["mkdir", "-p", path13]);
57898
+ await proc.exited;
57899
+ },
57900
+ stdout: (msg) => {
57901
+ process.stdout.write(`${msg}
57902
+ `);
57903
+ },
57904
+ stderr: (msg) => {
57905
+ process.stderr.write(`${msg}
57906
+ `);
57907
+ }
57908
+ };
57909
+ });
57910
+
57072
57911
  // src/plugins/builtin/curator/collect.ts
57073
57912
  import * as path13 from "path";
57074
57913
  function now() {
@@ -57770,12 +58609,12 @@ function renderProposals(proposals, runId, observationCount) {
57770
58609
  }
57771
58610
 
57772
58611
  // src/plugins/builtin/curator/rollup.ts
57773
- import { appendFile as appendFile3, mkdir as mkdir10, writeFile } from "fs/promises";
58612
+ import { appendFile as appendFile3, mkdir as mkdir9, writeFile } from "fs/promises";
57774
58613
  import * as path14 from "path";
57775
58614
  async function appendToRollup(observations, rollupPath) {
57776
58615
  try {
57777
58616
  const dir = path14.dirname(rollupPath);
57778
- await mkdir10(dir, { recursive: true });
58617
+ await mkdir9(dir, { recursive: true });
57779
58618
  if (observations.length === 0) {
57780
58619
  const f = Bun.file(rollupPath);
57781
58620
  if (!await f.exists()) {
@@ -57792,7 +58631,7 @@ async function appendToRollup(observations, rollupPath) {
57792
58631
  var init_rollup = () => {};
57793
58632
 
57794
58633
  // src/plugins/builtin/curator/index.ts
57795
- import { mkdir as mkdir11 } from "fs/promises";
58634
+ import { mkdir as mkdir10 } from "fs/promises";
57796
58635
  import * as path15 from "path";
57797
58636
  function getCuratorEnabled(context) {
57798
58637
  const cfg = context.config;
@@ -57867,7 +58706,7 @@ var init_curator = __esm(() => {
57867
58706
  if (context.outputDir) {
57868
58707
  const { observationsPath, rollupPath } = resolveCuratorOutputs(curatorContext);
57869
58708
  const runDir = path15.dirname(observationsPath);
57870
- await mkdir11(runDir, { recursive: true });
58709
+ await mkdir10(runDir, { recursive: true });
57871
58710
  await Bun.write(observationsPath, observations.map((o) => JSON.stringify(o)).join(`
57872
58711
  `) + (observations.length > 0 ? `
57873
58712
  ` : ""));
@@ -58443,12 +59282,12 @@ var init_loader4 = __esm(() => {
58443
59282
  });
58444
59283
 
58445
59284
  // src/utils/paths.ts
58446
- import { join as join64 } from "path";
59285
+ import { join as join68 } from "path";
58447
59286
  function getRunsDir() {
58448
- return process.env.NAX_RUNS_DIR ?? join64(globalConfigDir(), "runs");
59287
+ return process.env.NAX_RUNS_DIR ?? join68(globalConfigDir(), "runs");
58449
59288
  }
58450
59289
  function getEventsRootDir() {
58451
- return join64(globalConfigDir(), "events");
59290
+ return join68(globalConfigDir(), "events");
58452
59291
  }
58453
59292
  var init_paths3 = __esm(() => {
58454
59293
  init_paths();
@@ -58508,7 +59347,7 @@ var init_command_argv = __esm(() => {
58508
59347
  });
58509
59348
 
58510
59349
  // src/hooks/runner.ts
58511
- import { join as join71 } from "path";
59350
+ import { join as join75 } from "path";
58512
59351
  function createDrainDeadline2(deadlineMs) {
58513
59352
  let timeoutId;
58514
59353
  const promise2 = new Promise((resolve16) => {
@@ -58527,14 +59366,14 @@ async function loadHooksConfig(projectDir, globalDir) {
58527
59366
  let globalHooks = { hooks: {} };
58528
59367
  let projectHooks = { hooks: {} };
58529
59368
  let skipGlobal = false;
58530
- const projectPath = join71(projectDir, "hooks.json");
59369
+ const projectPath = join75(projectDir, "hooks.json");
58531
59370
  const projectData = await loadJsonFile(projectPath, "hooks");
58532
59371
  if (projectData) {
58533
59372
  projectHooks = projectData;
58534
59373
  skipGlobal = projectData.skipGlobal ?? false;
58535
59374
  }
58536
59375
  if (!skipGlobal && globalDir) {
58537
- const globalPath = join71(globalDir, "hooks.json");
59376
+ const globalPath = join75(globalDir, "hooks.json");
58538
59377
  const globalData = await loadJsonFile(globalPath, "hooks");
58539
59378
  if (globalData) {
58540
59379
  globalHooks = globalData;
@@ -58704,7 +59543,7 @@ var package_default;
58704
59543
  var init_package = __esm(() => {
58705
59544
  package_default = {
58706
59545
  name: "@nathapp/nax",
58707
- version: "0.69.0",
59546
+ version: "0.69.2",
58708
59547
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
58709
59548
  type: "module",
58710
59549
  bin: {
@@ -58799,8 +59638,8 @@ var init_version = __esm(() => {
58799
59638
  NAX_VERSION = package_default.version;
58800
59639
  NAX_COMMIT = (() => {
58801
59640
  try {
58802
- if (/^[0-9a-f]{6,10}$/.test("ce4d8f0e"))
58803
- return "ce4d8f0e";
59641
+ if (/^[0-9a-f]{6,10}$/.test("99db370f"))
59642
+ return "99db370f";
58804
59643
  } catch {}
58805
59644
  try {
58806
59645
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -59676,16 +60515,16 @@ var init_acceptance_loop = __esm(() => {
59676
60515
  });
59677
60516
 
59678
60517
  // src/session/scratch-purge.ts
59679
- import { mkdir as mkdir13, rename, rm } from "fs/promises";
59680
- import { dirname as dirname12, join as join72 } from "path";
60518
+ import { mkdir as mkdir12, rename, rm } from "fs/promises";
60519
+ import { dirname as dirname12, join as join76 } from "path";
59681
60520
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
59682
- const sessionsDir = join72(projectDir, ".nax", "features", featureName, "sessions");
60521
+ const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
59683
60522
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
59684
60523
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
59685
60524
  let purged = 0;
59686
60525
  for (const sessionId of sessionIds) {
59687
- const sessionDir = join72(sessionsDir, sessionId);
59688
- const descriptorPath = join72(sessionDir, "descriptor.json");
60526
+ const sessionDir = join76(sessionsDir, sessionId);
60527
+ const descriptorPath = join76(sessionDir, "descriptor.json");
59689
60528
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
59690
60529
  continue;
59691
60530
  let lastActivityAt;
@@ -59701,7 +60540,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
59701
60540
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
59702
60541
  continue;
59703
60542
  if (archiveInsteadOfDelete) {
59704
- const archiveDest = join72(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
60543
+ const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
59705
60544
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
59706
60545
  } else {
59707
60546
  await _scratchPurgeDeps.remove(sessionDir);
@@ -59728,7 +60567,7 @@ var init_scratch_purge = __esm(() => {
59728
60567
  readFile: (path19) => Bun.file(path19).text(),
59729
60568
  remove: (path19) => rm(path19, { recursive: true, force: true }),
59730
60569
  move: async (src, dest) => {
59731
- await mkdir13(dirname12(dest), { recursive: true });
60570
+ await mkdir12(dirname12(dest), { recursive: true });
59732
60571
  await rename(src, dest);
59733
60572
  },
59734
60573
  now: () => Date.now()
@@ -60438,19 +61277,19 @@ function precomputeBatchPlan(stories, maxBatchSize = DEFAULT_MAX_BATCH_SIZE) {
60438
61277
  var DEFAULT_MAX_BATCH_SIZE = 4;
60439
61278
 
60440
61279
  // src/pipeline/subscribers/events-writer.ts
60441
- import { appendFile as appendFile4, mkdir as mkdir14 } from "fs/promises";
60442
- import { basename as basename13, join as join73 } from "path";
61280
+ import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
61281
+ import { basename as basename13, join as join77 } from "path";
60443
61282
  function wireEventsWriter(bus, feature, runId, workdir) {
60444
61283
  const logger = getSafeLogger();
60445
61284
  const project = basename13(workdir);
60446
- const eventsDir = join73(getEventsRootDir(), project);
60447
- const eventsFile = join73(eventsDir, "events.jsonl");
61285
+ const eventsDir = join77(getEventsRootDir(), project);
61286
+ const eventsFile = join77(eventsDir, "events.jsonl");
60448
61287
  let dirReady = false;
60449
61288
  const write = (line) => {
60450
61289
  return (async () => {
60451
61290
  try {
60452
61291
  if (!dirReady) {
60453
- await mkdir14(eventsDir, { recursive: true });
61292
+ await mkdir13(eventsDir, { recursive: true });
60454
61293
  dirReady = true;
60455
61294
  }
60456
61295
  await appendFile4(eventsFile, `${JSON.stringify(line)}
@@ -60624,24 +61463,24 @@ var init_interaction2 = __esm(() => {
60624
61463
  });
60625
61464
 
60626
61465
  // src/pipeline/subscribers/registry.ts
60627
- import { mkdir as mkdir15, writeFile as writeFile2 } from "fs/promises";
60628
- import { basename as basename14, join as join74 } from "path";
61466
+ import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
61467
+ import { basename as basename14, join as join78 } from "path";
60629
61468
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
60630
61469
  const logger = getSafeLogger();
60631
61470
  const project = basename14(workdir);
60632
- const runDir = join74(getRunsDir(), `${project}-${feature}-${runId}`);
60633
- const metaFile = join74(runDir, "meta.json");
61471
+ const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
61472
+ const metaFile = join78(runDir, "meta.json");
60634
61473
  const unsub = bus.on("run:started", (_ev) => {
60635
61474
  return (async () => {
60636
61475
  try {
60637
- await mkdir15(runDir, { recursive: true });
61476
+ await mkdir14(runDir, { recursive: true });
60638
61477
  const meta3 = {
60639
61478
  runId,
60640
61479
  project,
60641
61480
  feature,
60642
61481
  workdir,
60643
- statusPath: join74(outputDir, "features", feature, "status.json"),
60644
- eventsDir: join74(outputDir, "features", feature, "runs"),
61482
+ statusPath: join78(outputDir, "features", feature, "status.json"),
61483
+ eventsDir: join78(outputDir, "features", feature, "runs"),
60645
61484
  registeredAt: new Date().toISOString()
60646
61485
  };
60647
61486
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -60886,8 +61725,8 @@ var init_types9 = __esm(() => {
60886
61725
  });
60887
61726
 
60888
61727
  // src/worktree/dependencies.ts
60889
- import { existsSync as existsSync31 } from "fs";
60890
- import { join as join75 } from "path";
61728
+ import { existsSync as existsSync30 } from "fs";
61729
+ import { join as join79 } from "path";
60891
61730
  async function prepareWorktreeDependencies(options) {
60892
61731
  const mode = options.config.execution.worktreeDependencies.mode;
60893
61732
  const resolvedCwd = resolveDependencyCwd(options);
@@ -60901,7 +61740,7 @@ async function prepareWorktreeDependencies(options) {
60901
61740
  }
60902
61741
  }
60903
61742
  function resolveDependencyCwd(options) {
60904
- return options.storyWorkdir ? join75(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
61743
+ return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
60905
61744
  }
60906
61745
  function resolveInheritedDependencies(options, resolvedCwd) {
60907
61746
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -60911,14 +61750,14 @@ function resolveInheritedDependencies(options, resolvedCwd) {
60911
61750
  }
60912
61751
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
60913
61752
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
60914
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join75(directory, filename))));
61753
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
60915
61754
  }
60916
61755
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
60917
- const setupCommand = config2.execution.worktreeDependencies.setupCommand;
60918
- if (!setupCommand) {
61756
+ const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
61757
+ if (!setupCommand2) {
60919
61758
  throw new WorktreeDependencyPreparationError("[worktree-deps] provision mode requires execution.worktreeDependencies.setupCommand in phase 1.", "provision");
60920
61759
  }
60921
- const argv = parseCommandToArgv(setupCommand);
61760
+ const argv = parseCommandToArgv(setupCommand2);
60922
61761
  if (argv.length === 0) {
60923
61762
  throw new WorktreeDependencyPreparationError("[worktree-deps] setupCommand cannot be empty.", "provision");
60924
61763
  }
@@ -60962,7 +61801,7 @@ var init_dependencies = __esm(() => {
60962
61801
  "build.gradle.kts"
60963
61802
  ];
60964
61803
  _worktreeDependencyDeps = {
60965
- existsSync: existsSync31,
61804
+ existsSync: existsSync30,
60966
61805
  spawn
60967
61806
  };
60968
61807
  });
@@ -60973,19 +61812,19 @@ __export(exports_manager, {
60973
61812
  _managerDeps: () => _managerDeps,
60974
61813
  WorktreeManager: () => WorktreeManager
60975
61814
  });
60976
- import { existsSync as existsSync32, symlinkSync } from "fs";
60977
- import { mkdir as mkdir16 } from "fs/promises";
60978
- import { join as join76 } from "path";
61815
+ import { existsSync as existsSync31, symlinkSync } from "fs";
61816
+ import { mkdir as mkdir15 } from "fs/promises";
61817
+ import { join as join80 } from "path";
60979
61818
 
60980
61819
  class WorktreeManager {
60981
61820
  async ensureGitExcludes(projectRoot) {
60982
61821
  const logger = getSafeLogger();
60983
- const infoDir = join76(projectRoot, ".git", "info");
60984
- const excludePath = join76(infoDir, "exclude");
61822
+ const infoDir = join80(projectRoot, ".git", "info");
61823
+ const excludePath = join80(infoDir, "exclude");
60985
61824
  try {
60986
- await mkdir16(infoDir, { recursive: true });
61825
+ await mkdir15(infoDir, { recursive: true });
60987
61826
  let existing = "";
60988
- if (existsSync32(excludePath)) {
61827
+ if (existsSync31(excludePath)) {
60989
61828
  existing = await Bun.file(excludePath).text();
60990
61829
  }
60991
61830
  const missing = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
@@ -61008,7 +61847,7 @@ ${missing.join(`
61008
61847
  }
61009
61848
  async create(projectRoot, storyId) {
61010
61849
  validateStoryId(storyId);
61011
- const worktreePath = join76(projectRoot, ".nax-wt", storyId);
61850
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
61012
61851
  const branchName = `nax/${storyId}`;
61013
61852
  try {
61014
61853
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -61049,9 +61888,9 @@ ${missing.join(`
61049
61888
  }
61050
61889
  throw new Error(`Failed to create worktree: ${String(error48)}`);
61051
61890
  }
61052
- const envSource = join76(projectRoot, ".env");
61053
- if (existsSync32(envSource)) {
61054
- const envTarget = join76(worktreePath, ".env");
61891
+ const envSource = join80(projectRoot, ".env");
61892
+ if (existsSync31(envSource)) {
61893
+ const envTarget = join80(worktreePath, ".env");
61055
61894
  try {
61056
61895
  symlinkSync(envSource, envTarget, "file");
61057
61896
  } catch (error48) {
@@ -61062,7 +61901,7 @@ ${missing.join(`
61062
61901
  }
61063
61902
  async remove(projectRoot, storyId) {
61064
61903
  validateStoryId(storyId);
61065
- const worktreePath = join76(projectRoot, ".nax-wt", storyId);
61904
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
61066
61905
  const branchName = `nax/${storyId}`;
61067
61906
  try {
61068
61907
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -61874,10 +62713,10 @@ var init_merge_conflict_rectify = __esm(() => {
61874
62713
  });
61875
62714
 
61876
62715
  // src/execution/pipeline-result-handler.ts
61877
- import { join as join77 } from "path";
62716
+ import { join as join81 } from "path";
61878
62717
  async function removeWorktreeDirectory(projectRoot, storyId) {
61879
62718
  const logger = getSafeLogger();
61880
- const worktreePath = join77(projectRoot, ".nax-wt", storyId);
62719
+ const worktreePath = join81(projectRoot, ".nax-wt", storyId);
61881
62720
  try {
61882
62721
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
61883
62722
  cwd: projectRoot,
@@ -62093,8 +62932,8 @@ var init_pipeline_result_handler = __esm(() => {
62093
62932
  });
62094
62933
 
62095
62934
  // src/execution/iteration-runner.ts
62096
- import { existsSync as existsSync33 } from "fs";
62097
- import { join as join78 } from "path";
62935
+ import { existsSync as existsSync32 } from "fs";
62936
+ import { join as join82 } from "path";
62098
62937
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
62099
62938
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
62100
62939
  if (ctx.dryRun) {
@@ -62119,7 +62958,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62119
62958
  const storyStartTime = Date.now();
62120
62959
  let effectiveWorkdir = ctx.workdir;
62121
62960
  if (ctx.config.execution.storyIsolation === "worktree") {
62122
- const worktreePath = join78(ctx.workdir, ".nax-wt", story.id);
62961
+ const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
62123
62962
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
62124
62963
  if (!worktreeExists) {
62125
62964
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -62139,7 +62978,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62139
62978
  }
62140
62979
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
62141
62980
  const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
62142
- const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join78(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;
62143
62982
  let dependencyContext;
62144
62983
  if (ctx.config.execution.storyIsolation === "worktree") {
62145
62984
  try {
@@ -62166,7 +63005,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
62166
63005
  };
62167
63006
  }
62168
63007
  }
62169
- const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join78(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join78(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;
62170
63009
  const pipelineContext = {
62171
63010
  config: effectiveConfig,
62172
63011
  rootConfig: ctx.config,
@@ -62298,7 +63137,7 @@ var init_iteration_runner = __esm(() => {
62298
63137
  loadConfigForWorkdir,
62299
63138
  prepareWorktreeDependencies,
62300
63139
  runPipeline,
62301
- existsSync: existsSync33,
63140
+ existsSync: existsSync32,
62302
63141
  worktreeManager: new WorktreeManager
62303
63142
  };
62304
63143
  });
@@ -62368,7 +63207,7 @@ __export(exports_parallel_worker, {
62368
63207
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
62369
63208
  _parallelWorkerDeps: () => _parallelWorkerDeps
62370
63209
  });
62371
- import { join as join79 } from "path";
63210
+ import { join as join83 } from "path";
62372
63211
  function buildWorktreePipelineContext(base, _story) {
62373
63212
  return { ...base, prd: structuredClone(base.prd) };
62374
63213
  }
@@ -62391,7 +63230,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
62391
63230
  story,
62392
63231
  stories: [story],
62393
63232
  projectDir: context.projectDir,
62394
- workdir: dependencyContext.cwd ?? (story.workdir ? join79(worktreePath, story.workdir) : worktreePath),
63233
+ workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
62395
63234
  worktreeDependencyContext: dependencyContext,
62396
63235
  routing,
62397
63236
  storyGitRef: storyGitRef ?? undefined
@@ -63278,7 +64117,7 @@ async function writeStatusFile(filePath, status) {
63278
64117
  var init_status_file = () => {};
63279
64118
 
63280
64119
  // src/execution/status-writer.ts
63281
- import { join as join80 } from "path";
64120
+ import { join as join84 } from "path";
63282
64121
 
63283
64122
  class StatusWriter {
63284
64123
  statusFile;
@@ -63397,7 +64236,7 @@ class StatusWriter {
63397
64236
  if (!this._prd)
63398
64237
  return;
63399
64238
  const safeLogger = getSafeLogger();
63400
- const featureStatusPath = join80(featureDir, "status.json");
64239
+ const featureStatusPath = join84(featureDir, "status.json");
63401
64240
  const write = async () => {
63402
64241
  try {
63403
64242
  const base = this.getSnapshot(totalCost, iterations);
@@ -63428,11 +64267,11 @@ __export(exports_migrate, {
63428
64267
  migrateCommand: () => migrateCommand,
63429
64268
  detectGeneratedContent: () => detectGeneratedContent
63430
64269
  });
63431
- import { existsSync as existsSync34 } from "fs";
63432
- import { mkdir as mkdir17, readdir as readdir6, rename as rename3 } from "fs/promises";
64270
+ import { existsSync as existsSync33 } from "fs";
64271
+ import { mkdir as mkdir16, readdir as readdir6, rename as rename3 } from "fs/promises";
63433
64272
  import path22 from "path";
63434
64273
  async function detectGeneratedContent(naxDir) {
63435
- if (!existsSync34(naxDir))
64274
+ if (!existsSync33(naxDir))
63436
64275
  return [];
63437
64276
  const candidates = [];
63438
64277
  let entries = [];
@@ -63447,7 +64286,7 @@ async function detectGeneratedContent(naxDir) {
63447
64286
  }
63448
64287
  }
63449
64288
  const featuresDir = path22.join(naxDir, "features");
63450
- if (existsSync34(featuresDir)) {
64289
+ if (existsSync33(featuresDir)) {
63451
64290
  let featureDirs = [];
63452
64291
  try {
63453
64292
  featureDirs = await readdir6(featuresDir);
@@ -63509,7 +64348,7 @@ async function migrateCommand(options) {
63509
64348
  });
63510
64349
  }
63511
64350
  const src = path22.join(globalConfigDir(), options.reclaim);
63512
- if (!existsSync34(src)) {
64351
+ if (!existsSync33(src)) {
63513
64352
  throw new NaxError(`Nothing to reclaim: ~/.nax/${options.reclaim} does not exist`, "MIGRATE_RECLAIM_NOT_FOUND", {
63514
64353
  stage: "migrate",
63515
64354
  name: options.reclaim
@@ -63517,7 +64356,7 @@ async function migrateCommand(options) {
63517
64356
  }
63518
64357
  const archiveBase = path22.join(globalConfigDir(), "_archive");
63519
64358
  const archiveDest = path22.join(archiveBase, `${options.reclaim}-${Date.now()}`);
63520
- await mkdir17(archiveBase, { recursive: true });
64359
+ await mkdir16(archiveBase, { recursive: true });
63521
64360
  await rename3(src, archiveDest);
63522
64361
  logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
63523
64362
  return;
@@ -63555,7 +64394,7 @@ async function migrateCommand(options) {
63555
64394
  }
63556
64395
  const naxDir = path22.join(options.workdir, ".nax");
63557
64396
  const configPath = path22.join(naxDir, "config.json");
63558
- if (!existsSync34(configPath)) {
64397
+ if (!existsSync33(configPath)) {
63559
64398
  throw new NaxError("No .nax/config.json found \u2014 run nax init first", "MIGRATE_NO_CONFIG", {
63560
64399
  stage: "migrate",
63561
64400
  workdir: options.workdir
@@ -63585,12 +64424,12 @@ async function migrateCommand(options) {
63585
64424
  }
63586
64425
  return;
63587
64426
  }
63588
- await mkdir17(destBase, { recursive: true });
64427
+ await mkdir16(destBase, { recursive: true });
63589
64428
  let moved = 0;
63590
64429
  for (const candidate of candidates) {
63591
64430
  const dest = path22.join(destBase, candidate.name);
63592
- await mkdir17(path22.dirname(dest), { recursive: true });
63593
- if (existsSync34(dest)) {
64431
+ await mkdir16(path22.dirname(dest), { recursive: true });
64432
+ if (existsSync33(dest)) {
63594
64433
  throw new NaxError(`Migration conflict: destination already exists.
63595
64434
  Source: ${candidate.srcPath}
63596
64435
  Destination: ${dest}
@@ -63831,7 +64670,7 @@ __export(exports_run_initialization, {
63831
64670
  initializeRun: () => initializeRun,
63832
64671
  _reconcileDeps: () => _reconcileDeps
63833
64672
  });
63834
- import { join as join81 } from "path";
64673
+ import { join as join85 } from "path";
63835
64674
  async function reconcileState(prd, prdPath, workdir, config2) {
63836
64675
  const logger = getSafeLogger();
63837
64676
  let reconciledCount = 0;
@@ -63848,7 +64687,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
63848
64687
  });
63849
64688
  continue;
63850
64689
  }
63851
- const effectiveWorkdir = story.workdir ? join81(workdir, story.workdir) : workdir;
64690
+ const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
63852
64691
  try {
63853
64692
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
63854
64693
  if (!reviewResult.success) {
@@ -64114,6 +64953,17 @@ async function setupRun(options) {
64114
64953
  featureName: options.feature,
64115
64954
  agentStreamEvents: options.agentStreamEvents
64116
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
+ }
64117
64967
  await runtime.pidRegistry.cleanupStale();
64118
64968
  const cleanupCrashHandlers = installCrashHandlers({
64119
64969
  statusWriter,
@@ -64280,6 +65130,7 @@ async function setupRun(options) {
64280
65130
  var _runSetupDeps;
64281
65131
  var init_run_setup = __esm(() => {
64282
65132
  init_pipeline();
65133
+ init_test_runners();
64283
65134
  init_paths();
64284
65135
  init_errors();
64285
65136
  init_interaction();
@@ -64289,7 +65140,6 @@ var init_run_setup = __esm(() => {
64289
65140
  init_project();
64290
65141
  init_runtime();
64291
65142
  init_session();
64292
- init_resolver();
64293
65143
  init_version();
64294
65144
  init_crash_recovery();
64295
65145
  init_helpers();
@@ -93641,7 +94491,7 @@ __export(exports_curator, {
93641
94491
  });
93642
94492
  import { readdirSync as readdirSync8 } from "fs";
93643
94493
  import { unlink as unlink4 } from "fs/promises";
93644
- import { basename as basename15, join as join83 } from "path";
94494
+ import { basename as basename15, join as join87 } from "path";
93645
94495
  function getProjectKey(config2, projectDir) {
93646
94496
  return config2.name?.trim() || basename15(projectDir);
93647
94497
  }
@@ -93724,7 +94574,7 @@ async function curatorStatus(options) {
93724
94574
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
93725
94575
  const projectKey = getProjectKey(config2, resolved.projectDir);
93726
94576
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
93727
- const runsDir = join83(outputDir, "runs");
94577
+ const runsDir = join87(outputDir, "runs");
93728
94578
  const runIds = listRunIds(runsDir);
93729
94579
  let runId;
93730
94580
  if (options.run) {
@@ -93741,8 +94591,8 @@ async function curatorStatus(options) {
93741
94591
  runId = runIds[runIds.length - 1];
93742
94592
  }
93743
94593
  console.log(`Run: ${runId}`);
93744
- const runDir = join83(runsDir, runId);
93745
- const observationsPath = join83(runDir, "observations.jsonl");
94594
+ const runDir = join87(runsDir, runId);
94595
+ const observationsPath = join87(runDir, "observations.jsonl");
93746
94596
  const observations = await parseObservations(observationsPath);
93747
94597
  const counts = new Map;
93748
94598
  for (const obs of observations) {
@@ -93752,7 +94602,7 @@ async function curatorStatus(options) {
93752
94602
  for (const [kind, count] of counts.entries()) {
93753
94603
  console.log(` ${kind}: ${count}`);
93754
94604
  }
93755
- const proposalsPath = join83(runDir, "curator-proposals.md");
94605
+ const proposalsPath = join87(runDir, "curator-proposals.md");
93756
94606
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
93757
94607
  if (proposalText !== null) {
93758
94608
  console.log("");
@@ -93766,8 +94616,8 @@ async function curatorCommit(options) {
93766
94616
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
93767
94617
  const projectKey = getProjectKey(config2, resolved.projectDir);
93768
94618
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
93769
- const runDir = join83(outputDir, "runs", options.runId);
93770
- const proposalsPath = join83(runDir, "curator-proposals.md");
94619
+ const runDir = join87(outputDir, "runs", options.runId);
94620
+ const proposalsPath = join87(runDir, "curator-proposals.md");
93771
94621
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
93772
94622
  if (proposalText === null) {
93773
94623
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -93783,7 +94633,7 @@ async function curatorCommit(options) {
93783
94633
  const dropFileState = new Map;
93784
94634
  const skippedDrops = new Set;
93785
94635
  for (const drop2 of drops) {
93786
- const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
94636
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
93787
94637
  if (!dropFileState.has(targetPath)) {
93788
94638
  const fileExists2 = await Bun.file(targetPath).exists();
93789
94639
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -93817,7 +94667,7 @@ async function curatorCommit(options) {
93817
94667
  if (skippedDrops.has(drop2)) {
93818
94668
  continue;
93819
94669
  }
93820
- const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
94670
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
93821
94671
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
93822
94672
  const filtered = filterDropContent(existing, drop2.description);
93823
94673
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -93826,7 +94676,7 @@ async function curatorCommit(options) {
93826
94676
  }
93827
94677
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
93828
94678
  for (const add2 of adds) {
93829
- const targetPath = join83(resolved.projectDir, add2.canonicalFile);
94679
+ const targetPath = join87(resolved.projectDir, add2.canonicalFile);
93830
94680
  const content = buildAddContent(add2);
93831
94681
  await _curatorCmdDeps.appendFile(targetPath, content);
93832
94682
  modifiedFiles.add(targetPath);
@@ -93863,7 +94713,7 @@ async function curatorDryrun(options) {
93863
94713
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
93864
94714
  const projectKey = getProjectKey(config2, resolved.projectDir);
93865
94715
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
93866
- const runsDir = join83(outputDir, "runs");
94716
+ const runsDir = join87(outputDir, "runs");
93867
94717
  const runIds = listRunIds(runsDir);
93868
94718
  if (runIds.length === 0) {
93869
94719
  console.log("No runs found.");
@@ -93874,7 +94724,7 @@ async function curatorDryrun(options) {
93874
94724
  console.log(`Run ${options.run} not found in ${runsDir}.`);
93875
94725
  return;
93876
94726
  }
93877
- const observationsPath = join83(runsDir, runId, "observations.jsonl");
94727
+ const observationsPath = join87(runsDir, runId, "observations.jsonl");
93878
94728
  const observations = await parseObservations(observationsPath);
93879
94729
  const thresholds = getThresholds(config2);
93880
94730
  const proposals = runHeuristics(observations, thresholds);
@@ -93915,12 +94765,12 @@ async function curatorGc(options) {
93915
94765
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
93916
94766
  const projectKey = getProjectKey(config2, resolved.projectDir);
93917
94767
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
93918
- const perRunsDir = join83(outputDir, "runs");
94768
+ const perRunsDir = join87(outputDir, "runs");
93919
94769
  for (const runId of uniqueRunIds) {
93920
94770
  if (!keepSet.has(runId)) {
93921
- const runDir = join83(perRunsDir, runId);
93922
- await _curatorCmdDeps.removeFile(join83(runDir, "observations.jsonl"));
93923
- await _curatorCmdDeps.removeFile(join83(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"));
93924
94774
  }
93925
94775
  }
93926
94776
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -93963,9 +94813,9 @@ var init_curator2 = __esm(() => {
93963
94813
 
93964
94814
  // bin/nax.ts
93965
94815
  init_source();
93966
- import { existsSync as existsSync36, mkdirSync as mkdirSync7 } from "fs";
94816
+ import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
93967
94817
  import { homedir as homedir3 } from "os";
93968
- import { basename as basename16, join as join84 } from "path";
94818
+ import { basename as basename16, join as join88 } from "path";
93969
94819
 
93970
94820
  // node_modules/commander/esm.mjs
93971
94821
  var import__ = __toESM(require_commander(), 1);
@@ -93989,12 +94839,12 @@ init_errors();
93989
94839
  init_operations();
93990
94840
 
93991
94841
  // src/plan/strategies/context-builder.ts
93992
- import { join as join38 } from "path";
94842
+ import { join as join39 } from "path";
93993
94843
  init_config();
93994
94844
  init_errors();
93995
94845
  init_interaction();
93996
94846
  async function buildPlanModeContext(workdir, fullConfig, options, deps) {
93997
- const naxDir = join38(workdir, ".nax");
94847
+ const naxDir = join39(workdir, ".nax");
93998
94848
  if (!deps.existsSync(naxDir)) {
93999
94849
  throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
94000
94850
  stage: "plan",
@@ -94002,8 +94852,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94002
94852
  });
94003
94853
  }
94004
94854
  validateFeatureName(options.feature);
94005
- const outputDir = join38(naxDir, "features", options.feature);
94006
- const outputPath = join38(outputDir, "prd.json");
94855
+ const outputDir = join39(naxDir, "features", options.feature);
94856
+ const outputPath = join39(outputDir, "prd.json");
94007
94857
  const [specContent, sourceRoots, pkg] = await Promise.all([
94008
94858
  deps.readFile(options.from),
94009
94859
  deps.scanSourceRoots(workdir),
@@ -94018,7 +94868,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
94018
94868
  ...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
94019
94869
  ];
94020
94870
  const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
94021
- const packageJson = await deps.readPackageJsonAt(join38(workdir, relativePath, "package.json"));
94871
+ const packageJson = await deps.readPackageJsonAt(join39(workdir, relativePath, "package.json"));
94022
94872
  return buildPackageSummary(relativePath, packageJson);
94023
94873
  }));
94024
94874
  const projectName = detectProjectName(workdir, pkg);
@@ -94621,7 +95471,7 @@ init_interaction();
94621
95471
  init_prd();
94622
95472
  init_runtime();
94623
95473
  import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
94624
- 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";
94625
95475
  var _statusFeaturesDeps = {
94626
95476
  projectOutputDir,
94627
95477
  loadConfig
@@ -94635,7 +95485,7 @@ function isPidAlive(pid) {
94635
95485
  }
94636
95486
  }
94637
95487
  async function loadStatusFile(featureDir) {
94638
- const statusPath = join43(featureDir, "status.json");
95488
+ const statusPath = join44(featureDir, "status.json");
94639
95489
  if (!existsSync17(statusPath)) {
94640
95490
  return null;
94641
95491
  }
@@ -94650,7 +95500,7 @@ async function loadProjectStatusFile(projectDir) {
94650
95500
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
94651
95501
  const projectKey = config2?.name?.trim() || basename7(projectDir);
94652
95502
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
94653
- const statusPath = join43(outputDir, "status.json");
95503
+ const statusPath = join44(outputDir, "status.json");
94654
95504
  if (!existsSync17(statusPath)) {
94655
95505
  return null;
94656
95506
  }
@@ -94662,7 +95512,7 @@ async function loadProjectStatusFile(projectDir) {
94662
95512
  }
94663
95513
  }
94664
95514
  async function getFeatureSummary(featureName, featureDir) {
94665
- const prdPath = join43(featureDir, "prd.json");
95515
+ const prdPath = join44(featureDir, "prd.json");
94666
95516
  if (!existsSync17(prdPath)) {
94667
95517
  return {
94668
95518
  name: featureName,
@@ -94705,7 +95555,7 @@ async function getFeatureSummary(featureName, featureDir) {
94705
95555
  };
94706
95556
  }
94707
95557
  }
94708
- const runsDir = join43(featureDir, "runs");
95558
+ const runsDir = join44(featureDir, "runs");
94709
95559
  if (existsSync17(runsDir)) {
94710
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();
94711
95561
  if (runs.length > 0) {
@@ -94719,7 +95569,7 @@ async function displayAllFeatures(projectDir) {
94719
95569
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
94720
95570
  const projectKey = config2?.name?.trim() || basename7(projectDir);
94721
95571
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
94722
- const featuresDir = join43(outputDir, "features");
95572
+ const featuresDir = join44(outputDir, "features");
94723
95573
  if (!existsSync17(featuresDir)) {
94724
95574
  console.log(source_default.dim("No features found."));
94725
95575
  return;
@@ -94760,7 +95610,7 @@ async function displayAllFeatures(projectDir) {
94760
95610
  console.log();
94761
95611
  }
94762
95612
  }
94763
- 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))));
94764
95614
  console.log(source_default.bold(`\uD83D\uDCCA Features
94765
95615
  `));
94766
95616
  const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
@@ -94786,7 +95636,7 @@ async function displayAllFeatures(projectDir) {
94786
95636
  console.log();
94787
95637
  }
94788
95638
  async function displayFeatureDetails(featureName, featureDir) {
94789
- const prdPath = join43(featureDir, "prd.json");
95639
+ const prdPath = join44(featureDir, "prd.json");
94790
95640
  if (!existsSync17(prdPath)) {
94791
95641
  console.log(source_default.bold(`
94792
95642
  \uD83D\uDCCA ${featureName}
@@ -94932,7 +95782,7 @@ async function displayFeatureStatus(options = {}) {
94932
95782
  const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
94933
95783
  const projectKey = config2?.name?.trim() || basename7(projectDir);
94934
95784
  const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
94935
- featureDir = join43(outputDir, "features", options.feature);
95785
+ featureDir = join44(outputDir, "features", options.feature);
94936
95786
  } else {
94937
95787
  const resolved = resolveProject({ feature: options.feature });
94938
95788
  if (!resolved.featureDir) {
@@ -94952,7 +95802,7 @@ init_errors();
94952
95802
  init_logger2();
94953
95803
  init_runtime();
94954
95804
  import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
94955
- import { basename as basename8, join as join44 } from "path";
95805
+ import { basename as basename8, join as join45 } from "path";
94956
95806
  async function resolveOutputDir2(workdir, override) {
94957
95807
  if (override)
94958
95808
  return override;
@@ -94976,7 +95826,7 @@ async function runsListCommand(options) {
94976
95826
  const logger = getLogger();
94977
95827
  const { feature, workdir } = options;
94978
95828
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
94979
- const runsDir = join44(outputDir, "features", feature, "runs");
95829
+ const runsDir = join45(outputDir, "features", feature, "runs");
94980
95830
  if (!existsSync18(runsDir)) {
94981
95831
  logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
94982
95832
  return;
@@ -94988,7 +95838,7 @@ async function runsListCommand(options) {
94988
95838
  }
94989
95839
  logger.info("cli", `Runs for ${feature}`, { count: files.length });
94990
95840
  for (const file3 of files.sort().reverse()) {
94991
- const logPath = join44(runsDir, file3);
95841
+ const logPath = join45(runsDir, file3);
94992
95842
  const entries = await parseRunLog(logPath);
94993
95843
  const startEvent = entries.find((e) => e.message === "run.start");
94994
95844
  const completeEvent = entries.find((e) => e.message === "run.complete");
@@ -95015,7 +95865,7 @@ async function runsShowCommand(options) {
95015
95865
  const logger = getLogger();
95016
95866
  const { runId, feature, workdir } = options;
95017
95867
  const outputDir = await resolveOutputDir2(workdir, options.outputDir);
95018
- const logPath = join44(outputDir, "features", feature, "runs", `${runId}.jsonl`);
95868
+ const logPath = join45(outputDir, "features", feature, "runs", `${runId}.jsonl`);
95019
95869
  if (!existsSync18(logPath)) {
95020
95870
  logger.error("cli", "Run not found", { runId, feature, logPath });
95021
95871
  throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
@@ -95055,6 +95905,7 @@ async function runsShowCommand(options) {
95055
95905
  // src/cli/index.ts
95056
95906
  init_prompts2();
95057
95907
  init_init2();
95908
+ init_setup();
95058
95909
 
95059
95910
  // src/cli/plugins.ts
95060
95911
  init_paths();
@@ -95129,8 +95980,8 @@ init_interaction();
95129
95980
  init_source();
95130
95981
  init_loader();
95131
95982
  init_generator2();
95132
- import { existsSync as existsSync25 } from "fs";
95133
- import { join as join58 } from "path";
95983
+ import { existsSync as existsSync24 } from "fs";
95984
+ import { join as join62 } from "path";
95134
95985
  var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
95135
95986
  async function generateCommand(options) {
95136
95987
  const workdir = options.dir ?? process.cwd();
@@ -95173,7 +96024,7 @@ async function generateCommand(options) {
95173
96024
  return;
95174
96025
  }
95175
96026
  if (options.package) {
95176
- const packageDir = join58(workdir, options.package);
96027
+ const packageDir = join62(workdir, options.package);
95177
96028
  if (dryRun) {
95178
96029
  console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
95179
96030
  }
@@ -95193,10 +96044,10 @@ async function generateCommand(options) {
95193
96044
  process.exit(1);
95194
96045
  return;
95195
96046
  }
95196
- const contextPath = options.context ? join58(workdir, options.context) : join58(workdir, ".nax/context.md");
95197
- const outputDir = options.output ? join58(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;
95198
96049
  const autoInject = !options.noAutoInject;
95199
- if (!existsSync25(contextPath)) {
96050
+ if (!existsSync24(contextPath)) {
95200
96051
  console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
95201
96052
  console.error(source_default.yellow(" Create .nax/context.md first, or run `nax init` to scaffold it."));
95202
96053
  process.exit(1);
@@ -95299,8 +96150,8 @@ async function generateCommand(options) {
95299
96150
  }
95300
96151
  // src/cli/config-display.ts
95301
96152
  init_loader();
95302
- import { existsSync as existsSync27 } from "fs";
95303
- import { join as join60 } from "path";
96153
+ import { existsSync as existsSync26 } from "fs";
96154
+ import { join as join64 } from "path";
95304
96155
 
95305
96156
  // src/cli/config-descriptions.ts
95306
96157
  var FIELD_DESCRIPTIONS = {
@@ -95551,10 +96402,10 @@ function deepEqual(a, b) {
95551
96402
  // src/cli/config-get.ts
95552
96403
  init_defaults();
95553
96404
  init_loader();
95554
- import { existsSync as existsSync26 } from "fs";
95555
- import { join as join59 } from "path";
96405
+ import { existsSync as existsSync25 } from "fs";
96406
+ import { join as join63 } from "path";
95556
96407
  async function loadConfigFile(path18) {
95557
- if (!existsSync26(path18))
96408
+ if (!existsSync25(path18))
95558
96409
  return null;
95559
96410
  try {
95560
96411
  return await Bun.file(path18).json();
@@ -95574,7 +96425,7 @@ async function loadProjectConfig() {
95574
96425
  const projectDir = findProjectDir();
95575
96426
  if (!projectDir)
95576
96427
  return null;
95577
- const projectPath = join59(projectDir, "config.json");
96428
+ const projectPath = join63(projectDir, "config.json");
95578
96429
  return await loadConfigFile(projectPath);
95579
96430
  }
95580
96431
 
@@ -95634,14 +96485,14 @@ async function configCommand(config2, options = {}) {
95634
96485
  function determineConfigSources() {
95635
96486
  const globalPath = globalConfigPath();
95636
96487
  const projectDir = findProjectDir();
95637
- const projectPath = projectDir ? join60(projectDir, "config.json") : null;
96488
+ const projectPath = projectDir ? join64(projectDir, "config.json") : null;
95638
96489
  return {
95639
96490
  global: fileExists(globalPath) ? globalPath : null,
95640
96491
  project: projectPath && fileExists(projectPath) ? projectPath : null
95641
96492
  };
95642
96493
  }
95643
96494
  function fileExists(path18) {
95644
- return existsSync27(path18);
96495
+ return existsSync26(path18);
95645
96496
  }
95646
96497
  function displayConfigWithDescriptions(obj, path18, sources, indent = 0) {
95647
96498
  const indentStr = " ".repeat(indent);
@@ -95783,15 +96634,15 @@ init_paths();
95783
96634
  init_profile();
95784
96635
  import { mkdirSync as mkdirSync5 } from "fs";
95785
96636
  import { readdirSync as readdirSync5 } from "fs";
95786
- import { join as join61 } from "path";
96637
+ import { join as join65 } from "path";
95787
96638
  var _profileCLIDeps = {
95788
96639
  env: process.env
95789
96640
  };
95790
96641
  var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
95791
96642
  var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
95792
96643
  async function profileListCommand(startDir) {
95793
- const globalProfilesDir = join61(globalConfigDir(), "profiles");
95794
- const projectProfilesDir = join61(projectConfigDir(startDir), "profiles");
96644
+ const globalProfilesDir = join65(globalConfigDir(), "profiles");
96645
+ const projectProfilesDir = join65(projectConfigDir(startDir), "profiles");
95795
96646
  const globalProfiles = scanProfileDir(globalProfilesDir);
95796
96647
  const projectProfiles = scanProfileDir(projectProfilesDir);
95797
96648
  const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
@@ -95850,7 +96701,7 @@ function maskProfileValues(obj) {
95850
96701
  return result;
95851
96702
  }
95852
96703
  async function profileUseCommand(profileName, startDir) {
95853
- const configPath = join61(projectConfigDir(startDir), "config.json");
96704
+ const configPath = join65(projectConfigDir(startDir), "config.json");
95854
96705
  const configFile = Bun.file(configPath);
95855
96706
  let existing = {};
95856
96707
  if (await configFile.exists()) {
@@ -95869,8 +96720,8 @@ async function profileCurrentCommand(startDir) {
95869
96720
  return resolveProfileName({}, _profileCLIDeps.env, startDir);
95870
96721
  }
95871
96722
  async function profileCreateCommand(profileName, startDir) {
95872
- const profilesDir = join61(projectConfigDir(startDir), "profiles");
95873
- const profilePath = join61(profilesDir, `${profileName}.json`);
96723
+ const profilesDir = join65(projectConfigDir(startDir), "profiles");
96724
+ const profilePath = join65(profilesDir, `${profileName}.json`);
95874
96725
  const profileFile = Bun.file(profilePath);
95875
96726
  if (await profileFile.exists()) {
95876
96727
  throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
@@ -95991,8 +96842,8 @@ async function contextInspectCommand(options) {
95991
96842
  // src/cli/rules.ts
95992
96843
  init_canonical_loader();
95993
96844
  init_errors();
95994
- import { mkdir as mkdir12 } from "fs/promises";
95995
- import { basename as basename12, join as join62 } from "path";
96845
+ import { mkdir as mkdir11 } from "fs/promises";
96846
+ import { basename as basename12, join as join66 } from "path";
95996
96847
  var _rulesCLIDeps = {
95997
96848
  readFile: async (path18) => Bun.file(path18).text(),
95998
96849
  writeFile: async (path18, content) => {
@@ -96001,13 +96852,13 @@ var _rulesCLIDeps = {
96001
96852
  fileExists: async (path18) => Bun.file(path18).exists(),
96002
96853
  globInDir: (dir) => {
96003
96854
  try {
96004
- return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join62(dir, f));
96855
+ return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join66(dir, f));
96005
96856
  } catch {
96006
96857
  return [];
96007
96858
  }
96008
96859
  },
96009
96860
  mkdir: async (path18) => {
96010
- await mkdir12(path18, { recursive: true });
96861
+ await mkdir11(path18, { recursive: true });
96011
96862
  },
96012
96863
  globCanonicalRuleFiles: (workdir) => {
96013
96864
  try {
@@ -96050,7 +96901,7 @@ ${r.content}`).join(`
96050
96901
  `);
96051
96902
  const shimContent = `${header + body}
96052
96903
  `;
96053
- const shimPath = join62(workdir, shimFileName);
96904
+ const shimPath = join66(workdir, shimFileName);
96054
96905
  if (options.dryRun) {
96055
96906
  console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
96056
96907
  return;
@@ -96079,14 +96930,14 @@ function neutralizeContent(content) {
96079
96930
  }
96080
96931
  async function collectMigrationSources(workdir) {
96081
96932
  const sources = [];
96082
- const claudeMdPath = join62(workdir, "CLAUDE.md");
96933
+ const claudeMdPath = join66(workdir, "CLAUDE.md");
96083
96934
  if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
96084
96935
  const content = await _rulesCLIDeps.readFile(claudeMdPath);
96085
96936
  if (content.trim()) {
96086
96937
  sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
96087
96938
  }
96088
96939
  }
96089
- const rulesDir = join62(workdir, ".claude", "rules");
96940
+ const rulesDir = join66(workdir, ".claude", "rules");
96090
96941
  const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
96091
96942
  for (const filePath of ruleFiles) {
96092
96943
  try {
@@ -96106,7 +96957,7 @@ async function rulesMigrateCommand(options) {
96106
96957
  console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
96107
96958
  return;
96108
96959
  }
96109
- const targetDir = join62(workdir, CANONICAL_RULES_DIR);
96960
+ const targetDir = join66(workdir, CANONICAL_RULES_DIR);
96110
96961
  if (!options.dryRun) {
96111
96962
  try {
96112
96963
  await _rulesCLIDeps.mkdir(targetDir);
@@ -96117,7 +96968,7 @@ async function rulesMigrateCommand(options) {
96117
96968
  let written = 0;
96118
96969
  let skipped = 0;
96119
96970
  for (const { sourcePath, targetFileName, content } of sources) {
96120
- const targetPath = join62(targetDir, targetFileName);
96971
+ const targetPath = join66(targetDir, targetFileName);
96121
96972
  if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
96122
96973
  console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
96123
96974
  skipped++;
@@ -96156,7 +97007,7 @@ function collectCanonicalRuleRoots(workdir) {
96156
97007
  const packageRel = normalized.slice(0, idx);
96157
97008
  if (!packageRel)
96158
97009
  continue;
96159
- roots.add(join62(workdir, packageRel));
97010
+ roots.add(join66(workdir, packageRel));
96160
97011
  }
96161
97012
  return [...roots].sort();
96162
97013
  }
@@ -96178,7 +97029,7 @@ init_logger2();
96178
97029
  init_detect2();
96179
97030
  init_workspace();
96180
97031
  init_common();
96181
- import { join as join63 } from "path";
97032
+ import { join as join67 } from "path";
96182
97033
  function resolveEffective(detected, configPatterns) {
96183
97034
  if (configPatterns !== undefined)
96184
97035
  return "config";
@@ -96263,7 +97114,7 @@ async function detectCommand(options) {
96263
97114
  const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
96264
97115
  const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
96265
97116
  const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
96266
- const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
97117
+ const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
96267
97118
  const pkgRaw = await loadRawConfig(pkgConfigPath);
96268
97119
  const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
96269
97120
  const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
@@ -96317,13 +97168,13 @@ async function detectCommand(options) {
96317
97168
  if (rootDetected.confidence === "empty") {
96318
97169
  console.log(source_default.yellow(" root: skipped (empty detection)"));
96319
97170
  } else {
96320
- const rootConfigPath = join63(workdir, ".nax", "config.json");
97171
+ const rootConfigPath = join67(workdir, ".nax", "config.json");
96321
97172
  try {
96322
97173
  const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
96323
97174
  if (status === "skipped") {
96324
97175
  console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
96325
97176
  } else {
96326
- console.log(source_default.green(` root: ${status} \u2192 ${join63(".nax", "config.json")}`));
97177
+ console.log(source_default.green(` root: ${status} \u2192 ${join67(".nax", "config.json")}`));
96327
97178
  }
96328
97179
  } catch (err) {
96329
97180
  console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
@@ -96336,13 +97187,13 @@ async function detectCommand(options) {
96336
97187
  console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
96337
97188
  continue;
96338
97189
  }
96339
- const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
97190
+ const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
96340
97191
  try {
96341
97192
  const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
96342
97193
  if (status === "skipped") {
96343
97194
  console.log(source_default.dim(` ${dir}: skipped (already set)`));
96344
97195
  } else {
96345
- console.log(source_default.green(` ${dir}: ${status} \u2192 ${join63(".nax", "mono", dir, "config.json")}`));
97196
+ console.log(source_default.green(` ${dir}: ${status} \u2192 ${join67(".nax", "mono", dir, "config.json")}`));
96346
97197
  }
96347
97198
  } catch (err) {
96348
97199
  console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
@@ -96359,20 +97210,20 @@ async function detectCommand(options) {
96359
97210
 
96360
97211
  // src/commands/logs.ts
96361
97212
  init_common();
96362
- import { existsSync as existsSync29 } from "fs";
96363
- import { join as join67 } from "path";
97213
+ import { existsSync as existsSync28 } from "fs";
97214
+ import { join as join71 } from "path";
96364
97215
 
96365
97216
  // src/commands/logs-formatter.ts
96366
97217
  init_source();
96367
97218
  init_formatter();
96368
97219
  import { readdirSync as readdirSync7 } from "fs";
96369
- import { join as join66 } from "path";
97220
+ import { join as join70 } from "path";
96370
97221
 
96371
97222
  // src/commands/logs-reader.ts
96372
97223
  init_paths3();
96373
- import { existsSync as existsSync28, readdirSync as readdirSync6 } from "fs";
97224
+ import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
96374
97225
  import { readdir as readdir4 } from "fs/promises";
96375
- import { join as join65 } from "path";
97226
+ import { join as join69 } from "path";
96376
97227
  var _logsReaderDeps = {
96377
97228
  getRunsDir
96378
97229
  };
@@ -96386,7 +97237,7 @@ async function resolveRunFileFromRegistry(runId) {
96386
97237
  }
96387
97238
  let matched = null;
96388
97239
  for (const entry of entries) {
96389
- const metaPath = join65(runsDir, entry, "meta.json");
97240
+ const metaPath = join69(runsDir, entry, "meta.json");
96390
97241
  try {
96391
97242
  const meta3 = await Bun.file(metaPath).json();
96392
97243
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -96398,7 +97249,7 @@ async function resolveRunFileFromRegistry(runId) {
96398
97249
  if (!matched) {
96399
97250
  throw new Error(`Run not found in registry: ${runId}`);
96400
97251
  }
96401
- if (!existsSync28(matched.eventsDir)) {
97252
+ if (!existsSync27(matched.eventsDir)) {
96402
97253
  console.log(`Log directory unavailable for run: ${runId}`);
96403
97254
  return null;
96404
97255
  }
@@ -96408,14 +97259,14 @@ async function resolveRunFileFromRegistry(runId) {
96408
97259
  return null;
96409
97260
  }
96410
97261
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
96411
- return join65(matched.eventsDir, specificFile ?? files[0]);
97262
+ return join69(matched.eventsDir, specificFile ?? files[0]);
96412
97263
  }
96413
97264
  async function selectRunFile(runsDir) {
96414
97265
  const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
96415
97266
  if (files.length === 0) {
96416
97267
  return null;
96417
97268
  }
96418
- return join65(runsDir, files[0]);
97269
+ return join69(runsDir, files[0]);
96419
97270
  }
96420
97271
  async function extractRunSummary(filePath) {
96421
97272
  const file3 = Bun.file(filePath);
@@ -96501,7 +97352,7 @@ Runs:
96501
97352
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
96502
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"));
96503
97354
  for (const file3 of files) {
96504
- const filePath = join66(runsDir, file3);
97355
+ const filePath = join70(runsDir, file3);
96505
97356
  const summary = await extractRunSummary(filePath);
96506
97357
  const timestamp = file3.replace(".jsonl", "");
96507
97358
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -96615,7 +97466,7 @@ async function logsCommand(options) {
96615
97466
  return;
96616
97467
  }
96617
97468
  const resolved = resolveProject({ dir: options.dir });
96618
- const naxDir = join67(resolved.projectDir, ".nax");
97469
+ const naxDir = join71(resolved.projectDir, ".nax");
96619
97470
  const configPath = resolved.configPath;
96620
97471
  const configFile = Bun.file(configPath);
96621
97472
  const config2 = await configFile.json();
@@ -96623,9 +97474,9 @@ async function logsCommand(options) {
96623
97474
  if (!featureName) {
96624
97475
  throw new Error("No feature specified in config.json");
96625
97476
  }
96626
- const featureDir = join67(naxDir, "features", featureName);
96627
- const runsDir = join67(featureDir, "runs");
96628
- if (!existsSync29(runsDir)) {
97477
+ const featureDir = join71(naxDir, "features", featureName);
97478
+ const runsDir = join71(featureDir, "runs");
97479
+ if (!existsSync28(runsDir)) {
96629
97480
  throw new Error(`No runs directory found for feature: ${featureName}`);
96630
97481
  }
96631
97482
  if (options.list) {
@@ -96649,8 +97500,8 @@ init_config();
96649
97500
  init_prd();
96650
97501
  init_precheck();
96651
97502
  init_common();
96652
- import { existsSync as existsSync30 } from "fs";
96653
- import { join as join68 } from "path";
97503
+ import { existsSync as existsSync29 } from "fs";
97504
+ import { join as join72 } from "path";
96654
97505
  async function precheckCommand(options) {
96655
97506
  const resolved = resolveProject({
96656
97507
  dir: options.dir,
@@ -96672,14 +97523,14 @@ async function precheckCommand(options) {
96672
97523
  process.exit(1);
96673
97524
  }
96674
97525
  }
96675
- const naxDir = join68(resolved.projectDir, ".nax");
96676
- const featureDir = join68(naxDir, "features", featureName);
96677
- const prdPath = join68(featureDir, "prd.json");
96678
- if (!existsSync30(featureDir)) {
97526
+ const naxDir = join72(resolved.projectDir, ".nax");
97527
+ const featureDir = join72(naxDir, "features", featureName);
97528
+ const prdPath = join72(featureDir, "prd.json");
97529
+ if (!existsSync29(featureDir)) {
96679
97530
  console.error(source_default.red(`Feature not found: ${featureName}`));
96680
97531
  process.exit(1);
96681
97532
  }
96682
- if (!existsSync30(prdPath)) {
97533
+ if (!existsSync29(prdPath)) {
96683
97534
  console.error(source_default.red(`Missing prd.json for feature: ${featureName}`));
96684
97535
  console.error(source_default.dim(`Run: nax plan -f ${featureName} --from spec.md --auto`));
96685
97536
  process.exit(EXIT_CODES.INVALID_PRD);
@@ -96697,7 +97548,7 @@ async function precheckCommand(options) {
96697
97548
  init_source();
96698
97549
  init_paths3();
96699
97550
  import { readdir as readdir5 } from "fs/promises";
96700
- import { join as join69 } from "path";
97551
+ import { join as join73 } from "path";
96701
97552
  var DEFAULT_LIMIT = 20;
96702
97553
  var _runsCmdDeps = {
96703
97554
  getRunsDir
@@ -96752,7 +97603,7 @@ async function runsCommand(options = {}) {
96752
97603
  }
96753
97604
  const rows = [];
96754
97605
  for (const entry of entries) {
96755
- const metaPath = join69(runsDir, entry, "meta.json");
97606
+ const metaPath = join73(runsDir, entry, "meta.json");
96756
97607
  let meta3;
96757
97608
  try {
96758
97609
  meta3 = await Bun.file(metaPath).json();
@@ -96829,7 +97680,7 @@ async function runsCommand(options = {}) {
96829
97680
 
96830
97681
  // src/commands/unlock.ts
96831
97682
  init_source();
96832
- import { join as join70 } from "path";
97683
+ import { join as join74 } from "path";
96833
97684
  function isProcessAlive2(pid) {
96834
97685
  try {
96835
97686
  process.kill(pid, 0);
@@ -96844,7 +97695,7 @@ function formatLockAge(ageMs) {
96844
97695
  }
96845
97696
  async function unlockCommand(options) {
96846
97697
  const workdir = options.dir ?? process.cwd();
96847
- const lockPath = join70(workdir, "nax.lock");
97698
+ const lockPath = join74(workdir, "nax.lock");
96848
97699
  const lockFile = Bun.file(lockPath);
96849
97700
  const exists = await lockFile.exists();
96850
97701
  if (!exists) {
@@ -97392,6 +98243,7 @@ init_run_regression();
97392
98243
 
97393
98244
  // src/execution/index.ts
97394
98245
  init_story_orchestrator();
98246
+ init_story_orchestrator_logging();
97395
98247
  init_plan_inputs();
97396
98248
  init_build_plan_for_strategy();
97397
98249
  init_post_run();
@@ -105268,8 +106120,8 @@ Next: nax generate --package ${options.package}`));
105268
106120
  }
105269
106121
  return;
105270
106122
  }
105271
- const naxDir = join84(workdir, ".nax");
105272
- if (existsSync36(naxDir) && !options.force) {
106123
+ const naxDir = join88(workdir, ".nax");
106124
+ if (existsSync35(naxDir) && !options.force) {
105273
106125
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
105274
106126
  return;
105275
106127
  }
@@ -105297,11 +106149,11 @@ Next: nax generate --package ${options.package}`));
105297
106149
  }
105298
106150
  }
105299
106151
  }
105300
- mkdirSync7(join84(naxDir, "features"), { recursive: true });
105301
- mkdirSync7(join84(naxDir, "hooks"), { recursive: true });
106152
+ mkdirSync7(join88(naxDir, "features"), { recursive: true });
106153
+ mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
105302
106154
  const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
105303
- await Bun.write(join84(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
105304
- await Bun.write(join84(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({
105305
106157
  hooks: {
105306
106158
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
105307
106159
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -105309,12 +106161,12 @@ Next: nax generate --package ${options.package}`));
105309
106161
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
105310
106162
  }
105311
106163
  }, null, 2));
105312
- await Bun.write(join84(naxDir, ".gitignore"), `# nax temp files
106164
+ await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
105313
106165
  *.tmp
105314
106166
  .paused.json
105315
106167
  .nax-verifier-verdict.json
105316
106168
  `);
105317
- await Bun.write(join84(naxDir, "context.md"), `# Project Context
106169
+ await Bun.write(join88(naxDir, "context.md"), `# Project Context
105318
106170
 
105319
106171
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
105320
106172
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -105399,6 +106251,24 @@ Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cu
105399
106251
  console.log(source_default.dim(`
105400
106252
  Next: nax features create <name>`));
105401
106253
  });
106254
+ program2.command("setup").description("Analyze repo and generate .nax/config.json via LLM").option("-d, --dir <path>", "Project directory", process.cwd()).option("-a, --agent <name>", "Force a specific agent").option("--fill-scripts", "Add missing quality-gate scripts to package.json", false).option("--dry-run", "Preview planned config without writing files", false).option("--force", "Overwrite existing .nax/config.json", false).action(async (options) => {
106255
+ let workdir;
106256
+ try {
106257
+ workdir = validateDirectory(options.dir);
106258
+ } catch (err) {
106259
+ console.error(source_default.red(`Invalid directory: ${err.message}`));
106260
+ process.exit(1);
106261
+ }
106262
+ const { setupCommand: setupCommand2 } = await Promise.resolve().then(() => (init_setup(), exports_setup));
106263
+ const exitCode = await setupCommand2({
106264
+ dir: workdir,
106265
+ agent: options.agent,
106266
+ fillScripts: options.fillScripts,
106267
+ dryRun: options.dryRun,
106268
+ force: options.force
106269
+ });
106270
+ process.exit(exitCode);
106271
+ });
105402
106272
  program2.command("run").description("Run the orchestration loop for a feature").requiredOption("-f, --feature <name>", "Feature name").option("-a, --agent <name>", "Force a specific agent").option("-m, --max-iterations <n>", "Max iterations", "20").option("--dry-run", "Show plan without executing", false).option("--no-context", "Disable context builder (skip file context in prompts)").option("--no-batch", "Disable story batching (execute all stories individually)").option("--parallel <n>", "Max parallel sessions (0=auto, omit=sequential)").option("--plan", "Run plan phase first before execution", false).option("--from <spec-path>", "Path to spec file (required when --plan is used)").option("--one-shot", "Skip interactive planning Q&A, use single LLM call (ACP only)", false).option("--force", "Force overwrite existing prd.json when using --plan", false).option("--headless", "Force headless mode (disable TUI, use pipe mode)", false).option("--verbose", "Enable verbose logging (debug level)", false).option("--quiet", "Quiet mode (warnings and errors only)", false).option("--silent", "Silent mode (errors only)", false).option("--json", "JSON mode (raw JSONL output to stdout)", false).option("-d, --dir <path>", "Working directory", process.cwd()).option("--skip-precheck", "Skip precheck validations (advanced users only)", false).option("--profile <name>", "Profile to use (overrides config.json profile)").action(async (options) => {
105403
106273
  let workdir;
105404
106274
  try {
@@ -105411,7 +106281,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
105411
106281
  console.error(source_default.red("Error: --plan requires --from <spec-path>"));
105412
106282
  process.exit(1);
105413
106283
  }
105414
- if (options.from && !existsSync36(options.from)) {
106284
+ if (options.from && !existsSync35(options.from)) {
105415
106285
  console.error(source_default.red(`Error: File not found: ${options.from} (required with --plan)`));
105416
106286
  process.exit(1);
105417
106287
  }
@@ -105444,10 +106314,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
105444
106314
  console.error(source_default.red("nax not initialized. Run: nax init"));
105445
106315
  process.exit(1);
105446
106316
  }
105447
- const featureDir = join84(naxDir, "features", options.feature);
105448
- const prdPath = join84(featureDir, "prd.json");
106317
+ const featureDir = join88(naxDir, "features", options.feature);
106318
+ const prdPath = join88(featureDir, "prd.json");
105449
106319
  if (options.plan && options.from) {
105450
- if (existsSync36(prdPath) && !options.force) {
106320
+ if (existsSync35(prdPath) && !options.force) {
105451
106321
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
105452
106322
  console.error(source_default.dim(" Use --force to overwrite, or run without --plan to use the existing PRD."));
105453
106323
  process.exit(1);
@@ -105467,10 +106337,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
105467
106337
  }
105468
106338
  }
105469
106339
  try {
105470
- const planLogDir = join84(featureDir, "plan");
106340
+ const planLogDir = join88(featureDir, "plan");
105471
106341
  mkdirSync7(planLogDir, { recursive: true });
105472
106342
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
105473
- const planLogPath = join84(planLogDir, `${planLogId}.jsonl`);
106343
+ const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
105474
106344
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
105475
106345
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
105476
106346
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -105509,17 +106379,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
105509
106379
  process.exit(1);
105510
106380
  }
105511
106381
  }
105512
- if (!existsSync36(prdPath)) {
106382
+ if (!existsSync35(prdPath)) {
105513
106383
  console.error(source_default.red(`Feature "${options.feature}" not found or missing prd.json`));
105514
106384
  process.exit(1);
105515
106385
  }
105516
106386
  resetLogger();
105517
106387
  const projectKey = config2.name?.trim() || basename16(workdir);
105518
106388
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
105519
- const runsDir = join84(outputDir, "features", options.feature, "runs");
106389
+ const runsDir = join88(outputDir, "features", options.feature, "runs");
105520
106390
  mkdirSync7(runsDir, { recursive: true });
105521
106391
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
105522
- const logFilePath = join84(runsDir, `${runId}.jsonl`);
106392
+ const logFilePath = join88(runsDir, `${runId}.jsonl`);
105523
106393
  const isTTY = process.stdout.isTTY ?? false;
105524
106394
  const headlessFlag = options.headless ?? false;
105525
106395
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -105537,7 +106407,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
105537
106407
  config2.agent.default = options.agent;
105538
106408
  }
105539
106409
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
105540
- const globalNaxDir = join84(homedir3(), ".nax");
106410
+ const globalNaxDir = join88(homedir3(), ".nax");
105541
106411
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
105542
106412
  const eventEmitter = new PipelineEventEmitter;
105543
106413
  const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
@@ -105557,12 +106427,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
105557
106427
  events: eventEmitter,
105558
106428
  ptyOptions: null,
105559
106429
  agentStreamEvents,
105560
- queueFilePath: join84(workdir, ".queue.txt")
106430
+ queueFilePath: join88(workdir, ".queue.txt")
105561
106431
  });
105562
106432
  } else {
105563
106433
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
105564
106434
  }
105565
- const statusFilePath = join84(outputDir, "status.json");
106435
+ const statusFilePath = join88(outputDir, "status.json");
105566
106436
  let parallel;
105567
106437
  if (options.parallel !== undefined) {
105568
106438
  parallel = Number.parseInt(options.parallel, 10);
@@ -105589,9 +106459,9 @@ program2.command("run").description("Run the orchestration loop for a feature").
105589
106459
  skipPrecheck: options.skipPrecheck ?? false,
105590
106460
  agentStreamEvents
105591
106461
  });
105592
- const latestSymlink = join84(runsDir, "latest.jsonl");
106462
+ const latestSymlink = join88(runsDir, "latest.jsonl");
105593
106463
  try {
105594
- if (existsSync36(latestSymlink)) {
106464
+ if (existsSync35(latestSymlink)) {
105595
106465
  Bun.spawnSync(["rm", latestSymlink]);
105596
106466
  }
105597
106467
  Bun.spawnSync(["ln", "-s", `${runId}.jsonl`, latestSymlink], {
@@ -105650,9 +106520,9 @@ features.command("create <name>").description("Create a new feature").option("-d
105650
106520
  console.error(source_default.red("nax not initialized. Run: nax init"));
105651
106521
  process.exit(1);
105652
106522
  }
105653
- const featureDir = join84(naxDir, "features", name);
106523
+ const featureDir = join88(naxDir, "features", name);
105654
106524
  mkdirSync7(featureDir, { recursive: true });
105655
- await Bun.write(join84(featureDir, "spec.md"), `# Feature: ${name}
106525
+ await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
105656
106526
 
105657
106527
  ## Overview
105658
106528
 
@@ -105685,7 +106555,7 @@ features.command("create <name>").description("Create a new feature").option("-d
105685
106555
 
105686
106556
  <!-- What this feature explicitly does NOT cover. -->
105687
106557
  `);
105688
- await Bun.write(join84(featureDir, "progress.txt"), `# Progress: ${name}
106558
+ await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
105689
106559
 
105690
106560
  Created: ${new Date().toISOString()}
105691
106561
 
@@ -105711,8 +106581,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
105711
106581
  console.error(source_default.red("nax not initialized."));
105712
106582
  process.exit(1);
105713
106583
  }
105714
- const featuresDir = join84(naxDir, "features");
105715
- if (!existsSync36(featuresDir)) {
106584
+ const featuresDir = join88(naxDir, "features");
106585
+ if (!existsSync35(featuresDir)) {
105716
106586
  console.log(source_default.dim("No features yet."));
105717
106587
  return;
105718
106588
  }
@@ -105726,8 +106596,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
105726
106596
  Features:
105727
106597
  `));
105728
106598
  for (const name of entries) {
105729
- const prdPath = join84(featuresDir, name, "prd.json");
105730
- if (existsSync36(prdPath)) {
106599
+ const prdPath = join88(featuresDir, name, "prd.json");
106600
+ if (existsSync35(prdPath)) {
105731
106601
  const prd = await loadPRD(prdPath);
105732
106602
  const c = countStories(prd);
105733
106603
  console.log(` ${name} \u2014 ${c.passed}/${c.total} stories done`);
@@ -105761,10 +106631,10 @@ Use: nax plan -f <feature> --from <spec>`));
105761
106631
  cliOverrides.profile = options.profile;
105762
106632
  }
105763
106633
  const config2 = await loadConfig(workdir, cliOverrides);
105764
- const featureLogDir = join84(naxDir, "features", options.feature, "plan");
106634
+ const featureLogDir = join88(naxDir, "features", options.feature, "plan");
105765
106635
  mkdirSync7(featureLogDir, { recursive: true });
105766
106636
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
105767
- const planLogPath = join84(featureLogDir, `${planLogId}.jsonl`);
106637
+ const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
105768
106638
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
105769
106639
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
105770
106640
  try {