@nathapp/nax 0.70.1 → 0.70.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +571 -440
  2. package/package.json +6 -2
package/dist/nax.js CHANGED
@@ -17737,7 +17737,7 @@ var init_schema = __esm(() => {
17737
17737
  init_defaults();
17738
17738
  });
17739
17739
 
17740
- // src/logging/types.ts
17740
+ // src/log-format/types.ts
17741
17741
  var EMOJI;
17742
17742
  var init_types2 = __esm(() => {
17743
17743
  EMOJI = {
@@ -17759,7 +17759,7 @@ var init_types2 = __esm(() => {
17759
17759
  };
17760
17760
  });
17761
17761
 
17762
- // src/logging/formatter.ts
17762
+ // src/log-format/formatter.ts
17763
17763
  function formatTimestamp(isoTimestamp) {
17764
17764
  const date5 = new Date(isoTimestamp);
17765
17765
  return date5.toLocaleTimeString("en-US", {
@@ -18072,8 +18072,8 @@ var init_formatter = __esm(() => {
18072
18072
  ];
18073
18073
  });
18074
18074
 
18075
- // src/logging/index.ts
18076
- var init_logging = __esm(() => {
18075
+ // src/log-format/index.ts
18076
+ var init_log_format = __esm(() => {
18077
18077
  init_formatter();
18078
18078
  init_types2();
18079
18079
  });
@@ -18310,7 +18310,7 @@ function resetLogger() {
18310
18310
  }
18311
18311
  var LOG_LEVEL_PRIORITY, instance = null, noopLogger;
18312
18312
  var init_logger = __esm(() => {
18313
- init_logging();
18313
+ init_log_format();
18314
18314
  init_formatters();
18315
18315
  init_redact();
18316
18316
  LOG_LEVEL_PRIORITY = {
@@ -18963,6 +18963,19 @@ function rejectLegacyRectificationKeys(conf) {
18963
18963
  `);
18964
18964
  throw new NaxError(message, "CONFIG_LEGACY_RECTIFICATION_KEYS", { stage: "config", legacyKeys });
18965
18965
  }
18966
+ function rejectUnimplementedScopedProfile(conf) {
18967
+ const execution = conf.execution;
18968
+ if (execution?.permissionProfile !== "scoped")
18969
+ return;
18970
+ const message = [
18971
+ 'Invalid configuration \u2014 execution.permissionProfile: "scoped" is not yet implemented.',
18972
+ "The scoped (per-stage tool allowlist) profile is tracked by GitHub #374 and would",
18973
+ 'otherwise silently run as "safe", giving you weaker permissions than intended.',
18974
+ 'Use "unrestricted" or "safe" for now.'
18975
+ ].join(`
18976
+ `);
18977
+ throw new NaxError(message, "CONFIG_SCOPED_PROFILE_UNIMPLEMENTED", { stage: "config" });
18978
+ }
18966
18979
  function applyBatchModeCompat(conf) {
18967
18980
  const routing = conf.routing;
18968
18981
  const llm = routing?.llm;
@@ -19072,6 +19085,7 @@ async function loadConfig(startDir, cliOverrides) {
19072
19085
  }
19073
19086
  rejectLegacyAgentKeys(rawConfig);
19074
19087
  rejectLegacyRectificationKeys(rawConfig);
19088
+ rejectUnimplementedScopedProfile(rawConfig);
19075
19089
  const result = NaxConfigSchema.safeParse(rawConfig);
19076
19090
  if (!result.success) {
19077
19091
  const errors3 = result.error.issues.map((err) => {
@@ -19141,6 +19155,7 @@ async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
19141
19155
  rawMerged.profileChain = packageChain;
19142
19156
  rejectLegacyAgentKeys(rawMerged);
19143
19157
  rejectLegacyRectificationKeys(rawMerged);
19158
+ rejectUnimplementedScopedProfile(rawMerged);
19144
19159
  const result = NaxConfigSchema.safeParse(rawMerged);
19145
19160
  if (!result.success) {
19146
19161
  const errors3 = result.error.issues.map((err) => {
@@ -39398,6 +39413,7 @@ var init__finding_to_check = __esm(() => {
39398
39413
  function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
39399
39414
  const claimsAdversarial = opts.includeAdversarialReview === true;
39400
39415
  const adversarialReviewByFixTarget = opts.adversarialReviewByFixTarget;
39416
+ const blockingThreshold = opts.promptSeverityFloor ?? config2.review?.blockingThreshold;
39401
39417
  return {
39402
39418
  name: "autofix-implementer",
39403
39419
  appliesTo: (f) => (f.fixTarget === "source" || f.fixTarget == null) && IMPLEMENTER_SOURCES.has(f.source) || adversarialReviewByFixTarget !== undefined && f.source === "adversarial-review" && f.fixTarget === adversarialReviewByFixTarget || adversarialReviewByFixTarget === undefined && claimsAdversarial && f.source === "adversarial-review",
@@ -39405,7 +39421,8 @@ function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
39405
39421
  buildInput: (findings, _prior, _cycleCtx) => ({
39406
39422
  failedChecks: findingsToFailedChecks(findings),
39407
39423
  story,
39408
- findings
39424
+ findings,
39425
+ blockingThreshold
39409
39426
  }),
39410
39427
  extractApplied: (output) => {
39411
39428
  for (const decl of output.testEditDeclarations) {
@@ -39434,6 +39451,7 @@ var init_autofix_implementer_strategy = __esm(() => {
39434
39451
  // src/operations/autofix-test-writer-strategy.ts
39435
39452
  function makeAutofixTestWriterStrategy(story, config2, sink, opts = {}) {
39436
39453
  const includeAdversarial = opts.includeAdversarialReview !== false;
39454
+ const blockingThreshold = opts.promptSeverityFloor ?? config2.review?.blockingThreshold;
39437
39455
  return {
39438
39456
  name: "autofix-test-writer",
39439
39457
  appliesTo: (f) => f.fixTarget === "test" || includeAdversarial && f.source === "adversarial-review" || sink.mockHandoffs.length > 0,
@@ -39458,7 +39476,7 @@ function makeAutofixTestWriterStrategy(story, config2, sink, opts = {}) {
39458
39476
  failedChecks: findingsToFailedChecks(findings),
39459
39477
  story,
39460
39478
  mode: "mock-restructure",
39461
- blockingThreshold: config2.review?.blockingThreshold,
39479
+ blockingThreshold,
39462
39480
  handoffReason,
39463
39481
  handoffFiles
39464
39482
  };
@@ -39466,7 +39484,7 @@ function makeAutofixTestWriterStrategy(story, config2, sink, opts = {}) {
39466
39484
  return {
39467
39485
  failedChecks: findingsToFailedChecks(findings),
39468
39486
  story,
39469
- blockingThreshold: config2.review?.blockingThreshold
39487
+ blockingThreshold
39470
39488
  };
39471
39489
  },
39472
39490
  maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
@@ -45276,7 +45294,7 @@ function attachLoggingSubscriber(bus, runId) {
45276
45294
  offError();
45277
45295
  };
45278
45296
  }
45279
- var init_logging2 = __esm(() => {
45297
+ var init_logging = __esm(() => {
45280
45298
  init_logger2();
45281
45299
  });
45282
45300
 
@@ -45794,7 +45812,7 @@ var init_idle_watchdog = __esm(() => {
45794
45812
  // src/runtime/middleware/index.ts
45795
45813
  var init_middleware = __esm(() => {
45796
45814
  init_cancellation();
45797
- init_logging2();
45815
+ init_logging();
45798
45816
  init_agent_stream_logging();
45799
45817
  init_idle_watchdog();
45800
45818
  });
@@ -54979,159 +54997,66 @@ var init_non_blocking_fix = __esm(() => {
54979
54997
  };
54980
54998
  });
54981
54999
 
54982
- // src/execution/story-orchestrator-logging.ts
54983
- function formatPhaseResultMessage(opName, success2, stage, status) {
54984
- if (opName === "greenfield-gate") {
54985
- 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";
54986
- }
54987
- if (status === "skipped") {
54988
- return `Phase skipped: ${opName}`;
54989
- }
54990
- if (stage === "rectification") {
54991
- return `Rectification strategy completed: ${opName}`;
54992
- }
54993
- return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
54994
- }
54995
- function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
54996
- if (output === null || output === undefined || typeof output !== "object")
54997
- return null;
54998
- const r = output;
54999
- const success2 = r.success === true || r.passed === true;
55000
- const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
55001
- const status = typeof r.status === "string" ? r.status : undefined;
55002
- const data = { storyId, phase: opName, durationMs };
55003
- if (findingsCount !== undefined)
55004
- data.findingsCount = findingsCount;
55005
- if (status !== undefined)
55006
- data.status = status;
55007
- if (typeof r.failureCategory === "string")
55008
- data.failureCategory = r.failureCategory;
55009
- if (typeof r.reviewReason === "string")
55010
- data.reviewReason = r.reviewReason;
55011
- return { success: success2, data };
55012
- }
55013
- function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
55014
- if (isTddPhase)
55015
- return;
55016
- if (opName === "semantic-review" || opName === "adversarial-review")
55017
- return;
55018
- const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
55019
- if (!built)
55020
- return;
55021
- const { success: success2 } = built;
55022
- const data = { ...built.data, ...progressData };
55023
- const status = typeof built.data.status === "string" ? built.data.status : undefined;
55024
- const logger = getSafeLogger();
55025
- const message = formatPhaseResultMessage(opName, success2, stage, status);
55026
- if (stage === "rectification") {
55027
- logger?.info("story-orchestrator", message, data);
55028
- return;
55029
- }
55030
- if (success2) {
55031
- logger?.info("story-orchestrator", message, data);
55032
- } else {
55033
- logger?.warn("story-orchestrator", message, data);
55034
- }
55035
- }
55036
- var init_story_orchestrator_logging = __esm(() => {
55037
- init_logger2();
55000
+ // src/execution/story-orchestrator/types.ts
55001
+ var EXHAUSTED_EXIT_REASONS, TDD_OP_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES, STRICT_VERDICT_PHASE_NAMES;
55002
+ var init_types9 = __esm(() => {
55003
+ EXHAUSTED_EXIT_REASONS = new Set([
55004
+ "max-attempts-total",
55005
+ "max-attempts-per-strategy",
55006
+ "bail-when",
55007
+ "no-strategy",
55008
+ "agent-gave-up",
55009
+ "validate-short-circuit"
55010
+ ]);
55011
+ TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
55012
+ CANONICAL_ORDER = [
55013
+ "test-writer",
55014
+ "greenfield-gate",
55015
+ "implementer",
55016
+ "full-suite-gate",
55017
+ "verifier",
55018
+ "verify-scoped",
55019
+ "lint-check",
55020
+ "typecheck-check",
55021
+ "semantic-review",
55022
+ "adversarial-review"
55023
+ ];
55024
+ PHASE_KIND_TO_STATE_KEY = {
55025
+ "test-writer": "testWriter",
55026
+ "greenfield-gate": "greenfieldGate",
55027
+ implementer: "implementer",
55028
+ "full-suite-gate": "fullSuiteGate",
55029
+ verifier: "verifier",
55030
+ "verify-scoped": "verifyScoped",
55031
+ "lint-check": "lintCheck",
55032
+ "typecheck-check": "typecheckCheck",
55033
+ "semantic-review": "semanticReview",
55034
+ "adversarial-review": "adversarialReview"
55035
+ };
55036
+ STRATEGY_TO_REVALIDATION_PHASES = {
55037
+ "mechanical-lintfix": ["lint-check"],
55038
+ "mechanical-formatfix": ["lint-check"],
55039
+ "autofix-implementer": ["lint-check", "typecheck-check", "full-suite-gate", "semantic-review", "adversarial-review"],
55040
+ "autofix-test-writer": ["lint-check", "typecheck-check", "full-suite-gate", "adversarial-review"],
55041
+ "full-suite-rectify": [
55042
+ "lint-check",
55043
+ "typecheck-check",
55044
+ "full-suite-gate",
55045
+ "verifier",
55046
+ "verify-scoped",
55047
+ "semantic-review"
55048
+ ]
55049
+ };
55050
+ STRICT_VERDICT_PHASE_NAMES = new Set([
55051
+ "full-suite-gate",
55052
+ "verify-scoped",
55053
+ "lint-check",
55054
+ "typecheck-check",
55055
+ "verifier"
55056
+ ]);
55038
55057
  });
55039
55058
 
55040
- // src/execution/story-orchestrator.ts
55041
- async function refreshReviewInputForDispatch(opName, input) {
55042
- if (opName !== "semantic-review" && opName !== "adversarial-review")
55043
- return input;
55044
- const i = input;
55045
- const { _refresh } = i;
55046
- if (!_refresh || !i.workdir)
55047
- return input;
55048
- try {
55049
- if (opName === "semantic-review") {
55050
- const { _refresh: _, ...semInput } = input;
55051
- const fresh2 = await _storyOrchestratorDeps.prepareSemanticReviewInput({
55052
- workdir: semInput.workdir,
55053
- projectDir: _refresh.projectDir,
55054
- storyId: _refresh.storyId,
55055
- storyGitRef: _refresh.storyGitRef,
55056
- config: _refresh.config,
55057
- naxIgnoreIndex: _refresh.naxIgnoreIndex,
55058
- resolvedTestPatterns: _refresh.resolvedTestPatterns,
55059
- semanticConfig: semInput.semanticConfig
55060
- });
55061
- return {
55062
- ...semInput,
55063
- stat: fresh2.stat,
55064
- diff: fresh2.diff,
55065
- excludePatterns: fresh2.excludePatterns,
55066
- storyGitRef: fresh2.effectiveRef ?? semInput.storyGitRef
55067
- };
55068
- }
55069
- const { _refresh: __, ...advInput } = input;
55070
- const fresh = await _storyOrchestratorDeps.prepareAdversarialReviewInput({
55071
- workdir: advInput.workdir,
55072
- projectDir: _refresh.projectDir,
55073
- storyId: _refresh.storyId,
55074
- storyGitRef: _refresh.storyGitRef,
55075
- config: _refresh.config,
55076
- naxIgnoreIndex: _refresh.naxIgnoreIndex,
55077
- resolvedTestPatterns: _refresh.resolvedTestPatterns,
55078
- adversarialConfig: advInput.adversarialConfig
55079
- });
55080
- return {
55081
- ...advInput,
55082
- stat: fresh.stat,
55083
- diff: fresh.diff,
55084
- testInventory: fresh.testInventory,
55085
- excludePatterns: fresh.excludePatterns,
55086
- testGlobs: fresh.testGlobs,
55087
- refExcludePatterns: fresh.refExcludePatterns,
55088
- storyGitRef: fresh.effectiveRef ?? advInput.storyGitRef
55089
- };
55090
- } catch (err) {
55091
- getSafeLogger()?.warn("story-orchestrator", "review input refresh failed \u2014 dispatching with stale input", {
55092
- storyId: _refresh.storyId,
55093
- phase: opName,
55094
- error: errorMessage(err)
55095
- });
55096
- const { _refresh: _stripped, ...fallback } = input;
55097
- return fallback;
55098
- }
55099
- }
55100
- function isSlot(value) {
55101
- return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
55102
- }
55103
- function setPhase(state, kind, slot) {
55104
- const key = PHASE_KIND_TO_STATE_KEY[kind];
55105
- if (state[key] !== undefined) {
55106
- throw new NaxError(`StoryOrchestratorBuilder: addX was called twice for phase "${kind}"`, "ORCHESTRATOR_PHASE_DUPLICATE", { stage: "execution", kind });
55107
- }
55108
- state[key] = { kind, slot };
55109
- }
55110
- function collectOrderedPhases(state) {
55111
- return CANONICAL_ORDER.flatMap((kind) => {
55112
- if (kind === "test-writer" && state.testWriter)
55113
- return [state.testWriter];
55114
- if (kind === "greenfield-gate" && state.greenfieldGate)
55115
- return [state.greenfieldGate];
55116
- if (kind === "implementer" && state.implementer)
55117
- return [state.implementer];
55118
- if (kind === "full-suite-gate" && state.fullSuiteGate)
55119
- return [state.fullSuiteGate];
55120
- if (kind === "verifier" && state.verifier)
55121
- return [state.verifier];
55122
- if (kind === "verify-scoped" && state.verifyScoped)
55123
- return [state.verifyScoped];
55124
- if (kind === "lint-check" && state.lintCheck)
55125
- return [state.lintCheck];
55126
- if (kind === "typecheck-check" && state.typecheckCheck)
55127
- return [state.typecheckCheck];
55128
- if (kind === "semantic-review" && state.semanticReview)
55129
- return [state.semanticReview];
55130
- if (kind === "adversarial-review" && state.adversarialReview)
55131
- return [state.adversarialReview];
55132
- return [];
55133
- });
55134
- }
55059
+ // src/execution/story-orchestrator/phase-eval.ts
55135
55060
  function phaseExplicitlyPassed(output) {
55136
55061
  if (output === null || output === undefined || typeof output !== "object")
55137
55062
  return false;
@@ -55189,34 +55114,6 @@ function gateFailureKeys(gateOutput) {
55189
55114
  }
55190
55115
  return keys;
55191
55116
  }
55192
- function shouldSkipPhaseForRectification(phase, state, phaseOutputs) {
55193
- if (phase.kind !== "full-suite-gate")
55194
- return false;
55195
- const verifierName = state.verifier?.slot.op.name;
55196
- if (!verifierName)
55197
- return false;
55198
- return phaseExplicitlyPassed(phaseOutputs[verifierName]);
55199
- }
55200
- function gatherRectificationFindings(phaseOutputs, phases, state) {
55201
- const findings = [];
55202
- for (const phase of phases) {
55203
- if (shouldSkipPhaseForRectification(phase, state, phaseOutputs))
55204
- continue;
55205
- findings.push(...extractPhaseFindings(phaseOutputs[phase.slot.op.name]));
55206
- }
55207
- return findings;
55208
- }
55209
- function collectRectificationPhases(state) {
55210
- return [
55211
- state.fullSuiteGate,
55212
- state.verifier,
55213
- state.verifyScoped,
55214
- state.lintCheck,
55215
- state.typecheckCheck,
55216
- state.semanticReview,
55217
- state.adversarialReview
55218
- ].filter((phase) => phase !== undefined);
55219
- }
55220
55117
  function phasesToRevalidate(strategiesRun, allPhases) {
55221
55118
  if (!strategiesRun || strategiesRun.length === 0)
55222
55119
  return allPhases;
@@ -55236,6 +55133,111 @@ function orderGateLast(phases) {
55236
55133
  const gates = phases.filter((p) => p.kind === "full-suite-gate");
55237
55134
  return [...rest, ...gates];
55238
55135
  }
55136
+ var init_phase_eval = __esm(() => {
55137
+ init_logger2();
55138
+ init_types9();
55139
+ });
55140
+
55141
+ // src/execution/story-orchestrator/phase-state.ts
55142
+ function isSlot(value) {
55143
+ return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
55144
+ }
55145
+ function setPhase(state, kind, slot) {
55146
+ const key = PHASE_KIND_TO_STATE_KEY[kind];
55147
+ if (state[key] !== undefined) {
55148
+ throw new NaxError(`StoryOrchestratorBuilder: addX was called twice for phase "${kind}"`, "ORCHESTRATOR_PHASE_DUPLICATE", { stage: "execution", kind });
55149
+ }
55150
+ state[key] = { kind, slot };
55151
+ }
55152
+ function collectOrderedPhases(state) {
55153
+ return CANONICAL_ORDER.flatMap((kind) => {
55154
+ if (kind === "test-writer" && state.testWriter)
55155
+ return [state.testWriter];
55156
+ if (kind === "greenfield-gate" && state.greenfieldGate)
55157
+ return [state.greenfieldGate];
55158
+ if (kind === "implementer" && state.implementer)
55159
+ return [state.implementer];
55160
+ if (kind === "full-suite-gate" && state.fullSuiteGate)
55161
+ return [state.fullSuiteGate];
55162
+ if (kind === "verifier" && state.verifier)
55163
+ return [state.verifier];
55164
+ if (kind === "verify-scoped" && state.verifyScoped)
55165
+ return [state.verifyScoped];
55166
+ if (kind === "lint-check" && state.lintCheck)
55167
+ return [state.lintCheck];
55168
+ if (kind === "typecheck-check" && state.typecheckCheck)
55169
+ return [state.typecheckCheck];
55170
+ if (kind === "semantic-review" && state.semanticReview)
55171
+ return [state.semanticReview];
55172
+ if (kind === "adversarial-review" && state.adversarialReview)
55173
+ return [state.adversarialReview];
55174
+ return [];
55175
+ });
55176
+ }
55177
+ var init_phase_state = __esm(() => {
55178
+ init_errors();
55179
+ init_types9();
55180
+ });
55181
+
55182
+ // src/execution/story-orchestrator-logging.ts
55183
+ function formatPhaseResultMessage(opName, success2, stage, status) {
55184
+ if (opName === "greenfield-gate") {
55185
+ 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";
55186
+ }
55187
+ if (status === "skipped") {
55188
+ return `Phase skipped: ${opName}`;
55189
+ }
55190
+ if (stage === "rectification") {
55191
+ return `Rectification strategy completed: ${opName}`;
55192
+ }
55193
+ return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
55194
+ }
55195
+ function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
55196
+ if (output === null || output === undefined || typeof output !== "object")
55197
+ return null;
55198
+ const r = output;
55199
+ const success2 = r.success === true || r.passed === true;
55200
+ const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
55201
+ const status = typeof r.status === "string" ? r.status : undefined;
55202
+ const data = { storyId, phase: opName, durationMs };
55203
+ if (findingsCount !== undefined)
55204
+ data.findingsCount = findingsCount;
55205
+ if (status !== undefined)
55206
+ data.status = status;
55207
+ if (typeof r.failureCategory === "string")
55208
+ data.failureCategory = r.failureCategory;
55209
+ if (typeof r.reviewReason === "string")
55210
+ data.reviewReason = r.reviewReason;
55211
+ return { success: success2, data };
55212
+ }
55213
+ function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
55214
+ if (isTddPhase)
55215
+ return;
55216
+ if (opName === "semantic-review" || opName === "adversarial-review")
55217
+ return;
55218
+ const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
55219
+ if (!built)
55220
+ return;
55221
+ const { success: success2 } = built;
55222
+ const data = { ...built.data, ...progressData };
55223
+ const status = typeof built.data.status === "string" ? built.data.status : undefined;
55224
+ const logger = getSafeLogger();
55225
+ const message = formatPhaseResultMessage(opName, success2, stage, status);
55226
+ if (stage === "rectification") {
55227
+ logger?.info("story-orchestrator", message, data);
55228
+ return;
55229
+ }
55230
+ if (success2) {
55231
+ logger?.info("story-orchestrator", message, data);
55232
+ } else {
55233
+ logger?.warn("story-orchestrator", message, data);
55234
+ }
55235
+ }
55236
+ var init_story_orchestrator_logging = __esm(() => {
55237
+ init_logger2();
55238
+ });
55239
+
55240
+ // src/execution/story-orchestrator/review-decision.ts
55239
55241
  function toReviewDecisionPayload(opName, output) {
55240
55242
  if (output === null || output === undefined || typeof output !== "object")
55241
55243
  return null;
@@ -55351,6 +55353,70 @@ function logUnifiedReviewPhaseResult(storyId, opName, output) {
55351
55353
  truncated: findingsCount > findingsSummary.length
55352
55354
  });
55353
55355
  }
55356
+ var init_review_decision = __esm(() => {
55357
+ init_logger2();
55358
+ });
55359
+
55360
+ // src/execution/story-orchestrator/run-phase.ts
55361
+ async function refreshReviewInputForDispatch(opName, input) {
55362
+ if (opName !== "semantic-review" && opName !== "adversarial-review")
55363
+ return input;
55364
+ const i = input;
55365
+ const { _refresh } = i;
55366
+ if (!_refresh || !i.workdir)
55367
+ return input;
55368
+ try {
55369
+ if (opName === "semantic-review") {
55370
+ const { _refresh: _, ...semInput } = input;
55371
+ const fresh2 = await _storyOrchestratorDeps.prepareSemanticReviewInput({
55372
+ workdir: semInput.workdir,
55373
+ projectDir: _refresh.projectDir,
55374
+ storyId: _refresh.storyId,
55375
+ storyGitRef: _refresh.storyGitRef,
55376
+ config: _refresh.config,
55377
+ naxIgnoreIndex: _refresh.naxIgnoreIndex,
55378
+ resolvedTestPatterns: _refresh.resolvedTestPatterns,
55379
+ semanticConfig: semInput.semanticConfig
55380
+ });
55381
+ return {
55382
+ ...semInput,
55383
+ stat: fresh2.stat,
55384
+ diff: fresh2.diff,
55385
+ excludePatterns: fresh2.excludePatterns,
55386
+ storyGitRef: fresh2.effectiveRef ?? semInput.storyGitRef
55387
+ };
55388
+ }
55389
+ const { _refresh: __, ...advInput } = input;
55390
+ const fresh = await _storyOrchestratorDeps.prepareAdversarialReviewInput({
55391
+ workdir: advInput.workdir,
55392
+ projectDir: _refresh.projectDir,
55393
+ storyId: _refresh.storyId,
55394
+ storyGitRef: _refresh.storyGitRef,
55395
+ config: _refresh.config,
55396
+ naxIgnoreIndex: _refresh.naxIgnoreIndex,
55397
+ resolvedTestPatterns: _refresh.resolvedTestPatterns,
55398
+ adversarialConfig: advInput.adversarialConfig
55399
+ });
55400
+ return {
55401
+ ...advInput,
55402
+ stat: fresh.stat,
55403
+ diff: fresh.diff,
55404
+ testInventory: fresh.testInventory,
55405
+ excludePatterns: fresh.excludePatterns,
55406
+ testGlobs: fresh.testGlobs,
55407
+ refExcludePatterns: fresh.refExcludePatterns,
55408
+ storyGitRef: fresh.effectiveRef ?? advInput.storyGitRef
55409
+ };
55410
+ } catch (err) {
55411
+ getSafeLogger()?.warn("story-orchestrator", "review input refresh failed \u2014 dispatching with stale input", {
55412
+ storyId: _refresh.storyId,
55413
+ phase: opName,
55414
+ error: errorMessage(err)
55415
+ });
55416
+ const { _refresh: _stripped, ...fallback } = input;
55417
+ return fallback;
55418
+ }
55419
+ }
55354
55420
  async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = false, progress) {
55355
55421
  const logger = getSafeLogger();
55356
55422
  const opName = slot.op.name;
@@ -55436,6 +55502,57 @@ function withIncreasingFailuresBail(strategies, enabled, consecutiveIncreases) {
55436
55502
  }
55437
55503
  }));
55438
55504
  }
55505
+ var _storyOrchestratorDeps;
55506
+ var init_run_phase = __esm(() => {
55507
+ init_findings();
55508
+ init_logger2();
55509
+ init_operations();
55510
+ init_pipeline();
55511
+ init_review();
55512
+ init_git();
55513
+ init_non_blocking_fix();
55514
+ init_story_orchestrator_logging();
55515
+ init_review_decision();
55516
+ init_types9();
55517
+ _storyOrchestratorDeps = {
55518
+ callOp,
55519
+ runFixCycle,
55520
+ captureGitRef,
55521
+ prepareSemanticReviewInput,
55522
+ prepareAdversarialReviewInput,
55523
+ runNonBlockingFix
55524
+ };
55525
+ });
55526
+
55527
+ // src/execution/story-orchestrator/rectification.ts
55528
+ function shouldSkipPhaseForRectification(phase, state, phaseOutputs) {
55529
+ if (phase.kind !== "full-suite-gate")
55530
+ return false;
55531
+ const verifierName = state.verifier?.slot.op.name;
55532
+ if (!verifierName)
55533
+ return false;
55534
+ return phaseExplicitlyPassed(phaseOutputs[verifierName]);
55535
+ }
55536
+ function gatherRectificationFindings(phaseOutputs, phases, state) {
55537
+ const findings = [];
55538
+ for (const phase of phases) {
55539
+ if (shouldSkipPhaseForRectification(phase, state, phaseOutputs))
55540
+ continue;
55541
+ findings.push(...extractPhaseFindings(phaseOutputs[phase.slot.op.name]));
55542
+ }
55543
+ return findings;
55544
+ }
55545
+ function collectRectificationPhases(state) {
55546
+ return [
55547
+ state.fullSuiteGate,
55548
+ state.verifier,
55549
+ state.verifyScoped,
55550
+ state.lintCheck,
55551
+ state.typecheckCheck,
55552
+ state.semanticReview,
55553
+ state.adversarialReview
55554
+ ].filter((phase) => phase !== undefined);
55555
+ }
55439
55556
  async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides) {
55440
55557
  const rectification = state.rectification;
55441
55558
  const baseValidationPhases = collectRectificationPhases(state);
@@ -55533,7 +55650,15 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides)
55533
55650
  }
55534
55651
  return {};
55535
55652
  }
55653
+ var init_rectification = __esm(() => {
55654
+ init_logger2();
55655
+ init_phase_eval();
55656
+ init_phase_eval();
55657
+ init_run_phase();
55658
+ init_types9();
55659
+ });
55536
55660
 
55661
+ // src/execution/story-orchestrator/execution-plan.ts
55537
55662
  class ExecutionPlan {
55538
55663
  ctx;
55539
55664
  state;
@@ -55687,18 +55812,29 @@ class ExecutionPlan {
55687
55812
  } else if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName], this.ctx.storyId)) {
55688
55813
  logger?.warn("story-orchestrator", "Full-suite gate failed but verifier judged story OK \u2014 treating gate failures as unrelated regressions", { storyId: this.ctx.storyId, packageDir: this.ctx.packageDir });
55689
55814
  }
55690
- const success2 = Object.entries(phaseOutputs).every(([name, output]) => {
55815
+ const requiredReviewPhaseNames = [
55816
+ this.state.semanticReview?.slot.op.name,
55817
+ this.state.adversarialReview?.slot.op.name
55818
+ ].filter((name) => name !== undefined);
55819
+ const missingRequiredReviewPhases = requiredReviewPhaseNames.filter((name) => !(name in phaseOutputs));
55820
+ if (missingRequiredReviewPhases.length > 0) {
55821
+ logger?.warn("story-orchestrator", "Configured review phase(s) never ran \u2014 story cannot pass without review judgment, failing for escalation", { storyId: this.ctx.storyId, packageDir: this.ctx.packageDir, missingRequiredReviewPhases });
55822
+ }
55823
+ const success2 = missingRequiredReviewPhases.length === 0 && Object.entries(phaseOutputs).every(([name, output]) => {
55691
55824
  if (verifierPassedSsot && name === gateName)
55692
55825
  return true;
55693
55826
  return phasePassed(name, output, this.ctx.storyId);
55694
55827
  });
55695
55828
  const totalCostUsd = Object.values(phaseCosts).reduce((sum, cost) => sum + cost, 0);
55696
55829
  const durationMs = Date.now() - startedAt;
55697
- const failedPhases = Object.entries(phaseOutputs).filter(([name, output]) => {
55698
- if (verifierPassedSsot && name === gateName)
55699
- return false;
55700
- return !phasePassed(name, output, this.ctx.storyId);
55701
- }).map(([name]) => name);
55830
+ const failedPhases = [
55831
+ ...Object.entries(phaseOutputs).filter(([name, output]) => {
55832
+ if (verifierPassedSsot && name === gateName)
55833
+ return false;
55834
+ return !phasePassed(name, output, this.ctx.storyId);
55835
+ }).map(([name]) => name),
55836
+ ...missingRequiredReviewPhases.map((name) => `${name} (never ran)`)
55837
+ ];
55702
55838
  const summary = {
55703
55839
  storyId: this.ctx.storyId,
55704
55840
  success: success2,
@@ -55711,6 +55847,8 @@ class ExecutionPlan {
55711
55847
  summary.rectificationExhausted = true;
55712
55848
  if (rectResult.unfixedFindings)
55713
55849
  summary.unfixedFindingsCount = rectResult.unfixedFindings.length;
55850
+ if (missingRequiredReviewPhases.length > 0)
55851
+ summary.missingRequiredReviewPhases = missingRequiredReviewPhases;
55714
55852
  if (success2) {
55715
55853
  logger?.info("story-orchestrator", "Story orchestration complete", summary);
55716
55854
  } else {
@@ -55723,11 +55861,21 @@ class ExecutionPlan {
55723
55861
  durationMs,
55724
55862
  phaseOutputs,
55725
55863
  ...rectResult,
55726
- gateRegressedDuringRect
55864
+ gateRegressedDuringRect,
55865
+ missingRequiredReviewPhases: missingRequiredReviewPhases.length > 0 ? missingRequiredReviewPhases : undefined
55727
55866
  };
55728
55867
  }
55729
55868
  }
55869
+ var init_execution_plan = __esm(() => {
55870
+ init_logger2();
55871
+ init_non_blocking_fix();
55872
+ init_phase_eval();
55873
+ init_phase_state();
55874
+ init_rectification();
55875
+ init_run_phase();
55876
+ });
55730
55877
 
55878
+ // src/execution/story-orchestrator/builder.ts
55731
55879
  class StoryOrchestratorBuilder {
55732
55880
  state = {};
55733
55881
  addImplementer(value) {
@@ -55787,80 +55935,21 @@ class StoryOrchestratorBuilder {
55787
55935
  return new ExecutionPlan(ctx, { ...this.state }, opts.isThreeSession ?? false);
55788
55936
  }
55789
55937
  }
55790
- var _storyOrchestratorDeps, EXHAUSTED_EXIT_REASONS, TDD_OP_NAMES, STRICT_VERDICT_PHASE_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
55791
- var init_story_orchestrator = __esm(() => {
55938
+ var init_builder2 = __esm(() => {
55792
55939
  init_errors();
55793
- init_findings();
55794
- init_logger2();
55795
55940
  init_operations();
55796
- init_call();
55797
- init_event_bus();
55798
- init_prepare_inputs();
55799
- init_git();
55800
- init_non_blocking_fix();
55801
- init_story_orchestrator_logging();
55802
- _storyOrchestratorDeps = {
55803
- callOp,
55804
- runFixCycle,
55805
- captureGitRef,
55806
- prepareSemanticReviewInput,
55807
- prepareAdversarialReviewInput,
55808
- runNonBlockingFix
55809
- };
55810
- EXHAUSTED_EXIT_REASONS = new Set([
55811
- "max-attempts-total",
55812
- "max-attempts-per-strategy",
55813
- "bail-when",
55814
- "no-strategy",
55815
- "agent-gave-up",
55816
- "validate-short-circuit"
55817
- ]);
55818
- TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
55819
- STRICT_VERDICT_PHASE_NAMES = new Set([
55820
- fullSuiteGateOp.name,
55821
- verifyScopedOp.name,
55822
- lintCheckOp.name,
55823
- typecheckCheckOp.name,
55824
- verifierOp.name
55825
- ]);
55826
- CANONICAL_ORDER = [
55827
- "test-writer",
55828
- "greenfield-gate",
55829
- "implementer",
55830
- "full-suite-gate",
55831
- "verifier",
55832
- "verify-scoped",
55833
- "lint-check",
55834
- "typecheck-check",
55835
- "semantic-review",
55836
- "adversarial-review"
55837
- ];
55838
- PHASE_KIND_TO_STATE_KEY = {
55839
- "test-writer": "testWriter",
55840
- "greenfield-gate": "greenfieldGate",
55841
- implementer: "implementer",
55842
- "full-suite-gate": "fullSuiteGate",
55843
- verifier: "verifier",
55844
- "verify-scoped": "verifyScoped",
55845
- "lint-check": "lintCheck",
55846
- "typecheck-check": "typecheckCheck",
55847
- "semantic-review": "semanticReview",
55848
- "adversarial-review": "adversarialReview"
55849
- };
55850
- STRATEGY_TO_REVALIDATION_PHASES = {
55851
- "mechanical-lintfix": ["lint-check"],
55852
- "mechanical-formatfix": ["lint-check"],
55853
- "autofix-implementer": ["lint-check", "typecheck-check", "full-suite-gate", "semantic-review", "adversarial-review"],
55854
- "autofix-test-writer": ["lint-check", "typecheck-check", "full-suite-gate", "adversarial-review"],
55855
- "full-suite-rectify": [
55856
- "lint-check",
55857
- "typecheck-check",
55858
- "full-suite-gate",
55859
- "verifier",
55860
- "verify-scoped",
55861
- "semantic-review"
55862
- ]
55863
- };
55941
+ init_execution_plan();
55942
+ init_phase_state();
55943
+ });
55944
+
55945
+ // src/execution/story-orchestrator/index.ts
55946
+ var init_story_orchestrator = __esm(() => {
55947
+ init_builder2();
55948
+ init_execution_plan();
55949
+ init_phase_eval();
55950
+ init_rectification();
55951
+ init_run_phase();
55952
+ init_types9();
55864
55953
  });
55865
55954
 
55866
55955
  // src/execution/build-plan-for-strategy.ts
@@ -55961,18 +56050,24 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
55961
56050
  const nbSink = makeDeclarationSink();
55962
56051
  if (nbf.scope === "source") {
55963
56052
  nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
55964
- includeAdversarialReview: true
56053
+ includeAdversarialReview: true,
56054
+ promptSeverityFloor: "info"
55965
56055
  }));
55966
56056
  } else if (nbf.scope === "triage") {
55967
56057
  nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
55968
- adversarialReviewByFixTarget: "source"
56058
+ adversarialReviewByFixTarget: "source",
56059
+ promptSeverityFloor: "info"
55969
56060
  }), makeAutofixTestWriterStrategy(story, config2, nbSink, {
55970
- includeAdversarialReview: false
56061
+ includeAdversarialReview: false,
56062
+ promptSeverityFloor: "info"
55971
56063
  }));
55972
56064
  } else {
55973
56065
  nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
55974
- includeAdversarialReview: false
55975
- }), makeAutofixTestWriterStrategy(story, config2, nbSink));
56066
+ includeAdversarialReview: false,
56067
+ promptSeverityFloor: "info"
56068
+ }), makeAutofixTestWriterStrategy(story, config2, nbSink, {
56069
+ promptSeverityFloor: "info"
56070
+ }));
55976
56071
  }
55977
56072
  nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2, nbSink));
55978
56073
  const nbPostValidate = async (findings, _validateCtx) => {
@@ -56247,7 +56342,7 @@ function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason, failure
56247
56342
  }
56248
56343
  return { action: "escalate", reason: buildReason("isolation-violation") };
56249
56344
  }
56250
- if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "full-suite-gate-exhausted" || failureCategory === "verifier-rejected" || failureCategory === "runtime-crash") {
56345
+ if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "full-suite-gate-exhausted" || failureCategory === "verifier-rejected" || failureCategory === "runtime-crash" || failureCategory === "review-incomplete") {
56251
56346
  return { action: "escalate", reason: buildReason(failureCategory) };
56252
56347
  }
56253
56348
  if (failureCategory === "greenfield-no-tests") {
@@ -56346,22 +56441,8 @@ async function closeAllRunSessions(sessionManager, agentGetFn, opts) {
56346
56441
  return totalClosed;
56347
56442
  }
56348
56443
 
56349
- // src/execution/post-run.ts
56350
- function shouldRollbackTddFailure(tddMode, failureCategory) {
56351
- return tddMode?.rollbackEnabled === true && failureCategory === "isolation-violation";
56352
- }
56353
- function extractPauseReason(phaseOutputs) {
56354
- for (const output of Object.values(phaseOutputs)) {
56355
- if (output !== null && typeof output === "object") {
56356
- const record2 = output;
56357
- if (typeof record2.pauseReason === "string" && record2.pauseReason) {
56358
- return record2.pauseReason;
56359
- }
56360
- }
56361
- }
56362
- return;
56363
- }
56364
- function deriveTddFailureCategory(phaseOutputs, unfixedFindings, gateRegressedDuringRect) {
56444
+ // src/execution/tdd-failure-category.ts
56445
+ function deriveTddFailureCategory(phaseOutputs, unfixedFindings, gateRegressedDuringRect, missingRequiredReviewPhases) {
56365
56446
  const testWriterOutput = phaseOutputs[testWriterOp.name];
56366
56447
  if (testWriterOutput?.success === false) {
56367
56448
  return "session-failure";
@@ -56400,6 +56481,29 @@ function deriveTddFailureCategory(phaseOutputs, unfixedFindings, gateRegressedDu
56400
56481
  if (implOutput?.success === false) {
56401
56482
  return "session-failure";
56402
56483
  }
56484
+ if (missingRequiredReviewPhases && missingRequiredReviewPhases.length > 0) {
56485
+ return "review-incomplete";
56486
+ }
56487
+ return;
56488
+ }
56489
+ var init_tdd_failure_category = __esm(() => {
56490
+ init_operations();
56491
+ init_story_orchestrator();
56492
+ });
56493
+
56494
+ // src/execution/post-run.ts
56495
+ function shouldRollbackTddFailure(tddMode, failureCategory) {
56496
+ return tddMode?.rollbackEnabled === true && failureCategory === "isolation-violation";
56497
+ }
56498
+ function extractPauseReason(phaseOutputs) {
56499
+ for (const output of Object.values(phaseOutputs)) {
56500
+ if (output !== null && typeof output === "object") {
56501
+ const record2 = output;
56502
+ if (typeof record2.pauseReason === "string" && record2.pauseReason) {
56503
+ return record2.pauseReason;
56504
+ }
56505
+ }
56506
+ }
56403
56507
  return;
56404
56508
  }
56405
56509
  async function cleanupSessionOnFailure(ctx) {
@@ -56497,7 +56601,7 @@ async function applyPostRunInspection(ctx, planResult, opts) {
56497
56601
  }
56498
56602
  }
56499
56603
  const pauseReason = extractPauseReason(planResult.phaseOutputs);
56500
- const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs, planResult.unfixedFindings, planResult.gateRegressedDuringRect) : undefined;
56604
+ const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs, planResult.unfixedFindings, planResult.gateRegressedDuringRect, planResult.missingRequiredReviewPhases) : undefined;
56501
56605
  if (isTdd && !planResult.success && !failureCategory) {
56502
56606
  const phaseSignals = {};
56503
56607
  for (const [name, output] of Object.entries(planResult.phaseOutputs)) {
@@ -56716,7 +56820,7 @@ var init_post_run = __esm(() => {
56716
56820
  init_scratch_writer();
56717
56821
  init_rollback();
56718
56822
  init_git();
56719
- init_story_orchestrator();
56823
+ init_tdd_failure_category();
56720
56824
  _postRunDeps = {
56721
56825
  detectMergeConflict,
56722
56826
  checkMergeConflict,
@@ -58853,17 +58957,46 @@ var init_setup_verify = __esm(() => {
58853
58957
  };
58854
58958
  });
58855
58959
 
58960
+ // src/cli/setup-write.ts
58961
+ import { join as join57 } from "path";
58962
+ async function writeSetupConfig(workdir, config2, monoConfigs, _opts, deps = _writeSetupDeps) {
58963
+ const naxDir = join57(workdir, ".nax");
58964
+ const naxConfigPath = join57(naxDir, "config.json");
58965
+ const written = [];
58966
+ await deps.mkdir(naxDir);
58967
+ await deps.writeFile(naxConfigPath, JSON.stringify(config2, null, 2));
58968
+ written.push(naxConfigPath);
58969
+ for (const mc of monoConfigs) {
58970
+ const monoDir = join57(naxDir, "mono", mc.relativeDir);
58971
+ const monoConfigPath = join57(monoDir, "config.json");
58972
+ await deps.mkdir(monoDir);
58973
+ await deps.writeFile(monoConfigPath, JSON.stringify(mc.config, null, 2));
58974
+ written.push(monoConfigPath);
58975
+ }
58976
+ return { written };
58977
+ }
58978
+ var _writeSetupDeps;
58979
+ var init_setup_write = __esm(() => {
58980
+ _writeSetupDeps = {
58981
+ writeFile: (path14, content) => Bun.write(path14, content).then(() => {}),
58982
+ mkdir: async (path14) => {
58983
+ const proc = Bun.spawn(["mkdir", "-p", path14]);
58984
+ await proc.exited;
58985
+ }
58986
+ };
58987
+ });
58988
+
58856
58989
  // src/cli/setup.ts
58857
58990
  var exports_setup = {};
58858
58991
  __export(exports_setup, {
58859
58992
  setupCommand: () => setupCommand,
58860
58993
  _setupDeps: () => _setupDeps
58861
58994
  });
58862
- import { join as join57 } from "path";
58995
+ import { join as join58 } from "path";
58863
58996
  async function setupCommand(options = {}) {
58864
58997
  const workdir = options.dir ?? process.cwd();
58865
- const naxDir = join57(workdir, ".nax");
58866
- const naxConfigPath = join57(naxDir, "config.json");
58998
+ const naxDir = join58(workdir, ".nax");
58999
+ const naxConfigPath = join58(naxDir, "config.json");
58867
59000
  const exists = await _setupDeps.fileExists(naxConfigPath);
58868
59001
  if (exists && !options.force) {
58869
59002
  _setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
@@ -58893,13 +59026,7 @@ ${JSON.stringify(plan.config, null, 2)}`);
58893
59026
  if (options.fillScripts) {
58894
59027
  await _setupDeps.fillScripts(workdir, analysis);
58895
59028
  }
58896
- await _setupDeps.mkdir(naxDir);
58897
- await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
58898
- for (const mc of plan.monoConfigs) {
58899
- const monoDir = join57(naxDir, "mono", mc.relativeDir);
58900
- await _setupDeps.mkdir(monoDir);
58901
- await _setupDeps.writeFile(join57(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
58902
- }
59029
+ await _setupDeps.writeSetupConfig(workdir, plan.config, plan.monoConfigs, { force: options.force });
58903
59030
  const gateResult = await _setupDeps.runGate(workdir, plan.config);
58904
59031
  if (gateResult !== 0) {
58905
59032
  return gateResult;
@@ -58916,6 +59043,7 @@ var init_setup = __esm(() => {
58916
59043
  init_setup_fill();
58917
59044
  init_setup_llm();
58918
59045
  init_setup_verify();
59046
+ init_setup_write();
58919
59047
  _setupDeps = {
58920
59048
  analyzeRepo,
58921
59049
  fillScripts,
@@ -58937,11 +59065,7 @@ var init_setup = __esm(() => {
58937
59065
  generateSetupPlan: (ctx, analysis) => generateSetupPlan(ctx, analysis),
58938
59066
  runGate: (workdir, config2) => runSetupGate(workdir, config2),
58939
59067
  fileExists: (path14) => Bun.file(path14).exists(),
58940
- writeFile: (path14, content) => Bun.write(path14, content).then(() => {}),
58941
- mkdir: async (path14) => {
58942
- const proc = Bun.spawn(["mkdir", "-p", path14]);
58943
- await proc.exited;
58944
- },
59068
+ writeSetupConfig: (workdir, config2, monoConfigs, opts) => writeSetupConfig(workdir, config2, monoConfigs, opts),
58945
59069
  stdout: (msg) => {
58946
59070
  process.stdout.write(`${msg}
58947
59071
  `);
@@ -60380,7 +60504,7 @@ var init_command_argv = __esm(() => {
60380
60504
  });
60381
60505
 
60382
60506
  // src/hooks/runner.ts
60383
- import { join as join74 } from "path";
60507
+ import { join as join75 } from "path";
60384
60508
  function createDrainDeadline2(deadlineMs) {
60385
60509
  let timeoutId;
60386
60510
  const promise2 = new Promise((resolve16) => {
@@ -60399,14 +60523,14 @@ async function loadHooksConfig(projectDir, globalDir) {
60399
60523
  let globalHooks = { hooks: {} };
60400
60524
  let projectHooks = { hooks: {} };
60401
60525
  let skipGlobal = false;
60402
- const projectPath = join74(projectDir, "hooks.json");
60526
+ const projectPath = join75(projectDir, "hooks.json");
60403
60527
  const projectData = await loadJsonFile(projectPath, "hooks");
60404
60528
  if (projectData) {
60405
60529
  projectHooks = projectData;
60406
60530
  skipGlobal = projectData.skipGlobal ?? false;
60407
60531
  }
60408
60532
  if (!skipGlobal && globalDir) {
60409
- const globalPath = join74(globalDir, "hooks.json");
60533
+ const globalPath = join75(globalDir, "hooks.json");
60410
60534
  const globalData = await loadJsonFile(globalPath, "hooks");
60411
60535
  if (globalData) {
60412
60536
  globalHooks = globalData;
@@ -60576,7 +60700,7 @@ var package_default;
60576
60700
  var init_package = __esm(() => {
60577
60701
  package_default = {
60578
60702
  name: "@nathapp/nax",
60579
- version: "0.70.1",
60703
+ version: "0.70.3",
60580
60704
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
60581
60705
  type: "module",
60582
60706
  bin: {
@@ -60587,7 +60711,7 @@ var init_package = __esm(() => {
60587
60711
  dev: "bun run bin/nax.ts",
60588
60712
  build: 'bun build bin/nax.ts --outdir dist --target bun --define "GIT_COMMIT=\\"$(git rev-parse --short HEAD)\\""',
60589
60713
  typecheck: "bun x tsc --noEmit && bun x tsc --noEmit -p tsconfig.contracts.json",
60590
- lint: "bun x biome check src/ bin/ && bun run check:no-real-global-nax && bun run check:alias-internals && bun run check:deep-relatives && bun run check:nax-error && bun run check:logger-storyid",
60714
+ lint: "bun x biome check src/ bin/ && bun run check:no-real-global-nax && bun run check:alias-internals && bun run check:deep-relatives && bun run check:nax-error && bun run check:logger-storyid && bun run check:file-sizes",
60591
60715
  "lint:json": "bun x biome check src/ bin/ --reporter json && bun run check:nax-error 1>&2 && bun run check:logger-storyid 1>&2",
60592
60716
  "lint:fix": "bun x biome check --write src/ bin/",
60593
60717
  "check:no-real-global-nax": "bun run scripts/check-no-real-global-nax.ts",
@@ -60598,6 +60722,8 @@ var init_package = __esm(() => {
60598
60722
  "check:nax-error:update": "bun run scripts/check-nax-error.ts --update-baseline",
60599
60723
  "check:logger-storyid": "bun run scripts/check-logger-storyid.ts",
60600
60724
  "check:logger-storyid:update": "bun run scripts/check-logger-storyid.ts --update-baseline",
60725
+ "check:file-sizes": "bun run scripts/check-file-sizes.ts",
60726
+ "check:file-sizes:update": "bun run scripts/check-file-sizes.ts --update-baseline",
60601
60727
  release: "bun scripts/release.ts",
60602
60728
  test: "AGENT=1 bun run scripts/run-tests.ts",
60603
60729
  "test:verbose": "bun run scripts/run-tests.ts",
@@ -60609,6 +60735,8 @@ var init_package = __esm(() => {
60609
60735
  "test:integration": "bun test ./test/integration/ --timeout=60000",
60610
60736
  "test:ui": "bun test ./test/ui/ --timeout=60000",
60611
60737
  "test:e2e": "timeout -k 5s 180s bun test test/e2e/ --timeout=60000",
60738
+ "test:coverage": "bun run scripts/check-coverage.ts",
60739
+ "test:coverage:report": "bun run scripts/check-coverage.ts --report",
60612
60740
  "check-test-overlap": "bun run scripts/check-test-overlap.ts",
60613
60741
  "check-dead-tests": "bun run scripts/check-dead-tests.ts",
60614
60742
  "check:test-sizes": "bun run scripts/check-test-sizes.ts",
@@ -60672,8 +60800,8 @@ var init_version = __esm(() => {
60672
60800
  NAX_VERSION = package_default.version;
60673
60801
  NAX_COMMIT = (() => {
60674
60802
  try {
60675
- if (/^[0-9a-f]{6,10}$/.test("c53be9bf"))
60676
- return "c53be9bf";
60803
+ if (/^[0-9a-f]{6,10}$/.test("c399b5b4"))
60804
+ return "c399b5b4";
60677
60805
  } catch {}
60678
60806
  try {
60679
60807
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -61551,15 +61679,15 @@ var init_acceptance_loop = __esm(() => {
61551
61679
 
61552
61680
  // src/session/scratch-purge.ts
61553
61681
  import { mkdir as mkdir12, rename, rm } from "fs/promises";
61554
- import { dirname as dirname12, join as join75 } from "path";
61682
+ import { dirname as dirname12, join as join76 } from "path";
61555
61683
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
61556
- const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
61684
+ const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
61557
61685
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
61558
61686
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
61559
61687
  let purged = 0;
61560
61688
  for (const sessionId of sessionIds) {
61561
- const sessionDir = join75(sessionsDir, sessionId);
61562
- const descriptorPath = join75(sessionDir, "descriptor.json");
61689
+ const sessionDir = join76(sessionsDir, sessionId);
61690
+ const descriptorPath = join76(sessionDir, "descriptor.json");
61563
61691
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
61564
61692
  continue;
61565
61693
  let lastActivityAt;
@@ -61575,7 +61703,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
61575
61703
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
61576
61704
  continue;
61577
61705
  if (archiveInsteadOfDelete) {
61578
- const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
61706
+ const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
61579
61707
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
61580
61708
  } else {
61581
61709
  await _scratchPurgeDeps.remove(sessionDir);
@@ -62280,7 +62408,7 @@ function outputRunFooter(options) {
62280
62408
  }
62281
62409
  var init_headless_formatter = __esm(() => {
62282
62410
  init_source();
62283
- init_logging();
62411
+ init_log_format();
62284
62412
  init_version();
62285
62413
  });
62286
62414
 
@@ -62326,12 +62454,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
62326
62454
 
62327
62455
  // src/pipeline/subscribers/events-writer.ts
62328
62456
  import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
62329
- import { basename as basename13, join as join76 } from "path";
62457
+ import { basename as basename13, join as join77 } from "path";
62330
62458
  function wireEventsWriter(bus, feature, runId, workdir) {
62331
62459
  const logger = getSafeLogger();
62332
62460
  const project = basename13(workdir);
62333
- const eventsDir = join76(getEventsRootDir(), project);
62334
- const eventsFile = join76(eventsDir, "events.jsonl");
62461
+ const eventsDir = join77(getEventsRootDir(), project);
62462
+ const eventsFile = join77(eventsDir, "events.jsonl");
62335
62463
  let dirReady = false;
62336
62464
  const write = (line) => {
62337
62465
  return (async () => {
@@ -62512,12 +62640,12 @@ var init_interaction2 = __esm(() => {
62512
62640
 
62513
62641
  // src/pipeline/subscribers/registry.ts
62514
62642
  import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
62515
- import { basename as basename14, join as join77 } from "path";
62643
+ import { basename as basename14, join as join78 } from "path";
62516
62644
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
62517
62645
  const logger = getSafeLogger();
62518
62646
  const project = basename14(workdir);
62519
- const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
62520
- const metaFile = join77(runDir, "meta.json");
62647
+ const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
62648
+ const metaFile = join78(runDir, "meta.json");
62521
62649
  const unsub = bus.on("run:started", (_ev) => {
62522
62650
  return (async () => {
62523
62651
  try {
@@ -62527,8 +62655,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
62527
62655
  project,
62528
62656
  feature,
62529
62657
  workdir,
62530
- statusPath: join77(outputDir, "features", feature, "status.json"),
62531
- eventsDir: join77(outputDir, "features", feature, "runs"),
62658
+ statusPath: join78(outputDir, "features", feature, "status.json"),
62659
+ eventsDir: join78(outputDir, "features", feature, "runs"),
62532
62660
  registeredAt: new Date().toISOString()
62533
62661
  };
62534
62662
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -62760,7 +62888,7 @@ function buildPreviewRouting(story, config2) {
62760
62888
 
62761
62889
  // src/worktree/types.ts
62762
62890
  var WorktreeDependencyPreparationError;
62763
- var init_types9 = __esm(() => {
62891
+ var init_types10 = __esm(() => {
62764
62892
  WorktreeDependencyPreparationError = class WorktreeDependencyPreparationError extends Error {
62765
62893
  mode;
62766
62894
  failureCategory = "dependency-prep";
@@ -62774,7 +62902,7 @@ var init_types9 = __esm(() => {
62774
62902
 
62775
62903
  // src/worktree/dependencies.ts
62776
62904
  import { existsSync as existsSync30 } from "fs";
62777
- import { join as join78 } from "path";
62905
+ import { join as join79 } from "path";
62778
62906
  async function prepareWorktreeDependencies(options) {
62779
62907
  const mode = options.config.execution.worktreeDependencies.mode;
62780
62908
  const resolvedCwd = resolveDependencyCwd(options);
@@ -62788,7 +62916,7 @@ async function prepareWorktreeDependencies(options) {
62788
62916
  }
62789
62917
  }
62790
62918
  function resolveDependencyCwd(options) {
62791
- return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
62919
+ return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
62792
62920
  }
62793
62921
  function resolveInheritedDependencies(options, resolvedCwd) {
62794
62922
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -62798,7 +62926,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
62798
62926
  }
62799
62927
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
62800
62928
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
62801
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join78(directory, filename))));
62929
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
62802
62930
  }
62803
62931
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
62804
62932
  const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
@@ -62830,7 +62958,7 @@ var PHASE_ONE_INHERIT_UNSUPPORTED_FILES, _worktreeDependencyDeps;
62830
62958
  var init_dependencies = __esm(() => {
62831
62959
  init_bun_deps();
62832
62960
  init_command_argv();
62833
- init_types9();
62961
+ init_types10();
62834
62962
  PHASE_ONE_INHERIT_UNSUPPORTED_FILES = [
62835
62963
  "package.json",
62836
62964
  "bun.lock",
@@ -62862,13 +62990,13 @@ __export(exports_manager, {
62862
62990
  });
62863
62991
  import { existsSync as existsSync31, symlinkSync } from "fs";
62864
62992
  import { mkdir as mkdir15 } from "fs/promises";
62865
- import { join as join79 } from "path";
62993
+ import { join as join80 } from "path";
62866
62994
 
62867
62995
  class WorktreeManager {
62868
62996
  async ensureGitExcludes(projectRoot) {
62869
62997
  const logger = getSafeLogger();
62870
- const infoDir = join79(projectRoot, ".git", "info");
62871
- const excludePath = join79(infoDir, "exclude");
62998
+ const infoDir = join80(projectRoot, ".git", "info");
62999
+ const excludePath = join80(infoDir, "exclude");
62872
63000
  try {
62873
63001
  await mkdir15(infoDir, { recursive: true });
62874
63002
  let existing = "";
@@ -62895,7 +63023,7 @@ ${missing.join(`
62895
63023
  }
62896
63024
  async create(projectRoot, storyId) {
62897
63025
  validateStoryId(storyId);
62898
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
63026
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62899
63027
  const branchName = `nax/${storyId}`;
62900
63028
  try {
62901
63029
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -62935,9 +63063,9 @@ ${missing.join(`
62935
63063
  }
62936
63064
  throw new Error(`Failed to create worktree: ${String(error48)}`);
62937
63065
  }
62938
- const envSource = join79(projectRoot, ".env");
63066
+ const envSource = join80(projectRoot, ".env");
62939
63067
  if (existsSync31(envSource)) {
62940
- const envTarget = join79(worktreePath, ".env");
63068
+ const envTarget = join80(worktreePath, ".env");
62941
63069
  try {
62942
63070
  symlinkSync(envSource, envTarget, "file");
62943
63071
  } catch (error48) {
@@ -62948,7 +63076,7 @@ ${missing.join(`
62948
63076
  }
62949
63077
  async remove(projectRoot, storyId) {
62950
63078
  validateStoryId(storyId);
62951
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
63079
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62952
63080
  const branchName = `nax/${storyId}`;
62953
63081
  try {
62954
63082
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -63532,6 +63660,8 @@ function resolveMaxAttemptsOutcome(failureCategory) {
63532
63660
  return "pause";
63533
63661
  case "runtime-crash":
63534
63662
  return "pause";
63663
+ case "review-incomplete":
63664
+ return "pause";
63535
63665
  case "session-failure":
63536
63666
  case "tests-failing":
63537
63667
  case "full-suite-gate-exhausted":
@@ -63770,10 +63900,10 @@ var init_merge_conflict_rectify = __esm(() => {
63770
63900
  });
63771
63901
 
63772
63902
  // src/execution/pipeline-result-handler.ts
63773
- import { join as join80 } from "path";
63903
+ import { join as join81 } from "path";
63774
63904
  async function removeWorktreeDirectory(projectRoot, storyId) {
63775
63905
  const logger = getSafeLogger();
63776
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
63906
+ const worktreePath = join81(projectRoot, ".nax-wt", storyId);
63777
63907
  try {
63778
63908
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
63779
63909
  cwd: projectRoot,
@@ -63990,7 +64120,7 @@ var init_pipeline_result_handler = __esm(() => {
63990
64120
 
63991
64121
  // src/execution/iteration-runner.ts
63992
64122
  import { existsSync as existsSync32 } from "fs";
63993
- import { join as join81 } from "path";
64123
+ import { join as join82 } from "path";
63994
64124
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
63995
64125
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
63996
64126
  if (ctx.dryRun) {
@@ -64015,7 +64145,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
64015
64145
  const storyStartTime = Date.now();
64016
64146
  let effectiveWorkdir = ctx.workdir;
64017
64147
  if (ctx.config.execution.storyIsolation === "worktree") {
64018
- const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
64148
+ const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
64019
64149
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
64020
64150
  if (!worktreeExists) {
64021
64151
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -64035,7 +64165,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
64035
64165
  }
64036
64166
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
64037
64167
  const profileOverride = profileOverrideFromConfig(ctx.config);
64038
- const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join81(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
64168
+ const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join82(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
64039
64169
  let dependencyContext;
64040
64170
  if (ctx.config.execution.storyIsolation === "worktree") {
64041
64171
  try {
@@ -64062,7 +64192,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
64062
64192
  };
64063
64193
  }
64064
64194
  }
64065
- const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join81(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join81(ctx.workdir, story.workdir) : ctx.workdir;
64195
+ 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;
64066
64196
  const pipelineContext = {
64067
64197
  config: effectiveConfig,
64068
64198
  rootConfig: ctx.config,
@@ -64265,7 +64395,7 @@ __export(exports_parallel_worker, {
64265
64395
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
64266
64396
  _parallelWorkerDeps: () => _parallelWorkerDeps
64267
64397
  });
64268
- import { join as join82 } from "path";
64398
+ import { join as join83 } from "path";
64269
64399
  function buildWorktreePipelineContext(base, _story) {
64270
64400
  return { ...base, prd: structuredClone(base.prd) };
64271
64401
  }
@@ -64288,7 +64418,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
64288
64418
  story,
64289
64419
  stories: [story],
64290
64420
  projectDir: context.projectDir,
64291
- workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
64421
+ workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
64292
64422
  worktreeDependencyContext: dependencyContext,
64293
64423
  routing,
64294
64424
  storyGitRef: storyGitRef ?? undefined
@@ -65188,7 +65318,7 @@ async function writeStatusFile(filePath, status) {
65188
65318
  var init_status_file = () => {};
65189
65319
 
65190
65320
  // src/execution/status-writer.ts
65191
- import { join as join83 } from "path";
65321
+ import { join as join84 } from "path";
65192
65322
 
65193
65323
  class StatusWriter {
65194
65324
  statusFile;
@@ -65307,7 +65437,7 @@ class StatusWriter {
65307
65437
  if (!this._prd)
65308
65438
  return;
65309
65439
  const safeLogger = getSafeLogger();
65310
- const featureStatusPath = join83(featureDir, "status.json");
65440
+ const featureStatusPath = join84(featureDir, "status.json");
65311
65441
  const write = async () => {
65312
65442
  try {
65313
65443
  const base = this.getSnapshot(totalCost, iterations);
@@ -65741,7 +65871,7 @@ __export(exports_run_initialization, {
65741
65871
  initializeRun: () => initializeRun,
65742
65872
  _reconcileDeps: () => _reconcileDeps
65743
65873
  });
65744
- import { join as join84 } from "path";
65874
+ import { join as join85 } from "path";
65745
65875
  async function reconcileState(prd, prdPath, workdir, config2) {
65746
65876
  const logger = getSafeLogger();
65747
65877
  let reconciledCount = 0;
@@ -65758,7 +65888,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
65758
65888
  });
65759
65889
  continue;
65760
65890
  }
65761
- const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
65891
+ const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
65762
65892
  try {
65763
65893
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
65764
65894
  if (!reviewResult.success) {
@@ -95589,7 +95719,7 @@ __export(exports_curator, {
95589
95719
  });
95590
95720
  import { readdirSync as readdirSync8 } from "fs";
95591
95721
  import { unlink as unlink4 } from "fs/promises";
95592
- import { basename as basename15, join as join86 } from "path";
95722
+ import { basename as basename15, join as join87 } from "path";
95593
95723
  function getProjectKey(config2, projectDir) {
95594
95724
  return config2.name?.trim() || basename15(projectDir);
95595
95725
  }
@@ -95672,7 +95802,7 @@ async function curatorStatus(options) {
95672
95802
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95673
95803
  const projectKey = getProjectKey(config2, resolved.projectDir);
95674
95804
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95675
- const runsDir = join86(outputDir, "runs");
95805
+ const runsDir = join87(outputDir, "runs");
95676
95806
  const runIds = listRunIds(runsDir);
95677
95807
  let runId;
95678
95808
  if (options.run) {
@@ -95689,8 +95819,8 @@ async function curatorStatus(options) {
95689
95819
  runId = runIds[runIds.length - 1];
95690
95820
  }
95691
95821
  console.log(`Run: ${runId}`);
95692
- const runDir = join86(runsDir, runId);
95693
- const observationsPath = join86(runDir, "observations.jsonl");
95822
+ const runDir = join87(runsDir, runId);
95823
+ const observationsPath = join87(runDir, "observations.jsonl");
95694
95824
  const observations = await parseObservations(observationsPath);
95695
95825
  const counts = new Map;
95696
95826
  for (const obs of observations) {
@@ -95700,7 +95830,7 @@ async function curatorStatus(options) {
95700
95830
  for (const [kind, count] of counts.entries()) {
95701
95831
  console.log(` ${kind}: ${count}`);
95702
95832
  }
95703
- const proposalsPath = join86(runDir, "curator-proposals.md");
95833
+ const proposalsPath = join87(runDir, "curator-proposals.md");
95704
95834
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
95705
95835
  if (proposalText !== null) {
95706
95836
  console.log("");
@@ -95714,8 +95844,8 @@ async function curatorCommit(options) {
95714
95844
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95715
95845
  const projectKey = getProjectKey(config2, resolved.projectDir);
95716
95846
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95717
- const runDir = join86(outputDir, "runs", options.runId);
95718
- const proposalsPath = join86(runDir, "curator-proposals.md");
95847
+ const runDir = join87(outputDir, "runs", options.runId);
95848
+ const proposalsPath = join87(runDir, "curator-proposals.md");
95719
95849
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
95720
95850
  if (proposalText === null) {
95721
95851
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -95731,7 +95861,7 @@ async function curatorCommit(options) {
95731
95861
  const dropFileState = new Map;
95732
95862
  const skippedDrops = new Set;
95733
95863
  for (const drop2 of drops) {
95734
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
95864
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
95735
95865
  if (!dropFileState.has(targetPath)) {
95736
95866
  const fileExists2 = await Bun.file(targetPath).exists();
95737
95867
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -95765,7 +95895,7 @@ async function curatorCommit(options) {
95765
95895
  if (skippedDrops.has(drop2)) {
95766
95896
  continue;
95767
95897
  }
95768
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
95898
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
95769
95899
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
95770
95900
  const filtered = filterDropContent(existing, drop2.description);
95771
95901
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -95774,7 +95904,7 @@ async function curatorCommit(options) {
95774
95904
  }
95775
95905
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
95776
95906
  for (const add2 of adds) {
95777
- const targetPath = join86(resolved.projectDir, add2.canonicalFile);
95907
+ const targetPath = join87(resolved.projectDir, add2.canonicalFile);
95778
95908
  const content = buildAddContent(add2);
95779
95909
  await _curatorCmdDeps.appendFile(targetPath, content);
95780
95910
  modifiedFiles.add(targetPath);
@@ -95811,7 +95941,7 @@ async function curatorDryrun(options) {
95811
95941
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95812
95942
  const projectKey = getProjectKey(config2, resolved.projectDir);
95813
95943
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95814
- const runsDir = join86(outputDir, "runs");
95944
+ const runsDir = join87(outputDir, "runs");
95815
95945
  const runIds = listRunIds(runsDir);
95816
95946
  if (runIds.length === 0) {
95817
95947
  console.log("No runs found.");
@@ -95822,7 +95952,7 @@ async function curatorDryrun(options) {
95822
95952
  console.log(`Run ${options.run} not found in ${runsDir}.`);
95823
95953
  return;
95824
95954
  }
95825
- const observationsPath = join86(runsDir, runId, "observations.jsonl");
95955
+ const observationsPath = join87(runsDir, runId, "observations.jsonl");
95826
95956
  const observations = await parseObservations(observationsPath);
95827
95957
  const thresholds = getThresholds(config2);
95828
95958
  const proposals = runHeuristics(observations, thresholds);
@@ -95863,12 +95993,12 @@ async function curatorGc(options) {
95863
95993
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
95864
95994
  const projectKey = getProjectKey(config2, resolved.projectDir);
95865
95995
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95866
- const perRunsDir = join86(outputDir, "runs");
95996
+ const perRunsDir = join87(outputDir, "runs");
95867
95997
  for (const runId of uniqueRunIds) {
95868
95998
  if (!keepSet.has(runId)) {
95869
- const runDir = join86(perRunsDir, runId);
95870
- await _curatorCmdDeps.removeFile(join86(runDir, "observations.jsonl"));
95871
- await _curatorCmdDeps.removeFile(join86(runDir, "curator-proposals.md"));
95999
+ const runDir = join87(perRunsDir, runId);
96000
+ await _curatorCmdDeps.removeFile(join87(runDir, "observations.jsonl"));
96001
+ await _curatorCmdDeps.removeFile(join87(runDir, "curator-proposals.md"));
95872
96002
  }
95873
96003
  }
95874
96004
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -95913,7 +96043,7 @@ var init_curator2 = __esm(() => {
95913
96043
  init_source();
95914
96044
  import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
95915
96045
  import { homedir as homedir3 } from "os";
95916
- import { basename as basename16, join as join87 } from "path";
96046
+ import { basename as basename16, join as join88 } from "path";
95917
96047
 
95918
96048
  // node_modules/commander/esm.mjs
95919
96049
  var import__ = __toESM(require_commander(), 1);
@@ -97037,6 +97167,7 @@ async function runsShowCommand(options) {
97037
97167
  init_prompts2();
97038
97168
  init_init2();
97039
97169
  init_setup();
97170
+ init_setup_write();
97040
97171
 
97041
97172
  // src/cli/plugins.ts
97042
97173
  init_paths();
@@ -97112,7 +97243,7 @@ init_source();
97112
97243
  init_loader();
97113
97244
  init_generator2();
97114
97245
  import { existsSync as existsSync24 } from "fs";
97115
- import { join as join62 } from "path";
97246
+ import { join as join63 } from "path";
97116
97247
  var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
97117
97248
  async function generateCommand(options) {
97118
97249
  const workdir = options.dir ?? process.cwd();
@@ -97155,7 +97286,7 @@ async function generateCommand(options) {
97155
97286
  return;
97156
97287
  }
97157
97288
  if (options.package) {
97158
- const packageDir = join62(workdir, options.package);
97289
+ const packageDir = join63(workdir, options.package);
97159
97290
  if (dryRun) {
97160
97291
  console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
97161
97292
  }
@@ -97175,8 +97306,8 @@ async function generateCommand(options) {
97175
97306
  process.exit(1);
97176
97307
  return;
97177
97308
  }
97178
- const contextPath = options.context ? join62(workdir, options.context) : join62(workdir, ".nax/context.md");
97179
- const outputDir = options.output ? join62(workdir, options.output) : workdir;
97309
+ const contextPath = options.context ? join63(workdir, options.context) : join63(workdir, ".nax/context.md");
97310
+ const outputDir = options.output ? join63(workdir, options.output) : workdir;
97180
97311
  const autoInject = !options.noAutoInject;
97181
97312
  if (!existsSync24(contextPath)) {
97182
97313
  console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
@@ -97282,7 +97413,7 @@ async function generateCommand(options) {
97282
97413
  // src/cli/config-display.ts
97283
97414
  init_loader();
97284
97415
  import { existsSync as existsSync26 } from "fs";
97285
- import { join as join64 } from "path";
97416
+ import { join as join65 } from "path";
97286
97417
 
97287
97418
  // src/cli/config-descriptions.ts
97288
97419
  var FIELD_DESCRIPTIONS = {
@@ -97535,7 +97666,7 @@ function deepEqual(a, b) {
97535
97666
  init_defaults();
97536
97667
  init_loader();
97537
97668
  import { existsSync as existsSync25 } from "fs";
97538
- import { join as join63 } from "path";
97669
+ import { join as join64 } from "path";
97539
97670
  async function loadConfigFile(path19) {
97540
97671
  if (!existsSync25(path19))
97541
97672
  return null;
@@ -97557,7 +97688,7 @@ async function loadProjectConfig() {
97557
97688
  const projectDir = findProjectDir();
97558
97689
  if (!projectDir)
97559
97690
  return null;
97560
- const projectPath = join63(projectDir, "config.json");
97691
+ const projectPath = join64(projectDir, "config.json");
97561
97692
  return await loadConfigFile(projectPath);
97562
97693
  }
97563
97694
 
@@ -97617,7 +97748,7 @@ async function configCommand(config2, options = {}) {
97617
97748
  function determineConfigSources() {
97618
97749
  const globalPath = globalConfigPath();
97619
97750
  const projectDir = findProjectDir();
97620
- const projectPath = projectDir ? join64(projectDir, "config.json") : null;
97751
+ const projectPath = projectDir ? join65(projectDir, "config.json") : null;
97621
97752
  return {
97622
97753
  global: fileExists(globalPath) ? globalPath : null,
97623
97754
  project: projectPath && fileExists(projectPath) ? projectPath : null
@@ -97766,15 +97897,15 @@ init_paths();
97766
97897
  init_profile();
97767
97898
  import { mkdirSync as mkdirSync5 } from "fs";
97768
97899
  import { readdirSync as readdirSync5 } from "fs";
97769
- import { join as join65 } from "path";
97900
+ import { join as join66 } from "path";
97770
97901
  var _profileCLIDeps = {
97771
97902
  env: process.env
97772
97903
  };
97773
97904
  var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
97774
97905
  var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
97775
97906
  async function profileListCommand(startDir) {
97776
- const globalProfilesDir = join65(globalConfigDir(), "profiles");
97777
- const projectProfilesDir = join65(projectConfigDir(startDir), "profiles");
97907
+ const globalProfilesDir = join66(globalConfigDir(), "profiles");
97908
+ const projectProfilesDir = join66(projectConfigDir(startDir), "profiles");
97778
97909
  const globalProfiles = scanProfileDir(globalProfilesDir);
97779
97910
  const projectProfiles = scanProfileDir(projectProfilesDir);
97780
97911
  const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
@@ -97833,7 +97964,7 @@ function maskProfileValues(obj) {
97833
97964
  return result;
97834
97965
  }
97835
97966
  async function profileUseCommand(profileName, startDir) {
97836
- const configPath = join65(projectConfigDir(startDir), "config.json");
97967
+ const configPath = join66(projectConfigDir(startDir), "config.json");
97837
97968
  const configFile = Bun.file(configPath);
97838
97969
  let existing = {};
97839
97970
  if (await configFile.exists()) {
@@ -97852,8 +97983,8 @@ async function profileCurrentCommand(startDir) {
97852
97983
  return resolveProfileName({}, _profileCLIDeps.env, startDir);
97853
97984
  }
97854
97985
  async function profileCreateCommand(profileName, startDir) {
97855
- const profilesDir = join65(projectConfigDir(startDir), "profiles");
97856
- const profilePath = join65(profilesDir, `${profileName}.json`);
97986
+ const profilesDir = join66(projectConfigDir(startDir), "profiles");
97987
+ const profilePath = join66(profilesDir, `${profileName}.json`);
97857
97988
  const profileFile = Bun.file(profilePath);
97858
97989
  if (await profileFile.exists()) {
97859
97990
  throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
@@ -97975,7 +98106,7 @@ async function contextInspectCommand(options) {
97975
98106
  init_canonical_loader();
97976
98107
  init_errors();
97977
98108
  import { mkdir as mkdir11 } from "fs/promises";
97978
- import { basename as basename12, join as join66 } from "path";
98109
+ import { basename as basename12, join as join67 } from "path";
97979
98110
  var _rulesCLIDeps = {
97980
98111
  readFile: async (path19) => Bun.file(path19).text(),
97981
98112
  writeFile: async (path19, content) => {
@@ -97984,7 +98115,7 @@ var _rulesCLIDeps = {
97984
98115
  fileExists: async (path19) => Bun.file(path19).exists(),
97985
98116
  globInDir: (dir) => {
97986
98117
  try {
97987
- return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join66(dir, f));
98118
+ return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join67(dir, f));
97988
98119
  } catch {
97989
98120
  return [];
97990
98121
  }
@@ -98033,7 +98164,7 @@ ${r.content}`).join(`
98033
98164
  `);
98034
98165
  const shimContent = `${header + body}
98035
98166
  `;
98036
- const shimPath = join66(workdir, shimFileName);
98167
+ const shimPath = join67(workdir, shimFileName);
98037
98168
  if (options.dryRun) {
98038
98169
  console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
98039
98170
  return;
@@ -98062,14 +98193,14 @@ function neutralizeContent(content) {
98062
98193
  }
98063
98194
  async function collectMigrationSources(workdir) {
98064
98195
  const sources = [];
98065
- const claudeMdPath = join66(workdir, "CLAUDE.md");
98196
+ const claudeMdPath = join67(workdir, "CLAUDE.md");
98066
98197
  if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
98067
98198
  const content = await _rulesCLIDeps.readFile(claudeMdPath);
98068
98199
  if (content.trim()) {
98069
98200
  sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
98070
98201
  }
98071
98202
  }
98072
- const rulesDir = join66(workdir, ".claude", "rules");
98203
+ const rulesDir = join67(workdir, ".claude", "rules");
98073
98204
  const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
98074
98205
  for (const filePath of ruleFiles) {
98075
98206
  try {
@@ -98089,7 +98220,7 @@ async function rulesMigrateCommand(options) {
98089
98220
  console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
98090
98221
  return;
98091
98222
  }
98092
- const targetDir = join66(workdir, CANONICAL_RULES_DIR);
98223
+ const targetDir = join67(workdir, CANONICAL_RULES_DIR);
98093
98224
  if (!options.dryRun) {
98094
98225
  try {
98095
98226
  await _rulesCLIDeps.mkdir(targetDir);
@@ -98100,7 +98231,7 @@ async function rulesMigrateCommand(options) {
98100
98231
  let written = 0;
98101
98232
  let skipped = 0;
98102
98233
  for (const { sourcePath, targetFileName, content } of sources) {
98103
- const targetPath = join66(targetDir, targetFileName);
98234
+ const targetPath = join67(targetDir, targetFileName);
98104
98235
  if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
98105
98236
  console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
98106
98237
  skipped++;
@@ -98139,7 +98270,7 @@ function collectCanonicalRuleRoots(workdir) {
98139
98270
  const packageRel = normalized.slice(0, idx);
98140
98271
  if (!packageRel)
98141
98272
  continue;
98142
- roots.add(join66(workdir, packageRel));
98273
+ roots.add(join67(workdir, packageRel));
98143
98274
  }
98144
98275
  return [...roots].sort();
98145
98276
  }
@@ -98191,7 +98322,7 @@ init_logger2();
98191
98322
  init_detect2();
98192
98323
  init_workspace();
98193
98324
  init_common();
98194
- import { join as join67 } from "path";
98325
+ import { join as join68 } from "path";
98195
98326
  function resolveEffective(detected, configPatterns) {
98196
98327
  if (configPatterns !== undefined)
98197
98328
  return "config";
@@ -98276,7 +98407,7 @@ async function detectCommand(options) {
98276
98407
  const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
98277
98408
  const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
98278
98409
  const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
98279
- const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
98410
+ const pkgConfigPath = join68(workdir, ".nax", "mono", dir, "config.json");
98280
98411
  const pkgRaw = await loadRawConfig(pkgConfigPath);
98281
98412
  const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
98282
98413
  const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
@@ -98330,13 +98461,13 @@ async function detectCommand(options) {
98330
98461
  if (rootDetected.confidence === "empty") {
98331
98462
  console.log(source_default.yellow(" root: skipped (empty detection)"));
98332
98463
  } else {
98333
- const rootConfigPath = join67(workdir, ".nax", "config.json");
98464
+ const rootConfigPath = join68(workdir, ".nax", "config.json");
98334
98465
  try {
98335
98466
  const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
98336
98467
  if (status === "skipped") {
98337
98468
  console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
98338
98469
  } else {
98339
- console.log(source_default.green(` root: ${status} \u2192 ${join67(".nax", "config.json")}`));
98470
+ console.log(source_default.green(` root: ${status} \u2192 ${join68(".nax", "config.json")}`));
98340
98471
  }
98341
98472
  } catch (err) {
98342
98473
  console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
@@ -98349,13 +98480,13 @@ async function detectCommand(options) {
98349
98480
  console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
98350
98481
  continue;
98351
98482
  }
98352
- const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
98483
+ const pkgConfigPath = join68(workdir, ".nax", "mono", dir, "config.json");
98353
98484
  try {
98354
98485
  const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
98355
98486
  if (status === "skipped") {
98356
98487
  console.log(source_default.dim(` ${dir}: skipped (already set)`));
98357
98488
  } else {
98358
- console.log(source_default.green(` ${dir}: ${status} \u2192 ${join67(".nax", "mono", dir, "config.json")}`));
98489
+ console.log(source_default.green(` ${dir}: ${status} \u2192 ${join68(".nax", "mono", dir, "config.json")}`));
98359
98490
  }
98360
98491
  } catch (err) {
98361
98492
  console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
@@ -98373,19 +98504,19 @@ async function detectCommand(options) {
98373
98504
  // src/commands/logs.ts
98374
98505
  init_common();
98375
98506
  import { existsSync as existsSync28 } from "fs";
98376
- import { join as join70 } from "path";
98507
+ import { join as join71 } from "path";
98377
98508
 
98378
98509
  // src/commands/logs-formatter.ts
98379
98510
  init_source();
98380
98511
  init_formatter();
98381
98512
  import { readdirSync as readdirSync7 } from "fs";
98382
- import { join as join69 } from "path";
98513
+ import { join as join70 } from "path";
98383
98514
 
98384
98515
  // src/commands/logs-reader.ts
98385
98516
  init_paths3();
98386
98517
  import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
98387
98518
  import { readdir as readdir3 } from "fs/promises";
98388
- import { join as join68 } from "path";
98519
+ import { join as join69 } from "path";
98389
98520
  var _logsReaderDeps = {
98390
98521
  getRunsDir
98391
98522
  };
@@ -98399,7 +98530,7 @@ async function resolveRunFileFromRegistry(runId) {
98399
98530
  }
98400
98531
  let matched = null;
98401
98532
  for (const entry of entries) {
98402
- const metaPath = join68(runsDir, entry, "meta.json");
98533
+ const metaPath = join69(runsDir, entry, "meta.json");
98403
98534
  try {
98404
98535
  const meta3 = await Bun.file(metaPath).json();
98405
98536
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -98421,14 +98552,14 @@ async function resolveRunFileFromRegistry(runId) {
98421
98552
  return null;
98422
98553
  }
98423
98554
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
98424
- return join68(matched.eventsDir, specificFile ?? files[0]);
98555
+ return join69(matched.eventsDir, specificFile ?? files[0]);
98425
98556
  }
98426
98557
  async function selectRunFile(runsDir) {
98427
98558
  const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
98428
98559
  if (files.length === 0) {
98429
98560
  return null;
98430
98561
  }
98431
- return join68(runsDir, files[0]);
98562
+ return join69(runsDir, files[0]);
98432
98563
  }
98433
98564
  async function extractRunSummary(filePath) {
98434
98565
  const file3 = Bun.file(filePath);
@@ -98514,7 +98645,7 @@ Runs:
98514
98645
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
98515
98646
  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"));
98516
98647
  for (const file3 of files) {
98517
- const filePath = join69(runsDir, file3);
98648
+ const filePath = join70(runsDir, file3);
98518
98649
  const summary = await extractRunSummary(filePath);
98519
98650
  const timestamp = file3.replace(".jsonl", "");
98520
98651
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -98628,7 +98759,7 @@ async function logsCommand(options) {
98628
98759
  return;
98629
98760
  }
98630
98761
  const resolved = resolveProject({ dir: options.dir });
98631
- const naxDir = join70(resolved.projectDir, ".nax");
98762
+ const naxDir = join71(resolved.projectDir, ".nax");
98632
98763
  const configPath = resolved.configPath;
98633
98764
  const configFile = Bun.file(configPath);
98634
98765
  const config2 = await configFile.json();
@@ -98636,8 +98767,8 @@ async function logsCommand(options) {
98636
98767
  if (!featureName) {
98637
98768
  throw new Error("No feature specified in config.json");
98638
98769
  }
98639
- const featureDir = join70(naxDir, "features", featureName);
98640
- const runsDir = join70(featureDir, "runs");
98770
+ const featureDir = join71(naxDir, "features", featureName);
98771
+ const runsDir = join71(featureDir, "runs");
98641
98772
  if (!existsSync28(runsDir)) {
98642
98773
  throw new Error(`No runs directory found for feature: ${featureName}`);
98643
98774
  }
@@ -98663,7 +98794,7 @@ init_prd();
98663
98794
  init_precheck();
98664
98795
  init_common();
98665
98796
  import { existsSync as existsSync29 } from "fs";
98666
- import { join as join71 } from "path";
98797
+ import { join as join72 } from "path";
98667
98798
  async function precheckCommand(options) {
98668
98799
  const resolved = resolveProject({
98669
98800
  dir: options.dir,
@@ -98685,9 +98816,9 @@ async function precheckCommand(options) {
98685
98816
  process.exit(1);
98686
98817
  }
98687
98818
  }
98688
- const naxDir = join71(resolved.projectDir, ".nax");
98689
- const featureDir = join71(naxDir, "features", featureName);
98690
- const prdPath = join71(featureDir, "prd.json");
98819
+ const naxDir = join72(resolved.projectDir, ".nax");
98820
+ const featureDir = join72(naxDir, "features", featureName);
98821
+ const prdPath = join72(featureDir, "prd.json");
98691
98822
  if (!existsSync29(featureDir)) {
98692
98823
  console.error(source_default.red(`Feature not found: ${featureName}`));
98693
98824
  process.exit(1);
@@ -98710,7 +98841,7 @@ async function precheckCommand(options) {
98710
98841
  init_source();
98711
98842
  init_paths3();
98712
98843
  import { readdir as readdir4 } from "fs/promises";
98713
- import { join as join72 } from "path";
98844
+ import { join as join73 } from "path";
98714
98845
  var DEFAULT_LIMIT = 20;
98715
98846
  var _runsCmdDeps = {
98716
98847
  getRunsDir
@@ -98765,7 +98896,7 @@ async function runsCommand(options = {}) {
98765
98896
  }
98766
98897
  const rows = [];
98767
98898
  for (const entry of entries) {
98768
- const metaPath = join72(runsDir, entry, "meta.json");
98899
+ const metaPath = join73(runsDir, entry, "meta.json");
98769
98900
  let meta3;
98770
98901
  try {
98771
98902
  meta3 = await Bun.file(metaPath).json();
@@ -98842,7 +98973,7 @@ async function runsCommand(options = {}) {
98842
98973
 
98843
98974
  // src/commands/unlock.ts
98844
98975
  init_source();
98845
- import { join as join73 } from "path";
98976
+ import { join as join74 } from "path";
98846
98977
  function isProcessAlive2(pid) {
98847
98978
  try {
98848
98979
  process.kill(pid, 0);
@@ -98857,7 +98988,7 @@ function formatLockAge(ageMs) {
98857
98988
  }
98858
98989
  async function unlockCommand(options) {
98859
98990
  const workdir = options.dir ?? process.cwd();
98860
- const lockPath = join73(workdir, "nax.lock");
98991
+ const lockPath = join74(workdir, "nax.lock");
98861
98992
  const lockFile = Bun.file(lockPath);
98862
98993
  const exists = await lockFile.exists();
98863
98994
  if (!exists) {
@@ -107295,7 +107426,7 @@ Next: nax generate --package ${options.package}`));
107295
107426
  }
107296
107427
  return;
107297
107428
  }
107298
- const naxDir = join87(workdir, ".nax");
107429
+ const naxDir = join88(workdir, ".nax");
107299
107430
  if (existsSync35(naxDir) && !options.force) {
107300
107431
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
107301
107432
  return;
@@ -107324,11 +107455,11 @@ Next: nax generate --package ${options.package}`));
107324
107455
  }
107325
107456
  }
107326
107457
  }
107327
- mkdirSync7(join87(naxDir, "features"), { recursive: true });
107328
- mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
107458
+ mkdirSync7(join88(naxDir, "features"), { recursive: true });
107459
+ mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
107329
107460
  const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
107330
- await Bun.write(join87(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
107331
- await Bun.write(join87(naxDir, "hooks.json"), JSON.stringify({
107461
+ await Bun.write(join88(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
107462
+ await Bun.write(join88(naxDir, "hooks.json"), JSON.stringify({
107332
107463
  hooks: {
107333
107464
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
107334
107465
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -107336,12 +107467,12 @@ Next: nax generate --package ${options.package}`));
107336
107467
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
107337
107468
  }
107338
107469
  }, null, 2));
107339
- await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
107470
+ await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
107340
107471
  *.tmp
107341
107472
  .paused.json
107342
107473
  .nax-verifier-verdict.json
107343
107474
  `);
107344
- await Bun.write(join87(naxDir, "context.md"), `# Project Context
107475
+ await Bun.write(join88(naxDir, "context.md"), `# Project Context
107345
107476
 
107346
107477
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
107347
107478
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -107483,7 +107614,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
107483
107614
  const cliOverrides = {};
107484
107615
  const cliProfiles = options.profile ?? [];
107485
107616
  const profileOverride = naxDir ? await resolveRunProfileOverride({
107486
- prdPath: join87(naxDir, "features", options.feature, "prd.json"),
107617
+ prdPath: join88(naxDir, "features", options.feature, "prd.json"),
107487
107618
  projectRoot: workdir,
107488
107619
  cliProfile: cliProfiles,
107489
107620
  envProfile: process.env.NAX_PROFILE
@@ -107496,8 +107627,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
107496
107627
  console.error(source_default.red("nax not initialized. Run: nax init"));
107497
107628
  process.exit(1);
107498
107629
  }
107499
- const featureDir = join87(naxDir, "features", options.feature);
107500
- const prdPath = join87(featureDir, "prd.json");
107630
+ const featureDir = join88(naxDir, "features", options.feature);
107631
+ const prdPath = join88(featureDir, "prd.json");
107501
107632
  if (options.plan && options.from) {
107502
107633
  if (existsSync35(prdPath) && !options.force) {
107503
107634
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
@@ -107519,10 +107650,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
107519
107650
  }
107520
107651
  }
107521
107652
  try {
107522
- const planLogDir = join87(featureDir, "plan");
107653
+ const planLogDir = join88(featureDir, "plan");
107523
107654
  mkdirSync7(planLogDir, { recursive: true });
107524
107655
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
107525
- const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
107656
+ const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
107526
107657
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
107527
107658
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
107528
107659
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -107568,10 +107699,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
107568
107699
  resetLogger();
107569
107700
  const projectKey = config2.name?.trim() || basename16(workdir);
107570
107701
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
107571
- const runsDir = join87(outputDir, "features", options.feature, "runs");
107702
+ const runsDir = join88(outputDir, "features", options.feature, "runs");
107572
107703
  mkdirSync7(runsDir, { recursive: true });
107573
107704
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
107574
- const logFilePath = join87(runsDir, `${runId}.jsonl`);
107705
+ const logFilePath = join88(runsDir, `${runId}.jsonl`);
107575
107706
  const isTTY = process.stdout.isTTY ?? false;
107576
107707
  const headlessFlag = options.headless ?? false;
107577
107708
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -107589,7 +107720,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
107589
107720
  config2.agent.default = options.agent;
107590
107721
  }
107591
107722
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
107592
- const globalNaxDir = join87(homedir3(), ".nax");
107723
+ const globalNaxDir = join88(homedir3(), ".nax");
107593
107724
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
107594
107725
  const eventEmitter = new PipelineEventEmitter;
107595
107726
  const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
@@ -107609,12 +107740,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
107609
107740
  events: eventEmitter,
107610
107741
  ptyOptions: null,
107611
107742
  agentStreamEvents,
107612
- queueFilePath: join87(workdir, ".queue.txt")
107743
+ queueFilePath: join88(workdir, ".queue.txt")
107613
107744
  });
107614
107745
  } else {
107615
107746
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
107616
107747
  }
107617
- const statusFilePath = join87(outputDir, "status.json");
107748
+ const statusFilePath = join88(outputDir, "status.json");
107618
107749
  let parallel;
107619
107750
  if (options.parallel !== undefined) {
107620
107751
  parallel = Number.parseInt(options.parallel, 10);
@@ -107641,7 +107772,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
107641
107772
  skipPrecheck: options.skipPrecheck ?? false,
107642
107773
  agentStreamEvents
107643
107774
  });
107644
- const latestSymlink = join87(runsDir, "latest.jsonl");
107775
+ const latestSymlink = join88(runsDir, "latest.jsonl");
107645
107776
  try {
107646
107777
  if (existsSync35(latestSymlink)) {
107647
107778
  Bun.spawnSync(["rm", latestSymlink]);
@@ -107702,9 +107833,9 @@ features.command("create <name>").description("Create a new feature").option("-d
107702
107833
  console.error(source_default.red("nax not initialized. Run: nax init"));
107703
107834
  process.exit(1);
107704
107835
  }
107705
- const featureDir = join87(naxDir, "features", name);
107836
+ const featureDir = join88(naxDir, "features", name);
107706
107837
  mkdirSync7(featureDir, { recursive: true });
107707
- await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
107838
+ await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
107708
107839
 
107709
107840
  ## Overview
107710
107841
 
@@ -107737,7 +107868,7 @@ features.command("create <name>").description("Create a new feature").option("-d
107737
107868
 
107738
107869
  <!-- What this feature explicitly does NOT cover. -->
107739
107870
  `);
107740
- await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
107871
+ await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
107741
107872
 
107742
107873
  Created: ${new Date().toISOString()}
107743
107874
 
@@ -107763,7 +107894,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
107763
107894
  console.error(source_default.red("nax not initialized."));
107764
107895
  process.exit(1);
107765
107896
  }
107766
- const featuresDir = join87(naxDir, "features");
107897
+ const featuresDir = join88(naxDir, "features");
107767
107898
  if (!existsSync35(featuresDir)) {
107768
107899
  console.log(source_default.dim("No features yet."));
107769
107900
  return;
@@ -107778,7 +107909,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
107778
107909
  Features:
107779
107910
  `));
107780
107911
  for (const name of entries) {
107781
- const prdPath = join87(featuresDir, name, "prd.json");
107912
+ const prdPath = join88(featuresDir, name, "prd.json");
107782
107913
  if (existsSync35(prdPath)) {
107783
107914
  const prd = await loadPRD(prdPath);
107784
107915
  const c = countStories(prd);
@@ -107814,10 +107945,10 @@ Use: nax plan -f <feature> --from <spec>`));
107814
107945
  cliOverrides.profile = cliProfiles;
107815
107946
  }
107816
107947
  const config2 = await loadConfig(workdir, cliOverrides);
107817
- const featureLogDir = join87(naxDir, "features", options.feature, "plan");
107948
+ const featureLogDir = join88(naxDir, "features", options.feature, "plan");
107818
107949
  mkdirSync7(featureLogDir, { recursive: true });
107819
107950
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
107820
- const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
107951
+ const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
107821
107952
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
107822
107953
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
107823
107954
  try {