@nathapp/nax 0.70.0 → 0.70.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +695 -466
  2. package/package.json +6 -2
package/dist/nax.js CHANGED
@@ -17277,9 +17277,13 @@ var init_schemas_review = __esm(() => {
17277
17277
  }).optional(),
17278
17278
  nonBlockingFix: exports_external.object({
17279
17279
  enabled: exports_external.boolean().default(false),
17280
- scope: exports_external.enum(["source", "both"]).default("both"),
17280
+ scope: exports_external.enum(["source", "both", "triage"]).default("both"),
17281
17281
  regressionAttempts: exports_external.number().int().min(0).default(1),
17282
- verifierGuard: exports_external.boolean().default(true)
17282
+ verifierGuard: exports_external.boolean().default(true),
17283
+ sourceDiffCap: exports_external.object({
17284
+ maxFiles: exports_external.number().int().min(0).default(10),
17285
+ maxLines: exports_external.number().int().min(0).default(500)
17286
+ }).optional().default({ maxFiles: 10, maxLines: 500 })
17283
17287
  }).optional()
17284
17288
  });
17285
17289
  ReviewConfigSchema = exports_external.object({
@@ -17733,7 +17737,7 @@ var init_schema = __esm(() => {
17733
17737
  init_defaults();
17734
17738
  });
17735
17739
 
17736
- // src/logging/types.ts
17740
+ // src/log-format/types.ts
17737
17741
  var EMOJI;
17738
17742
  var init_types2 = __esm(() => {
17739
17743
  EMOJI = {
@@ -17755,7 +17759,7 @@ var init_types2 = __esm(() => {
17755
17759
  };
17756
17760
  });
17757
17761
 
17758
- // src/logging/formatter.ts
17762
+ // src/log-format/formatter.ts
17759
17763
  function formatTimestamp(isoTimestamp) {
17760
17764
  const date5 = new Date(isoTimestamp);
17761
17765
  return date5.toLocaleTimeString("en-US", {
@@ -18068,8 +18072,8 @@ var init_formatter = __esm(() => {
18068
18072
  ];
18069
18073
  });
18070
18074
 
18071
- // src/logging/index.ts
18072
- var init_logging = __esm(() => {
18075
+ // src/log-format/index.ts
18076
+ var init_log_format = __esm(() => {
18073
18077
  init_formatter();
18074
18078
  init_types2();
18075
18079
  });
@@ -18306,7 +18310,7 @@ function resetLogger() {
18306
18310
  }
18307
18311
  var LOG_LEVEL_PRIORITY, instance = null, noopLogger;
18308
18312
  var init_logger = __esm(() => {
18309
- init_logging();
18313
+ init_log_format();
18310
18314
  init_formatters();
18311
18315
  init_redact();
18312
18316
  LOG_LEVEL_PRIORITY = {
@@ -18959,6 +18963,19 @@ function rejectLegacyRectificationKeys(conf) {
18959
18963
  `);
18960
18964
  throw new NaxError(message, "CONFIG_LEGACY_RECTIFICATION_KEYS", { stage: "config", legacyKeys });
18961
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
+ }
18962
18979
  function applyBatchModeCompat(conf) {
18963
18980
  const routing = conf.routing;
18964
18981
  const llm = routing?.llm;
@@ -19068,6 +19085,7 @@ async function loadConfig(startDir, cliOverrides) {
19068
19085
  }
19069
19086
  rejectLegacyAgentKeys(rawConfig);
19070
19087
  rejectLegacyRectificationKeys(rawConfig);
19088
+ rejectUnimplementedScopedProfile(rawConfig);
19071
19089
  const result = NaxConfigSchema.safeParse(rawConfig);
19072
19090
  if (!result.success) {
19073
19091
  const errors3 = result.error.issues.map((err) => {
@@ -19137,6 +19155,7 @@ async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
19137
19155
  rawMerged.profileChain = packageChain;
19138
19156
  rejectLegacyAgentKeys(rawMerged);
19139
19157
  rejectLegacyRectificationKeys(rawMerged);
19158
+ rejectUnimplementedScopedProfile(rawMerged);
19140
19159
  const result = NaxConfigSchema.safeParse(rawMerged);
19141
19160
  if (!result.success) {
19142
19161
  const errors3 = result.error.issues.map((err) => {
@@ -32581,6 +32600,14 @@ var init_ac_structural_counterfactual = __esm(() => {
32581
32600
  BLOCKING_CATEGORIES = new Set(["input", "error-path", "abandonment", "assumption"]);
32582
32601
  });
32583
32602
 
32603
+ // src/review/category-fix-target.ts
32604
+ function categoryToFixTarget(category) {
32605
+ return category != null && BLOCKING_CATEGORIES.has(category) ? "source" : "test";
32606
+ }
32607
+ var init_category_fix_target = __esm(() => {
32608
+ init_ac_structural_counterfactual();
32609
+ });
32610
+
32584
32611
  // src/review/severity.ts
32585
32612
  function isBlockingSeverity(sev, threshold = "error") {
32586
32613
  return (SEVERITY_RANK[sev] ?? 0) >= SEVERITY_RANK[threshold];
@@ -32637,12 +32664,13 @@ function toAdversarialReviewFindings(findings) {
32637
32664
  line: f.line,
32638
32665
  message: f.issue,
32639
32666
  suggestion: f.suggestion,
32640
- fixTarget: f.category === "test-gap" ? "test" : undefined,
32667
+ fixTarget: categoryToFixTarget(f.category),
32641
32668
  meta: Object.keys(metaExtras).length > 0 ? metaExtras : undefined
32642
32669
  };
32643
32670
  });
32644
32671
  }
32645
32672
  var init_adversarial_helpers = __esm(() => {
32673
+ init_category_fix_target();
32646
32674
  init_severity();
32647
32675
  });
32648
32676
 
@@ -33448,9 +33476,16 @@ function buildMeta(f, originalSeverity) {
33448
33476
  function findingCategory(f) {
33449
33477
  return "category" in f && f.category ? f.category : undefined;
33450
33478
  }
33479
+ function deriveFixTargetForReviewFinding(category, source) {
33480
+ if (source === "semantic-review" || source === "semantic-debate-review") {
33481
+ return "source";
33482
+ }
33483
+ return categoryToFixTarget(category);
33484
+ }
33451
33485
  function llmFindingToReviewFinding(f, opts = {}) {
33452
33486
  const category = findingCategory(f);
33453
33487
  const narrowed = narrowSeverity(f.severity);
33488
+ const source = opts.source;
33454
33489
  const result = {
33455
33490
  ruleId: deriveRuleId(category, f.issue),
33456
33491
  severity: narrowed,
@@ -33460,8 +33495,9 @@ function llmFindingToReviewFinding(f, opts = {}) {
33460
33495
  };
33461
33496
  if (category)
33462
33497
  result.category = category;
33463
- if (opts.source)
33464
- result.source = opts.source;
33498
+ if (source)
33499
+ result.source = source;
33500
+ result.fixTarget = deriveFixTargetForReviewFinding(category, source);
33465
33501
  const meta3 = buildMeta(f, f.severity !== narrowed ? f.severity : undefined);
33466
33502
  if (meta3)
33467
33503
  result.meta = meta3;
@@ -33472,6 +33508,7 @@ function llmFindingsToReviewFindings(findings, opts = {}) {
33472
33508
  }
33473
33509
  var SEVERITY_MAP, RULE_ID_SLUG_TOKENS = 6;
33474
33510
  var init_finding_projection = __esm(() => {
33511
+ init_category_fix_target();
33475
33512
  SEVERITY_MAP = {
33476
33513
  critical: "critical",
33477
33514
  error: "error",
@@ -39375,9 +39412,10 @@ var init__finding_to_check = __esm(() => {
39375
39412
  // src/operations/autofix-implementer-strategy.ts
39376
39413
  function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
39377
39414
  const claimsAdversarial = opts.includeAdversarialReview === true;
39415
+ const adversarialReviewByFixTarget = opts.adversarialReviewByFixTarget;
39378
39416
  return {
39379
39417
  name: "autofix-implementer",
39380
- appliesTo: (f) => (f.fixTarget === "source" || f.fixTarget == null) && IMPLEMENTER_SOURCES.has(f.source) || claimsAdversarial && f.source === "adversarial-review",
39418
+ 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",
39381
39419
  fixOp: implementerRectifyOp,
39382
39420
  buildInput: (findings, _prior, _cycleCtx) => ({
39383
39421
  failedChecks: findingsToFailedChecks(findings),
@@ -39409,10 +39447,11 @@ var init_autofix_implementer_strategy = __esm(() => {
39409
39447
  });
39410
39448
 
39411
39449
  // src/operations/autofix-test-writer-strategy.ts
39412
- function makeAutofixTestWriterStrategy(story, config2, sink) {
39450
+ function makeAutofixTestWriterStrategy(story, config2, sink, opts = {}) {
39451
+ const includeAdversarial = opts.includeAdversarialReview !== false;
39413
39452
  return {
39414
39453
  name: "autofix-test-writer",
39415
- appliesTo: (f) => f.fixTarget === "test" || f.source === "adversarial-review" || sink.mockHandoffs.length > 0,
39454
+ appliesTo: (f) => f.fixTarget === "test" || includeAdversarial && f.source === "adversarial-review" || sink.mockHandoffs.length > 0,
39416
39455
  fixOp: testWriterRectifyOp,
39417
39456
  buildInput: (findings, _prior, _cycleCtx) => {
39418
39457
  if (sink.mockHandoffs.length > 0) {
@@ -42307,6 +42346,7 @@ var init_runner2 = __esm(() => {
42307
42346
  // src/review/index.ts
42308
42347
  var init_review = __esm(() => {
42309
42348
  init_semantic_helpers();
42349
+ init_category_fix_target();
42310
42350
  init_ac_quote_validator();
42311
42351
  init_ac_structural_counterfactual();
42312
42352
  init_adversarial();
@@ -45251,7 +45291,7 @@ function attachLoggingSubscriber(bus, runId) {
45251
45291
  offError();
45252
45292
  };
45253
45293
  }
45254
- var init_logging2 = __esm(() => {
45294
+ var init_logging = __esm(() => {
45255
45295
  init_logger2();
45256
45296
  });
45257
45297
 
@@ -45769,7 +45809,7 @@ var init_idle_watchdog = __esm(() => {
45769
45809
  // src/runtime/middleware/index.ts
45770
45810
  var init_middleware = __esm(() => {
45771
45811
  init_cancellation();
45772
- init_logging2();
45812
+ init_logging();
45773
45813
  init_agent_stream_logging();
45774
45814
  init_idle_watchdog();
45775
45815
  });
@@ -54815,6 +54855,26 @@ var init_rollback = __esm(() => {
54815
54855
  };
54816
54856
  });
54817
54857
 
54858
+ // src/utils/paths.ts
54859
+ import { join as join47, relative as relative13, sep as sep4 } from "path";
54860
+ function packageDirRelative(projectDir, workdir) {
54861
+ if (!projectDir || !workdir || workdir === projectDir)
54862
+ return;
54863
+ const rel = relative13(projectDir, workdir);
54864
+ if (rel === ".." || rel.startsWith(`..${sep4}`))
54865
+ return;
54866
+ return rel && rel !== "." ? rel : undefined;
54867
+ }
54868
+ function getRunsDir() {
54869
+ return process.env.NAX_RUNS_DIR ?? join47(globalConfigDir(), "runs");
54870
+ }
54871
+ function getEventsRootDir() {
54872
+ return join47(globalConfigDir(), "events");
54873
+ }
54874
+ var init_paths3 = __esm(() => {
54875
+ init_paths();
54876
+ });
54877
+
54818
54878
  // src/execution/non-blocking-fix.ts
54819
54879
  function shouldRunNonBlockingFix(cfg, advisoryCount) {
54820
54880
  return cfg?.enabled === true && advisoryCount > 0;
@@ -54823,9 +54883,42 @@ function nonBlockingExcludePhases() {
54823
54883
  return REVIEW_PHASE_KINDS;
54824
54884
  }
54825
54885
  function nonBlockingExtraPhases(cfg) {
54826
- return cfg.scope === "both" && cfg.verifierGuard ? ["verifier"] : [];
54886
+ return (cfg.scope === "both" || cfg.scope === "triage") && cfg.verifierGuard ? ["verifier"] : [];
54887
+ }
54888
+ function createMeasureSourceDiff(args) {
54889
+ const packageDirRel = packageDirRelative(args.projectDir, args.packageDir);
54890
+ return async (workdir, fromRef) => {
54891
+ const resolved = await _nonBlockingFixDeps.resolveTestFilePatterns(args.config, args.projectDir, packageDirRel);
54892
+ const isTestFile3 = createTestFileClassifier(resolved);
54893
+ const proc = _nonBlockingFixDeps.spawn(["git", "diff", "--numstat", fromRef], {
54894
+ cwd: workdir,
54895
+ stdout: "pipe",
54896
+ stderr: "pipe"
54897
+ });
54898
+ const stdout = await Bun.readableStreamToText(proc.stdout);
54899
+ const stderr = await Bun.readableStreamToText(proc.stderr);
54900
+ const exitCode = await proc.exited;
54901
+ if (exitCode !== 0) {
54902
+ const detail = stderr.trim() || `exit ${exitCode}`;
54903
+ throw new Error(`[non-blocking-fix] git diff --numstat failed: ${detail}`);
54904
+ }
54905
+ let fileCount = 0;
54906
+ let sourceLineCount = 0;
54907
+ for (const line of stdout.trim().split(`
54908
+ `).filter(Boolean)) {
54909
+ const [addedStr, _deletedStr, filePath] = line.split("\t");
54910
+ if (!filePath || isTestFile3(filePath))
54911
+ continue;
54912
+ fileCount += 1;
54913
+ const added = Number.parseInt(addedStr ?? "", 10);
54914
+ if (Number.isFinite(added))
54915
+ sourceLineCount += added;
54916
+ }
54917
+ return { fileCount, sourceLineCount };
54918
+ };
54827
54919
  }
54828
- async function runNonBlockingFix(args, _deps = DEFAULT_DEPS) {
54920
+ async function runNonBlockingFix(args, overrides = {}) {
54921
+ const _deps = { ...DEFAULT_DEPS, ...overrides };
54829
54922
  const logger = getSafeLogger();
54830
54923
  if (!shouldRunNonBlockingFix(args.cfg, args.advisoryFindings.length)) {
54831
54924
  return { ran: false, kept: false, restored: false };
@@ -54845,9 +54938,34 @@ async function runNonBlockingFix(args, _deps = DEFAULT_DEPS) {
54845
54938
  exhausted = true;
54846
54939
  }
54847
54940
  if (!exhausted) {
54941
+ const cap = args.cfg.sourceDiffCap;
54942
+ if (cap) {
54943
+ let metrics;
54944
+ try {
54945
+ metrics = await _deps.measureSourceDiff(args.workdir, restoreRef);
54946
+ } catch (err) {
54947
+ logger?.warn("non-blocking-fix", "source-diff measurement threw \u2014 restoring", {
54948
+ storyId: args.storyId,
54949
+ error: err instanceof Error ? err.message : String(err)
54950
+ });
54951
+ return restoreToSnapshot(args, _deps, restoreRef, phaseOutputsSnapshot, logger);
54952
+ }
54953
+ if (metrics.fileCount > cap.maxFiles || metrics.sourceLineCount > cap.maxLines) {
54954
+ logger?.info("non-blocking-fix", "source diff exceeded cap \u2014 restoring", {
54955
+ storyId: args.storyId,
54956
+ fileCount: metrics.fileCount,
54957
+ sourceLineCount: metrics.sourceLineCount,
54958
+ cap
54959
+ });
54960
+ return restoreToSnapshot(args, _deps, restoreRef, phaseOutputsSnapshot, logger);
54961
+ }
54962
+ }
54848
54963
  logger?.info("non-blocking-fix", "best-effort fix kept", { storyId: args.storyId });
54849
54964
  return { ran: true, kept: true, restored: false };
54850
54965
  }
54966
+ return restoreToSnapshot(args, _deps, restoreRef, phaseOutputsSnapshot, logger);
54967
+ }
54968
+ async function restoreToSnapshot(args, _deps, restoreRef, phaseOutputsSnapshot, logger) {
54851
54969
  await _deps.rollbackToRef(args.workdir, restoreRef);
54852
54970
  for (const key of Object.keys(args.phaseOutputs))
54853
54971
  delete args.phaseOutputs[key];
@@ -54857,167 +54975,85 @@ async function runNonBlockingFix(args, _deps = DEFAULT_DEPS) {
54857
54975
  });
54858
54976
  return { ran: true, kept: false, restored: true };
54859
54977
  }
54860
- var REVIEW_PHASE_KINDS, DEFAULT_DEPS;
54978
+ var REVIEW_PHASE_KINDS, _nonBlockingFixDeps, DEFAULT_DEPS;
54861
54979
  var init_non_blocking_fix = __esm(() => {
54862
54980
  init_logger2();
54863
54981
  init_rollback();
54982
+ init_test_runners();
54983
+ init_bun_deps();
54984
+ init_paths3();
54864
54985
  REVIEW_PHASE_KINDS = ["semantic-review", "adversarial-review"];
54865
- DEFAULT_DEPS = { captureSnapshotRef, rollbackToRef };
54986
+ _nonBlockingFixDeps = {
54987
+ spawn: typedSpawn,
54988
+ resolveTestFilePatterns
54989
+ };
54990
+ DEFAULT_DEPS = {
54991
+ captureSnapshotRef,
54992
+ rollbackToRef,
54993
+ measureSourceDiff: async () => ({ fileCount: 0, sourceLineCount: 0 })
54994
+ };
54866
54995
  });
54867
54996
 
54868
- // src/execution/story-orchestrator-logging.ts
54869
- function formatPhaseResultMessage(opName, success2, stage, status) {
54870
- if (opName === "greenfield-gate") {
54871
- 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";
54872
- }
54873
- if (status === "skipped") {
54874
- return `Phase skipped: ${opName}`;
54875
- }
54876
- if (stage === "rectification") {
54877
- return `Rectification strategy completed: ${opName}`;
54878
- }
54879
- return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
54880
- }
54881
- function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
54882
- if (output === null || output === undefined || typeof output !== "object")
54883
- return null;
54884
- const r = output;
54885
- const success2 = r.success === true || r.passed === true;
54886
- const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
54887
- const status = typeof r.status === "string" ? r.status : undefined;
54888
- const data = { storyId, phase: opName, durationMs };
54889
- if (findingsCount !== undefined)
54890
- data.findingsCount = findingsCount;
54891
- if (status !== undefined)
54892
- data.status = status;
54893
- if (typeof r.failureCategory === "string")
54894
- data.failureCategory = r.failureCategory;
54895
- if (typeof r.reviewReason === "string")
54896
- data.reviewReason = r.reviewReason;
54897
- return { success: success2, data };
54898
- }
54899
- function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
54900
- if (isTddPhase)
54901
- return;
54902
- if (opName === "semantic-review" || opName === "adversarial-review")
54903
- return;
54904
- const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
54905
- if (!built)
54906
- return;
54907
- const { success: success2 } = built;
54908
- const data = { ...built.data, ...progressData };
54909
- const status = typeof built.data.status === "string" ? built.data.status : undefined;
54910
- const logger = getSafeLogger();
54911
- const message = formatPhaseResultMessage(opName, success2, stage, status);
54912
- if (stage === "rectification") {
54913
- logger?.info("story-orchestrator", message, data);
54914
- return;
54915
- }
54916
- if (success2) {
54917
- logger?.info("story-orchestrator", message, data);
54918
- } else {
54919
- logger?.warn("story-orchestrator", message, data);
54920
- }
54921
- }
54922
- var init_story_orchestrator_logging = __esm(() => {
54923
- init_logger2();
54997
+ // src/execution/story-orchestrator/types.ts
54998
+ var EXHAUSTED_EXIT_REASONS, TDD_OP_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES, STRICT_VERDICT_PHASE_NAMES;
54999
+ var init_types9 = __esm(() => {
55000
+ EXHAUSTED_EXIT_REASONS = new Set([
55001
+ "max-attempts-total",
55002
+ "max-attempts-per-strategy",
55003
+ "bail-when",
55004
+ "no-strategy",
55005
+ "agent-gave-up",
55006
+ "validate-short-circuit"
55007
+ ]);
55008
+ TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
55009
+ CANONICAL_ORDER = [
55010
+ "test-writer",
55011
+ "greenfield-gate",
55012
+ "implementer",
55013
+ "full-suite-gate",
55014
+ "verifier",
55015
+ "verify-scoped",
55016
+ "lint-check",
55017
+ "typecheck-check",
55018
+ "semantic-review",
55019
+ "adversarial-review"
55020
+ ];
55021
+ PHASE_KIND_TO_STATE_KEY = {
55022
+ "test-writer": "testWriter",
55023
+ "greenfield-gate": "greenfieldGate",
55024
+ implementer: "implementer",
55025
+ "full-suite-gate": "fullSuiteGate",
55026
+ verifier: "verifier",
55027
+ "verify-scoped": "verifyScoped",
55028
+ "lint-check": "lintCheck",
55029
+ "typecheck-check": "typecheckCheck",
55030
+ "semantic-review": "semanticReview",
55031
+ "adversarial-review": "adversarialReview"
55032
+ };
55033
+ STRATEGY_TO_REVALIDATION_PHASES = {
55034
+ "mechanical-lintfix": ["lint-check"],
55035
+ "mechanical-formatfix": ["lint-check"],
55036
+ "autofix-implementer": ["lint-check", "typecheck-check", "full-suite-gate", "semantic-review", "adversarial-review"],
55037
+ "autofix-test-writer": ["lint-check", "typecheck-check", "full-suite-gate", "adversarial-review"],
55038
+ "full-suite-rectify": [
55039
+ "lint-check",
55040
+ "typecheck-check",
55041
+ "full-suite-gate",
55042
+ "verifier",
55043
+ "verify-scoped",
55044
+ "semantic-review"
55045
+ ]
55046
+ };
55047
+ STRICT_VERDICT_PHASE_NAMES = new Set([
55048
+ "full-suite-gate",
55049
+ "verify-scoped",
55050
+ "lint-check",
55051
+ "typecheck-check",
55052
+ "verifier"
55053
+ ]);
54924
55054
  });
54925
55055
 
54926
- // src/execution/story-orchestrator.ts
54927
- async function refreshReviewInputForDispatch(opName, input) {
54928
- if (opName !== "semantic-review" && opName !== "adversarial-review")
54929
- return input;
54930
- const i = input;
54931
- const { _refresh } = i;
54932
- if (!_refresh || !i.workdir)
54933
- return input;
54934
- try {
54935
- if (opName === "semantic-review") {
54936
- const { _refresh: _, ...semInput } = input;
54937
- const fresh2 = await _storyOrchestratorDeps.prepareSemanticReviewInput({
54938
- workdir: semInput.workdir,
54939
- projectDir: _refresh.projectDir,
54940
- storyId: _refresh.storyId,
54941
- storyGitRef: _refresh.storyGitRef,
54942
- config: _refresh.config,
54943
- naxIgnoreIndex: _refresh.naxIgnoreIndex,
54944
- resolvedTestPatterns: _refresh.resolvedTestPatterns,
54945
- semanticConfig: semInput.semanticConfig
54946
- });
54947
- return {
54948
- ...semInput,
54949
- stat: fresh2.stat,
54950
- diff: fresh2.diff,
54951
- excludePatterns: fresh2.excludePatterns,
54952
- storyGitRef: fresh2.effectiveRef ?? semInput.storyGitRef
54953
- };
54954
- }
54955
- const { _refresh: __, ...advInput } = input;
54956
- const fresh = await _storyOrchestratorDeps.prepareAdversarialReviewInput({
54957
- workdir: advInput.workdir,
54958
- projectDir: _refresh.projectDir,
54959
- storyId: _refresh.storyId,
54960
- storyGitRef: _refresh.storyGitRef,
54961
- config: _refresh.config,
54962
- naxIgnoreIndex: _refresh.naxIgnoreIndex,
54963
- resolvedTestPatterns: _refresh.resolvedTestPatterns,
54964
- adversarialConfig: advInput.adversarialConfig
54965
- });
54966
- return {
54967
- ...advInput,
54968
- stat: fresh.stat,
54969
- diff: fresh.diff,
54970
- testInventory: fresh.testInventory,
54971
- excludePatterns: fresh.excludePatterns,
54972
- testGlobs: fresh.testGlobs,
54973
- refExcludePatterns: fresh.refExcludePatterns,
54974
- storyGitRef: fresh.effectiveRef ?? advInput.storyGitRef
54975
- };
54976
- } catch (err) {
54977
- getSafeLogger()?.warn("story-orchestrator", "review input refresh failed \u2014 dispatching with stale input", {
54978
- storyId: _refresh.storyId,
54979
- phase: opName,
54980
- error: errorMessage(err)
54981
- });
54982
- const { _refresh: _stripped, ...fallback } = input;
54983
- return fallback;
54984
- }
54985
- }
54986
- function isSlot(value) {
54987
- return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
54988
- }
54989
- function setPhase(state, kind, slot) {
54990
- const key = PHASE_KIND_TO_STATE_KEY[kind];
54991
- if (state[key] !== undefined) {
54992
- throw new NaxError(`StoryOrchestratorBuilder: addX was called twice for phase "${kind}"`, "ORCHESTRATOR_PHASE_DUPLICATE", { stage: "execution", kind });
54993
- }
54994
- state[key] = { kind, slot };
54995
- }
54996
- function collectOrderedPhases(state) {
54997
- return CANONICAL_ORDER.flatMap((kind) => {
54998
- if (kind === "test-writer" && state.testWriter)
54999
- return [state.testWriter];
55000
- if (kind === "greenfield-gate" && state.greenfieldGate)
55001
- return [state.greenfieldGate];
55002
- if (kind === "implementer" && state.implementer)
55003
- return [state.implementer];
55004
- if (kind === "full-suite-gate" && state.fullSuiteGate)
55005
- return [state.fullSuiteGate];
55006
- if (kind === "verifier" && state.verifier)
55007
- return [state.verifier];
55008
- if (kind === "verify-scoped" && state.verifyScoped)
55009
- return [state.verifyScoped];
55010
- if (kind === "lint-check" && state.lintCheck)
55011
- return [state.lintCheck];
55012
- if (kind === "typecheck-check" && state.typecheckCheck)
55013
- return [state.typecheckCheck];
55014
- if (kind === "semantic-review" && state.semanticReview)
55015
- return [state.semanticReview];
55016
- if (kind === "adversarial-review" && state.adversarialReview)
55017
- return [state.adversarialReview];
55018
- return [];
55019
- });
55020
- }
55056
+ // src/execution/story-orchestrator/phase-eval.ts
55021
55057
  function phaseExplicitlyPassed(output) {
55022
55058
  if (output === null || output === undefined || typeof output !== "object")
55023
55059
  return false;
@@ -55075,34 +55111,6 @@ function gateFailureKeys(gateOutput) {
55075
55111
  }
55076
55112
  return keys;
55077
55113
  }
55078
- function shouldSkipPhaseForRectification(phase, state, phaseOutputs) {
55079
- if (phase.kind !== "full-suite-gate")
55080
- return false;
55081
- const verifierName = state.verifier?.slot.op.name;
55082
- if (!verifierName)
55083
- return false;
55084
- return phaseExplicitlyPassed(phaseOutputs[verifierName]);
55085
- }
55086
- function gatherRectificationFindings(phaseOutputs, phases, state) {
55087
- const findings = [];
55088
- for (const phase of phases) {
55089
- if (shouldSkipPhaseForRectification(phase, state, phaseOutputs))
55090
- continue;
55091
- findings.push(...extractPhaseFindings(phaseOutputs[phase.slot.op.name]));
55092
- }
55093
- return findings;
55094
- }
55095
- function collectRectificationPhases(state) {
55096
- return [
55097
- state.fullSuiteGate,
55098
- state.verifier,
55099
- state.verifyScoped,
55100
- state.lintCheck,
55101
- state.typecheckCheck,
55102
- state.semanticReview,
55103
- state.adversarialReview
55104
- ].filter((phase) => phase !== undefined);
55105
- }
55106
55114
  function phasesToRevalidate(strategiesRun, allPhases) {
55107
55115
  if (!strategiesRun || strategiesRun.length === 0)
55108
55116
  return allPhases;
@@ -55122,6 +55130,111 @@ function orderGateLast(phases) {
55122
55130
  const gates = phases.filter((p) => p.kind === "full-suite-gate");
55123
55131
  return [...rest, ...gates];
55124
55132
  }
55133
+ var init_phase_eval = __esm(() => {
55134
+ init_logger2();
55135
+ init_types9();
55136
+ });
55137
+
55138
+ // src/execution/story-orchestrator/phase-state.ts
55139
+ function isSlot(value) {
55140
+ return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
55141
+ }
55142
+ function setPhase(state, kind, slot) {
55143
+ const key = PHASE_KIND_TO_STATE_KEY[kind];
55144
+ if (state[key] !== undefined) {
55145
+ throw new NaxError(`StoryOrchestratorBuilder: addX was called twice for phase "${kind}"`, "ORCHESTRATOR_PHASE_DUPLICATE", { stage: "execution", kind });
55146
+ }
55147
+ state[key] = { kind, slot };
55148
+ }
55149
+ function collectOrderedPhases(state) {
55150
+ return CANONICAL_ORDER.flatMap((kind) => {
55151
+ if (kind === "test-writer" && state.testWriter)
55152
+ return [state.testWriter];
55153
+ if (kind === "greenfield-gate" && state.greenfieldGate)
55154
+ return [state.greenfieldGate];
55155
+ if (kind === "implementer" && state.implementer)
55156
+ return [state.implementer];
55157
+ if (kind === "full-suite-gate" && state.fullSuiteGate)
55158
+ return [state.fullSuiteGate];
55159
+ if (kind === "verifier" && state.verifier)
55160
+ return [state.verifier];
55161
+ if (kind === "verify-scoped" && state.verifyScoped)
55162
+ return [state.verifyScoped];
55163
+ if (kind === "lint-check" && state.lintCheck)
55164
+ return [state.lintCheck];
55165
+ if (kind === "typecheck-check" && state.typecheckCheck)
55166
+ return [state.typecheckCheck];
55167
+ if (kind === "semantic-review" && state.semanticReview)
55168
+ return [state.semanticReview];
55169
+ if (kind === "adversarial-review" && state.adversarialReview)
55170
+ return [state.adversarialReview];
55171
+ return [];
55172
+ });
55173
+ }
55174
+ var init_phase_state = __esm(() => {
55175
+ init_errors();
55176
+ init_types9();
55177
+ });
55178
+
55179
+ // src/execution/story-orchestrator-logging.ts
55180
+ function formatPhaseResultMessage(opName, success2, stage, status) {
55181
+ if (opName === "greenfield-gate") {
55182
+ 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";
55183
+ }
55184
+ if (status === "skipped") {
55185
+ return `Phase skipped: ${opName}`;
55186
+ }
55187
+ if (stage === "rectification") {
55188
+ return `Rectification strategy completed: ${opName}`;
55189
+ }
55190
+ return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
55191
+ }
55192
+ function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
55193
+ if (output === null || output === undefined || typeof output !== "object")
55194
+ return null;
55195
+ const r = output;
55196
+ const success2 = r.success === true || r.passed === true;
55197
+ const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
55198
+ const status = typeof r.status === "string" ? r.status : undefined;
55199
+ const data = { storyId, phase: opName, durationMs };
55200
+ if (findingsCount !== undefined)
55201
+ data.findingsCount = findingsCount;
55202
+ if (status !== undefined)
55203
+ data.status = status;
55204
+ if (typeof r.failureCategory === "string")
55205
+ data.failureCategory = r.failureCategory;
55206
+ if (typeof r.reviewReason === "string")
55207
+ data.reviewReason = r.reviewReason;
55208
+ return { success: success2, data };
55209
+ }
55210
+ function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
55211
+ if (isTddPhase)
55212
+ return;
55213
+ if (opName === "semantic-review" || opName === "adversarial-review")
55214
+ return;
55215
+ const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
55216
+ if (!built)
55217
+ return;
55218
+ const { success: success2 } = built;
55219
+ const data = { ...built.data, ...progressData };
55220
+ const status = typeof built.data.status === "string" ? built.data.status : undefined;
55221
+ const logger = getSafeLogger();
55222
+ const message = formatPhaseResultMessage(opName, success2, stage, status);
55223
+ if (stage === "rectification") {
55224
+ logger?.info("story-orchestrator", message, data);
55225
+ return;
55226
+ }
55227
+ if (success2) {
55228
+ logger?.info("story-orchestrator", message, data);
55229
+ } else {
55230
+ logger?.warn("story-orchestrator", message, data);
55231
+ }
55232
+ }
55233
+ var init_story_orchestrator_logging = __esm(() => {
55234
+ init_logger2();
55235
+ });
55236
+
55237
+ // src/execution/story-orchestrator/review-decision.ts
55125
55238
  function toReviewDecisionPayload(opName, output) {
55126
55239
  if (output === null || output === undefined || typeof output !== "object")
55127
55240
  return null;
@@ -55237,6 +55350,70 @@ function logUnifiedReviewPhaseResult(storyId, opName, output) {
55237
55350
  truncated: findingsCount > findingsSummary.length
55238
55351
  });
55239
55352
  }
55353
+ var init_review_decision = __esm(() => {
55354
+ init_logger2();
55355
+ });
55356
+
55357
+ // src/execution/story-orchestrator/run-phase.ts
55358
+ async function refreshReviewInputForDispatch(opName, input) {
55359
+ if (opName !== "semantic-review" && opName !== "adversarial-review")
55360
+ return input;
55361
+ const i = input;
55362
+ const { _refresh } = i;
55363
+ if (!_refresh || !i.workdir)
55364
+ return input;
55365
+ try {
55366
+ if (opName === "semantic-review") {
55367
+ const { _refresh: _, ...semInput } = input;
55368
+ const fresh2 = await _storyOrchestratorDeps.prepareSemanticReviewInput({
55369
+ workdir: semInput.workdir,
55370
+ projectDir: _refresh.projectDir,
55371
+ storyId: _refresh.storyId,
55372
+ storyGitRef: _refresh.storyGitRef,
55373
+ config: _refresh.config,
55374
+ naxIgnoreIndex: _refresh.naxIgnoreIndex,
55375
+ resolvedTestPatterns: _refresh.resolvedTestPatterns,
55376
+ semanticConfig: semInput.semanticConfig
55377
+ });
55378
+ return {
55379
+ ...semInput,
55380
+ stat: fresh2.stat,
55381
+ diff: fresh2.diff,
55382
+ excludePatterns: fresh2.excludePatterns,
55383
+ storyGitRef: fresh2.effectiveRef ?? semInput.storyGitRef
55384
+ };
55385
+ }
55386
+ const { _refresh: __, ...advInput } = input;
55387
+ const fresh = await _storyOrchestratorDeps.prepareAdversarialReviewInput({
55388
+ workdir: advInput.workdir,
55389
+ projectDir: _refresh.projectDir,
55390
+ storyId: _refresh.storyId,
55391
+ storyGitRef: _refresh.storyGitRef,
55392
+ config: _refresh.config,
55393
+ naxIgnoreIndex: _refresh.naxIgnoreIndex,
55394
+ resolvedTestPatterns: _refresh.resolvedTestPatterns,
55395
+ adversarialConfig: advInput.adversarialConfig
55396
+ });
55397
+ return {
55398
+ ...advInput,
55399
+ stat: fresh.stat,
55400
+ diff: fresh.diff,
55401
+ testInventory: fresh.testInventory,
55402
+ excludePatterns: fresh.excludePatterns,
55403
+ testGlobs: fresh.testGlobs,
55404
+ refExcludePatterns: fresh.refExcludePatterns,
55405
+ storyGitRef: fresh.effectiveRef ?? advInput.storyGitRef
55406
+ };
55407
+ } catch (err) {
55408
+ getSafeLogger()?.warn("story-orchestrator", "review input refresh failed \u2014 dispatching with stale input", {
55409
+ storyId: _refresh.storyId,
55410
+ phase: opName,
55411
+ error: errorMessage(err)
55412
+ });
55413
+ const { _refresh: _stripped, ...fallback } = input;
55414
+ return fallback;
55415
+ }
55416
+ }
55240
55417
  async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = false, progress) {
55241
55418
  const logger = getSafeLogger();
55242
55419
  const opName = slot.op.name;
@@ -55322,6 +55499,57 @@ function withIncreasingFailuresBail(strategies, enabled, consecutiveIncreases) {
55322
55499
  }
55323
55500
  }));
55324
55501
  }
55502
+ var _storyOrchestratorDeps;
55503
+ var init_run_phase = __esm(() => {
55504
+ init_findings();
55505
+ init_logger2();
55506
+ init_operations();
55507
+ init_pipeline();
55508
+ init_review();
55509
+ init_git();
55510
+ init_non_blocking_fix();
55511
+ init_story_orchestrator_logging();
55512
+ init_review_decision();
55513
+ init_types9();
55514
+ _storyOrchestratorDeps = {
55515
+ callOp,
55516
+ runFixCycle,
55517
+ captureGitRef,
55518
+ prepareSemanticReviewInput,
55519
+ prepareAdversarialReviewInput,
55520
+ runNonBlockingFix
55521
+ };
55522
+ });
55523
+
55524
+ // src/execution/story-orchestrator/rectification.ts
55525
+ function shouldSkipPhaseForRectification(phase, state, phaseOutputs) {
55526
+ if (phase.kind !== "full-suite-gate")
55527
+ return false;
55528
+ const verifierName = state.verifier?.slot.op.name;
55529
+ if (!verifierName)
55530
+ return false;
55531
+ return phaseExplicitlyPassed(phaseOutputs[verifierName]);
55532
+ }
55533
+ function gatherRectificationFindings(phaseOutputs, phases, state) {
55534
+ const findings = [];
55535
+ for (const phase of phases) {
55536
+ if (shouldSkipPhaseForRectification(phase, state, phaseOutputs))
55537
+ continue;
55538
+ findings.push(...extractPhaseFindings(phaseOutputs[phase.slot.op.name]));
55539
+ }
55540
+ return findings;
55541
+ }
55542
+ function collectRectificationPhases(state) {
55543
+ return [
55544
+ state.fullSuiteGate,
55545
+ state.verifier,
55546
+ state.verifyScoped,
55547
+ state.lintCheck,
55548
+ state.typecheckCheck,
55549
+ state.semanticReview,
55550
+ state.adversarialReview
55551
+ ].filter((phase) => phase !== undefined);
55552
+ }
55325
55553
  async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides) {
55326
55554
  const rectification = state.rectification;
55327
55555
  const baseValidationPhases = collectRectificationPhases(state);
@@ -55419,7 +55647,15 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides)
55419
55647
  }
55420
55648
  return {};
55421
55649
  }
55650
+ var init_rectification = __esm(() => {
55651
+ init_logger2();
55652
+ init_phase_eval();
55653
+ init_phase_eval();
55654
+ init_run_phase();
55655
+ init_types9();
55656
+ });
55422
55657
 
55658
+ // src/execution/story-orchestrator/execution-plan.ts
55423
55659
  class ExecutionPlan {
55424
55660
  ctx;
55425
55661
  state;
@@ -55542,7 +55778,7 @@ class ExecutionPlan {
55542
55778
  const advisoryOut = phaseOutputs["adversarial-review"];
55543
55779
  const advisoryFindings = advisoryOut?.advisoryFindings ?? [];
55544
55780
  if (advCfg && this.state.rectification && this.ctx.storyId && shouldRunNonBlockingFix(advCfg, advisoryFindings.length)) {
55545
- await runNonBlockingFix({
55781
+ await _storyOrchestratorDeps.runNonBlockingFix({
55546
55782
  workdir: this.ctx.packageDir,
55547
55783
  storyId: this.ctx.storyId,
55548
55784
  advisoryFindings,
@@ -55556,6 +55792,12 @@ class ExecutionPlan {
55556
55792
  maxAttempts,
55557
55793
  postValidate: this.state.nonBlockingFixPostValidate
55558
55794
  })
55795
+ }, {
55796
+ measureSourceDiff: createMeasureSourceDiff({
55797
+ config: this.ctx.runtime.configLoader.current(),
55798
+ projectDir: this.ctx.runtime.projectDir,
55799
+ packageDir: this.ctx.packageDir
55800
+ })
55559
55801
  });
55560
55802
  }
55561
55803
  const verifierName = this.state.verifier?.slot.op.name;
@@ -55567,18 +55809,29 @@ class ExecutionPlan {
55567
55809
  } else if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName], this.ctx.storyId)) {
55568
55810
  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 });
55569
55811
  }
55570
- const success2 = Object.entries(phaseOutputs).every(([name, output]) => {
55812
+ const requiredReviewPhaseNames = [
55813
+ this.state.semanticReview?.slot.op.name,
55814
+ this.state.adversarialReview?.slot.op.name
55815
+ ].filter((name) => name !== undefined);
55816
+ const missingRequiredReviewPhases = requiredReviewPhaseNames.filter((name) => !(name in phaseOutputs));
55817
+ if (missingRequiredReviewPhases.length > 0) {
55818
+ 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 });
55819
+ }
55820
+ const success2 = missingRequiredReviewPhases.length === 0 && Object.entries(phaseOutputs).every(([name, output]) => {
55571
55821
  if (verifierPassedSsot && name === gateName)
55572
55822
  return true;
55573
55823
  return phasePassed(name, output, this.ctx.storyId);
55574
55824
  });
55575
55825
  const totalCostUsd = Object.values(phaseCosts).reduce((sum, cost) => sum + cost, 0);
55576
55826
  const durationMs = Date.now() - startedAt;
55577
- const failedPhases = Object.entries(phaseOutputs).filter(([name, output]) => {
55578
- if (verifierPassedSsot && name === gateName)
55579
- return false;
55580
- return !phasePassed(name, output, this.ctx.storyId);
55581
- }).map(([name]) => name);
55827
+ const failedPhases = [
55828
+ ...Object.entries(phaseOutputs).filter(([name, output]) => {
55829
+ if (verifierPassedSsot && name === gateName)
55830
+ return false;
55831
+ return !phasePassed(name, output, this.ctx.storyId);
55832
+ }).map(([name]) => name),
55833
+ ...missingRequiredReviewPhases.map((name) => `${name} (never ran)`)
55834
+ ];
55582
55835
  const summary = {
55583
55836
  storyId: this.ctx.storyId,
55584
55837
  success: success2,
@@ -55591,6 +55844,8 @@ class ExecutionPlan {
55591
55844
  summary.rectificationExhausted = true;
55592
55845
  if (rectResult.unfixedFindings)
55593
55846
  summary.unfixedFindingsCount = rectResult.unfixedFindings.length;
55847
+ if (missingRequiredReviewPhases.length > 0)
55848
+ summary.missingRequiredReviewPhases = missingRequiredReviewPhases;
55594
55849
  if (success2) {
55595
55850
  logger?.info("story-orchestrator", "Story orchestration complete", summary);
55596
55851
  } else {
@@ -55603,11 +55858,21 @@ class ExecutionPlan {
55603
55858
  durationMs,
55604
55859
  phaseOutputs,
55605
55860
  ...rectResult,
55606
- gateRegressedDuringRect
55861
+ gateRegressedDuringRect,
55862
+ missingRequiredReviewPhases: missingRequiredReviewPhases.length > 0 ? missingRequiredReviewPhases : undefined
55607
55863
  };
55608
55864
  }
55609
55865
  }
55866
+ var init_execution_plan = __esm(() => {
55867
+ init_logger2();
55868
+ init_non_blocking_fix();
55869
+ init_phase_eval();
55870
+ init_phase_state();
55871
+ init_rectification();
55872
+ init_run_phase();
55873
+ });
55610
55874
 
55875
+ // src/execution/story-orchestrator/builder.ts
55611
55876
  class StoryOrchestratorBuilder {
55612
55877
  state = {};
55613
55878
  addImplementer(value) {
@@ -55667,83 +55932,25 @@ class StoryOrchestratorBuilder {
55667
55932
  return new ExecutionPlan(ctx, { ...this.state }, opts.isThreeSession ?? false);
55668
55933
  }
55669
55934
  }
55670
- var _storyOrchestratorDeps, EXHAUSTED_EXIT_REASONS, TDD_OP_NAMES, STRICT_VERDICT_PHASE_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
55671
- var init_story_orchestrator = __esm(() => {
55935
+ var init_builder2 = __esm(() => {
55672
55936
  init_errors();
55673
- init_findings();
55674
- init_logger2();
55675
55937
  init_operations();
55676
- init_call();
55677
- init_event_bus();
55678
- init_prepare_inputs();
55679
- init_git();
55680
- init_non_blocking_fix();
55681
- init_story_orchestrator_logging();
55682
- _storyOrchestratorDeps = {
55683
- callOp,
55684
- runFixCycle,
55685
- captureGitRef,
55686
- prepareSemanticReviewInput,
55687
- prepareAdversarialReviewInput
55688
- };
55689
- EXHAUSTED_EXIT_REASONS = new Set([
55690
- "max-attempts-total",
55691
- "max-attempts-per-strategy",
55692
- "bail-when",
55693
- "no-strategy",
55694
- "agent-gave-up",
55695
- "validate-short-circuit"
55696
- ]);
55697
- TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
55698
- STRICT_VERDICT_PHASE_NAMES = new Set([
55699
- fullSuiteGateOp.name,
55700
- verifyScopedOp.name,
55701
- lintCheckOp.name,
55702
- typecheckCheckOp.name,
55703
- verifierOp.name
55704
- ]);
55705
- CANONICAL_ORDER = [
55706
- "test-writer",
55707
- "greenfield-gate",
55708
- "implementer",
55709
- "full-suite-gate",
55710
- "verifier",
55711
- "verify-scoped",
55712
- "lint-check",
55713
- "typecheck-check",
55714
- "semantic-review",
55715
- "adversarial-review"
55716
- ];
55717
- PHASE_KIND_TO_STATE_KEY = {
55718
- "test-writer": "testWriter",
55719
- "greenfield-gate": "greenfieldGate",
55720
- implementer: "implementer",
55721
- "full-suite-gate": "fullSuiteGate",
55722
- verifier: "verifier",
55723
- "verify-scoped": "verifyScoped",
55724
- "lint-check": "lintCheck",
55725
- "typecheck-check": "typecheckCheck",
55726
- "semantic-review": "semanticReview",
55727
- "adversarial-review": "adversarialReview"
55728
- };
55729
- STRATEGY_TO_REVALIDATION_PHASES = {
55730
- "mechanical-lintfix": ["lint-check"],
55731
- "mechanical-formatfix": ["lint-check"],
55732
- "autofix-implementer": ["lint-check", "typecheck-check", "full-suite-gate", "semantic-review", "adversarial-review"],
55733
- "autofix-test-writer": ["lint-check", "typecheck-check", "full-suite-gate", "adversarial-review"],
55734
- "full-suite-rectify": [
55735
- "lint-check",
55736
- "typecheck-check",
55737
- "full-suite-gate",
55738
- "verifier",
55739
- "verify-scoped",
55740
- "semantic-review"
55741
- ]
55742
- };
55938
+ init_execution_plan();
55939
+ init_phase_state();
55940
+ });
55941
+
55942
+ // src/execution/story-orchestrator/index.ts
55943
+ var init_story_orchestrator = __esm(() => {
55944
+ init_builder2();
55945
+ init_execution_plan();
55946
+ init_phase_eval();
55947
+ init_rectification();
55948
+ init_run_phase();
55949
+ init_types9();
55743
55950
  });
55744
55951
 
55745
55952
  // src/execution/build-plan-for-strategy.ts
55746
- import { join as join47 } from "path";
55953
+ import { join as join48 } from "path";
55747
55954
  function requiresInitialRefCapture(strategy) {
55748
55955
  return isThreeSessionStrategy(strategy);
55749
55956
  }
@@ -55787,7 +55994,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
55787
55994
  if (inputs.adversarialReview) {
55788
55995
  builder.addAdversarialReview(inputs.adversarialReview);
55789
55996
  }
55790
- const packageDir = join47(ctx.packageDir, story.workdir ?? "");
55997
+ const packageDir = join48(ctx.packageDir, story.workdir ?? "");
55791
55998
  const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
55792
55999
  if (shouldRunRectification(config2) && inputs.rectification) {
55793
56000
  const sink = makeDeclarationSink();
@@ -55842,6 +56049,12 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
55842
56049
  nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
55843
56050
  includeAdversarialReview: true
55844
56051
  }));
56052
+ } else if (nbf.scope === "triage") {
56053
+ nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
56054
+ adversarialReviewByFixTarget: "source"
56055
+ }), makeAutofixTestWriterStrategy(story, config2, nbSink, {
56056
+ includeAdversarialReview: false
56057
+ }));
55845
56058
  } else {
55846
56059
  nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
55847
56060
  includeAdversarialReview: false
@@ -55876,26 +56089,6 @@ var init_build_plan_for_strategy = __esm(() => {
55876
56089
  init_story_orchestrator();
55877
56090
  });
55878
56091
 
55879
- // src/utils/paths.ts
55880
- import { join as join48, relative as relative13, sep as sep4 } from "path";
55881
- function packageDirRelative(projectDir, workdir) {
55882
- if (!projectDir || !workdir || workdir === projectDir)
55883
- return;
55884
- const rel = relative13(projectDir, workdir);
55885
- if (rel === ".." || rel.startsWith(`..${sep4}`))
55886
- return;
55887
- return rel && rel !== "." ? rel : undefined;
55888
- }
55889
- function getRunsDir() {
55890
- return process.env.NAX_RUNS_DIR ?? join48(globalConfigDir(), "runs");
55891
- }
55892
- function getEventsRootDir() {
55893
- return join48(globalConfigDir(), "events");
55894
- }
55895
- var init_paths3 = __esm(() => {
55896
- init_paths();
55897
- });
55898
-
55899
56092
  // src/execution/plan-inputs.ts
55900
56093
  function validatePlanInputs(story, config2) {
55901
56094
  if (!story.id || story.id.trim() === "") {
@@ -56140,7 +56333,7 @@ function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason, failure
56140
56333
  }
56141
56334
  return { action: "escalate", reason: buildReason("isolation-violation") };
56142
56335
  }
56143
- if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "full-suite-gate-exhausted" || failureCategory === "verifier-rejected" || failureCategory === "runtime-crash") {
56336
+ if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "full-suite-gate-exhausted" || failureCategory === "verifier-rejected" || failureCategory === "runtime-crash" || failureCategory === "review-incomplete") {
56144
56337
  return { action: "escalate", reason: buildReason(failureCategory) };
56145
56338
  }
56146
56339
  if (failureCategory === "greenfield-no-tests") {
@@ -56239,22 +56432,8 @@ async function closeAllRunSessions(sessionManager, agentGetFn, opts) {
56239
56432
  return totalClosed;
56240
56433
  }
56241
56434
 
56242
- // src/execution/post-run.ts
56243
- function shouldRollbackTddFailure(tddMode, failureCategory) {
56244
- return tddMode?.rollbackEnabled === true && failureCategory === "isolation-violation";
56245
- }
56246
- function extractPauseReason(phaseOutputs) {
56247
- for (const output of Object.values(phaseOutputs)) {
56248
- if (output !== null && typeof output === "object") {
56249
- const record2 = output;
56250
- if (typeof record2.pauseReason === "string" && record2.pauseReason) {
56251
- return record2.pauseReason;
56252
- }
56253
- }
56254
- }
56255
- return;
56256
- }
56257
- function deriveTddFailureCategory(phaseOutputs, unfixedFindings, gateRegressedDuringRect) {
56435
+ // src/execution/tdd-failure-category.ts
56436
+ function deriveTddFailureCategory(phaseOutputs, unfixedFindings, gateRegressedDuringRect, missingRequiredReviewPhases) {
56258
56437
  const testWriterOutput = phaseOutputs[testWriterOp.name];
56259
56438
  if (testWriterOutput?.success === false) {
56260
56439
  return "session-failure";
@@ -56293,6 +56472,29 @@ function deriveTddFailureCategory(phaseOutputs, unfixedFindings, gateRegressedDu
56293
56472
  if (implOutput?.success === false) {
56294
56473
  return "session-failure";
56295
56474
  }
56475
+ if (missingRequiredReviewPhases && missingRequiredReviewPhases.length > 0) {
56476
+ return "review-incomplete";
56477
+ }
56478
+ return;
56479
+ }
56480
+ var init_tdd_failure_category = __esm(() => {
56481
+ init_operations();
56482
+ init_story_orchestrator();
56483
+ });
56484
+
56485
+ // src/execution/post-run.ts
56486
+ function shouldRollbackTddFailure(tddMode, failureCategory) {
56487
+ return tddMode?.rollbackEnabled === true && failureCategory === "isolation-violation";
56488
+ }
56489
+ function extractPauseReason(phaseOutputs) {
56490
+ for (const output of Object.values(phaseOutputs)) {
56491
+ if (output !== null && typeof output === "object") {
56492
+ const record2 = output;
56493
+ if (typeof record2.pauseReason === "string" && record2.pauseReason) {
56494
+ return record2.pauseReason;
56495
+ }
56496
+ }
56497
+ }
56296
56498
  return;
56297
56499
  }
56298
56500
  async function cleanupSessionOnFailure(ctx) {
@@ -56390,7 +56592,7 @@ async function applyPostRunInspection(ctx, planResult, opts) {
56390
56592
  }
56391
56593
  }
56392
56594
  const pauseReason = extractPauseReason(planResult.phaseOutputs);
56393
- const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs, planResult.unfixedFindings, planResult.gateRegressedDuringRect) : undefined;
56595
+ const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs, planResult.unfixedFindings, planResult.gateRegressedDuringRect, planResult.missingRequiredReviewPhases) : undefined;
56394
56596
  if (isTdd && !planResult.success && !failureCategory) {
56395
56597
  const phaseSignals = {};
56396
56598
  for (const [name, output] of Object.entries(planResult.phaseOutputs)) {
@@ -56609,7 +56811,7 @@ var init_post_run = __esm(() => {
56609
56811
  init_scratch_writer();
56610
56812
  init_rollback();
56611
56813
  init_git();
56612
- init_story_orchestrator();
56814
+ init_tdd_failure_category();
56613
56815
  _postRunDeps = {
56614
56816
  detectMergeConflict,
56615
56817
  checkMergeConflict,
@@ -58746,17 +58948,46 @@ var init_setup_verify = __esm(() => {
58746
58948
  };
58747
58949
  });
58748
58950
 
58951
+ // src/cli/setup-write.ts
58952
+ import { join as join57 } from "path";
58953
+ async function writeSetupConfig(workdir, config2, monoConfigs, _opts, deps = _writeSetupDeps) {
58954
+ const naxDir = join57(workdir, ".nax");
58955
+ const naxConfigPath = join57(naxDir, "config.json");
58956
+ const written = [];
58957
+ await deps.mkdir(naxDir);
58958
+ await deps.writeFile(naxConfigPath, JSON.stringify(config2, null, 2));
58959
+ written.push(naxConfigPath);
58960
+ for (const mc of monoConfigs) {
58961
+ const monoDir = join57(naxDir, "mono", mc.relativeDir);
58962
+ const monoConfigPath = join57(monoDir, "config.json");
58963
+ await deps.mkdir(monoDir);
58964
+ await deps.writeFile(monoConfigPath, JSON.stringify(mc.config, null, 2));
58965
+ written.push(monoConfigPath);
58966
+ }
58967
+ return { written };
58968
+ }
58969
+ var _writeSetupDeps;
58970
+ var init_setup_write = __esm(() => {
58971
+ _writeSetupDeps = {
58972
+ writeFile: (path14, content) => Bun.write(path14, content).then(() => {}),
58973
+ mkdir: async (path14) => {
58974
+ const proc = Bun.spawn(["mkdir", "-p", path14]);
58975
+ await proc.exited;
58976
+ }
58977
+ };
58978
+ });
58979
+
58749
58980
  // src/cli/setup.ts
58750
58981
  var exports_setup = {};
58751
58982
  __export(exports_setup, {
58752
58983
  setupCommand: () => setupCommand,
58753
58984
  _setupDeps: () => _setupDeps
58754
58985
  });
58755
- import { join as join57 } from "path";
58986
+ import { join as join58 } from "path";
58756
58987
  async function setupCommand(options = {}) {
58757
58988
  const workdir = options.dir ?? process.cwd();
58758
- const naxDir = join57(workdir, ".nax");
58759
- const naxConfigPath = join57(naxDir, "config.json");
58989
+ const naxDir = join58(workdir, ".nax");
58990
+ const naxConfigPath = join58(naxDir, "config.json");
58760
58991
  const exists = await _setupDeps.fileExists(naxConfigPath);
58761
58992
  if (exists && !options.force) {
58762
58993
  _setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
@@ -58786,13 +59017,7 @@ ${JSON.stringify(plan.config, null, 2)}`);
58786
59017
  if (options.fillScripts) {
58787
59018
  await _setupDeps.fillScripts(workdir, analysis);
58788
59019
  }
58789
- await _setupDeps.mkdir(naxDir);
58790
- await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
58791
- for (const mc of plan.monoConfigs) {
58792
- const monoDir = join57(naxDir, "mono", mc.relativeDir);
58793
- await _setupDeps.mkdir(monoDir);
58794
- await _setupDeps.writeFile(join57(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
58795
- }
59020
+ await _setupDeps.writeSetupConfig(workdir, plan.config, plan.monoConfigs, { force: options.force });
58796
59021
  const gateResult = await _setupDeps.runGate(workdir, plan.config);
58797
59022
  if (gateResult !== 0) {
58798
59023
  return gateResult;
@@ -58809,6 +59034,7 @@ var init_setup = __esm(() => {
58809
59034
  init_setup_fill();
58810
59035
  init_setup_llm();
58811
59036
  init_setup_verify();
59037
+ init_setup_write();
58812
59038
  _setupDeps = {
58813
59039
  analyzeRepo,
58814
59040
  fillScripts,
@@ -58830,11 +59056,7 @@ var init_setup = __esm(() => {
58830
59056
  generateSetupPlan: (ctx, analysis) => generateSetupPlan(ctx, analysis),
58831
59057
  runGate: (workdir, config2) => runSetupGate(workdir, config2),
58832
59058
  fileExists: (path14) => Bun.file(path14).exists(),
58833
- writeFile: (path14, content) => Bun.write(path14, content).then(() => {}),
58834
- mkdir: async (path14) => {
58835
- const proc = Bun.spawn(["mkdir", "-p", path14]);
58836
- await proc.exited;
58837
- },
59059
+ writeSetupConfig: (workdir, config2, monoConfigs, opts) => writeSetupConfig(workdir, config2, monoConfigs, opts),
58838
59060
  stdout: (msg) => {
58839
59061
  process.stdout.write(`${msg}
58840
59062
  `);
@@ -60273,7 +60495,7 @@ var init_command_argv = __esm(() => {
60273
60495
  });
60274
60496
 
60275
60497
  // src/hooks/runner.ts
60276
- import { join as join74 } from "path";
60498
+ import { join as join75 } from "path";
60277
60499
  function createDrainDeadline2(deadlineMs) {
60278
60500
  let timeoutId;
60279
60501
  const promise2 = new Promise((resolve16) => {
@@ -60292,14 +60514,14 @@ async function loadHooksConfig(projectDir, globalDir) {
60292
60514
  let globalHooks = { hooks: {} };
60293
60515
  let projectHooks = { hooks: {} };
60294
60516
  let skipGlobal = false;
60295
- const projectPath = join74(projectDir, "hooks.json");
60517
+ const projectPath = join75(projectDir, "hooks.json");
60296
60518
  const projectData = await loadJsonFile(projectPath, "hooks");
60297
60519
  if (projectData) {
60298
60520
  projectHooks = projectData;
60299
60521
  skipGlobal = projectData.skipGlobal ?? false;
60300
60522
  }
60301
60523
  if (!skipGlobal && globalDir) {
60302
- const globalPath = join74(globalDir, "hooks.json");
60524
+ const globalPath = join75(globalDir, "hooks.json");
60303
60525
  const globalData = await loadJsonFile(globalPath, "hooks");
60304
60526
  if (globalData) {
60305
60527
  globalHooks = globalData;
@@ -60469,7 +60691,7 @@ var package_default;
60469
60691
  var init_package = __esm(() => {
60470
60692
  package_default = {
60471
60693
  name: "@nathapp/nax",
60472
- version: "0.70.0",
60694
+ version: "0.70.2",
60473
60695
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
60474
60696
  type: "module",
60475
60697
  bin: {
@@ -60480,7 +60702,7 @@ var init_package = __esm(() => {
60480
60702
  dev: "bun run bin/nax.ts",
60481
60703
  build: 'bun build bin/nax.ts --outdir dist --target bun --define "GIT_COMMIT=\\"$(git rev-parse --short HEAD)\\""',
60482
60704
  typecheck: "bun x tsc --noEmit && bun x tsc --noEmit -p tsconfig.contracts.json",
60483
- 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",
60705
+ 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",
60484
60706
  "lint:json": "bun x biome check src/ bin/ --reporter json && bun run check:nax-error 1>&2 && bun run check:logger-storyid 1>&2",
60485
60707
  "lint:fix": "bun x biome check --write src/ bin/",
60486
60708
  "check:no-real-global-nax": "bun run scripts/check-no-real-global-nax.ts",
@@ -60491,6 +60713,8 @@ var init_package = __esm(() => {
60491
60713
  "check:nax-error:update": "bun run scripts/check-nax-error.ts --update-baseline",
60492
60714
  "check:logger-storyid": "bun run scripts/check-logger-storyid.ts",
60493
60715
  "check:logger-storyid:update": "bun run scripts/check-logger-storyid.ts --update-baseline",
60716
+ "check:file-sizes": "bun run scripts/check-file-sizes.ts",
60717
+ "check:file-sizes:update": "bun run scripts/check-file-sizes.ts --update-baseline",
60494
60718
  release: "bun scripts/release.ts",
60495
60719
  test: "AGENT=1 bun run scripts/run-tests.ts",
60496
60720
  "test:verbose": "bun run scripts/run-tests.ts",
@@ -60502,6 +60726,8 @@ var init_package = __esm(() => {
60502
60726
  "test:integration": "bun test ./test/integration/ --timeout=60000",
60503
60727
  "test:ui": "bun test ./test/ui/ --timeout=60000",
60504
60728
  "test:e2e": "timeout -k 5s 180s bun test test/e2e/ --timeout=60000",
60729
+ "test:coverage": "bun run scripts/check-coverage.ts",
60730
+ "test:coverage:report": "bun run scripts/check-coverage.ts --report",
60505
60731
  "check-test-overlap": "bun run scripts/check-test-overlap.ts",
60506
60732
  "check-dead-tests": "bun run scripts/check-dead-tests.ts",
60507
60733
  "check:test-sizes": "bun run scripts/check-test-sizes.ts",
@@ -60565,8 +60791,8 @@ var init_version = __esm(() => {
60565
60791
  NAX_VERSION = package_default.version;
60566
60792
  NAX_COMMIT = (() => {
60567
60793
  try {
60568
- if (/^[0-9a-f]{6,10}$/.test("0be1d11a"))
60569
- return "0be1d11a";
60794
+ if (/^[0-9a-f]{6,10}$/.test("0e202cfa"))
60795
+ return "0e202cfa";
60570
60796
  } catch {}
60571
60797
  try {
60572
60798
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -61444,15 +61670,15 @@ var init_acceptance_loop = __esm(() => {
61444
61670
 
61445
61671
  // src/session/scratch-purge.ts
61446
61672
  import { mkdir as mkdir12, rename, rm } from "fs/promises";
61447
- import { dirname as dirname12, join as join75 } from "path";
61673
+ import { dirname as dirname12, join as join76 } from "path";
61448
61674
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
61449
- const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
61675
+ const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
61450
61676
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
61451
61677
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
61452
61678
  let purged = 0;
61453
61679
  for (const sessionId of sessionIds) {
61454
- const sessionDir = join75(sessionsDir, sessionId);
61455
- const descriptorPath = join75(sessionDir, "descriptor.json");
61680
+ const sessionDir = join76(sessionsDir, sessionId);
61681
+ const descriptorPath = join76(sessionDir, "descriptor.json");
61456
61682
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
61457
61683
  continue;
61458
61684
  let lastActivityAt;
@@ -61468,7 +61694,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
61468
61694
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
61469
61695
  continue;
61470
61696
  if (archiveInsteadOfDelete) {
61471
- const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
61697
+ const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
61472
61698
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
61473
61699
  } else {
61474
61700
  await _scratchPurgeDeps.remove(sessionDir);
@@ -62173,7 +62399,7 @@ function outputRunFooter(options) {
62173
62399
  }
62174
62400
  var init_headless_formatter = __esm(() => {
62175
62401
  init_source();
62176
- init_logging();
62402
+ init_log_format();
62177
62403
  init_version();
62178
62404
  });
62179
62405
 
@@ -62219,12 +62445,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
62219
62445
 
62220
62446
  // src/pipeline/subscribers/events-writer.ts
62221
62447
  import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
62222
- import { basename as basename13, join as join76 } from "path";
62448
+ import { basename as basename13, join as join77 } from "path";
62223
62449
  function wireEventsWriter(bus, feature, runId, workdir) {
62224
62450
  const logger = getSafeLogger();
62225
62451
  const project = basename13(workdir);
62226
- const eventsDir = join76(getEventsRootDir(), project);
62227
- const eventsFile = join76(eventsDir, "events.jsonl");
62452
+ const eventsDir = join77(getEventsRootDir(), project);
62453
+ const eventsFile = join77(eventsDir, "events.jsonl");
62228
62454
  let dirReady = false;
62229
62455
  const write = (line) => {
62230
62456
  return (async () => {
@@ -62405,12 +62631,12 @@ var init_interaction2 = __esm(() => {
62405
62631
 
62406
62632
  // src/pipeline/subscribers/registry.ts
62407
62633
  import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
62408
- import { basename as basename14, join as join77 } from "path";
62634
+ import { basename as basename14, join as join78 } from "path";
62409
62635
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
62410
62636
  const logger = getSafeLogger();
62411
62637
  const project = basename14(workdir);
62412
- const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
62413
- const metaFile = join77(runDir, "meta.json");
62638
+ const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
62639
+ const metaFile = join78(runDir, "meta.json");
62414
62640
  const unsub = bus.on("run:started", (_ev) => {
62415
62641
  return (async () => {
62416
62642
  try {
@@ -62420,8 +62646,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
62420
62646
  project,
62421
62647
  feature,
62422
62648
  workdir,
62423
- statusPath: join77(outputDir, "features", feature, "status.json"),
62424
- eventsDir: join77(outputDir, "features", feature, "runs"),
62649
+ statusPath: join78(outputDir, "features", feature, "status.json"),
62650
+ eventsDir: join78(outputDir, "features", feature, "runs"),
62425
62651
  registeredAt: new Date().toISOString()
62426
62652
  };
62427
62653
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -62653,7 +62879,7 @@ function buildPreviewRouting(story, config2) {
62653
62879
 
62654
62880
  // src/worktree/types.ts
62655
62881
  var WorktreeDependencyPreparationError;
62656
- var init_types9 = __esm(() => {
62882
+ var init_types10 = __esm(() => {
62657
62883
  WorktreeDependencyPreparationError = class WorktreeDependencyPreparationError extends Error {
62658
62884
  mode;
62659
62885
  failureCategory = "dependency-prep";
@@ -62667,7 +62893,7 @@ var init_types9 = __esm(() => {
62667
62893
 
62668
62894
  // src/worktree/dependencies.ts
62669
62895
  import { existsSync as existsSync30 } from "fs";
62670
- import { join as join78 } from "path";
62896
+ import { join as join79 } from "path";
62671
62897
  async function prepareWorktreeDependencies(options) {
62672
62898
  const mode = options.config.execution.worktreeDependencies.mode;
62673
62899
  const resolvedCwd = resolveDependencyCwd(options);
@@ -62681,7 +62907,7 @@ async function prepareWorktreeDependencies(options) {
62681
62907
  }
62682
62908
  }
62683
62909
  function resolveDependencyCwd(options) {
62684
- return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
62910
+ return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
62685
62911
  }
62686
62912
  function resolveInheritedDependencies(options, resolvedCwd) {
62687
62913
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -62691,7 +62917,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
62691
62917
  }
62692
62918
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
62693
62919
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
62694
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join78(directory, filename))));
62920
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
62695
62921
  }
62696
62922
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
62697
62923
  const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
@@ -62723,7 +62949,7 @@ var PHASE_ONE_INHERIT_UNSUPPORTED_FILES, _worktreeDependencyDeps;
62723
62949
  var init_dependencies = __esm(() => {
62724
62950
  init_bun_deps();
62725
62951
  init_command_argv();
62726
- init_types9();
62952
+ init_types10();
62727
62953
  PHASE_ONE_INHERIT_UNSUPPORTED_FILES = [
62728
62954
  "package.json",
62729
62955
  "bun.lock",
@@ -62755,13 +62981,13 @@ __export(exports_manager, {
62755
62981
  });
62756
62982
  import { existsSync as existsSync31, symlinkSync } from "fs";
62757
62983
  import { mkdir as mkdir15 } from "fs/promises";
62758
- import { join as join79 } from "path";
62984
+ import { join as join80 } from "path";
62759
62985
 
62760
62986
  class WorktreeManager {
62761
62987
  async ensureGitExcludes(projectRoot) {
62762
62988
  const logger = getSafeLogger();
62763
- const infoDir = join79(projectRoot, ".git", "info");
62764
- const excludePath = join79(infoDir, "exclude");
62989
+ const infoDir = join80(projectRoot, ".git", "info");
62990
+ const excludePath = join80(infoDir, "exclude");
62765
62991
  try {
62766
62992
  await mkdir15(infoDir, { recursive: true });
62767
62993
  let existing = "";
@@ -62788,7 +63014,7 @@ ${missing.join(`
62788
63014
  }
62789
63015
  async create(projectRoot, storyId) {
62790
63016
  validateStoryId(storyId);
62791
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
63017
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62792
63018
  const branchName = `nax/${storyId}`;
62793
63019
  try {
62794
63020
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -62828,9 +63054,9 @@ ${missing.join(`
62828
63054
  }
62829
63055
  throw new Error(`Failed to create worktree: ${String(error48)}`);
62830
63056
  }
62831
- const envSource = join79(projectRoot, ".env");
63057
+ const envSource = join80(projectRoot, ".env");
62832
63058
  if (existsSync31(envSource)) {
62833
- const envTarget = join79(worktreePath, ".env");
63059
+ const envTarget = join80(worktreePath, ".env");
62834
63060
  try {
62835
63061
  symlinkSync(envSource, envTarget, "file");
62836
63062
  } catch (error48) {
@@ -62841,7 +63067,7 @@ ${missing.join(`
62841
63067
  }
62842
63068
  async remove(projectRoot, storyId) {
62843
63069
  validateStoryId(storyId);
62844
- const worktreePath = join79(projectRoot, ".nax-wt", storyId);
63070
+ const worktreePath = join80(projectRoot, ".nax-wt", storyId);
62845
63071
  const branchName = `nax/${storyId}`;
62846
63072
  try {
62847
63073
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -63425,6 +63651,8 @@ function resolveMaxAttemptsOutcome(failureCategory) {
63425
63651
  return "pause";
63426
63652
  case "runtime-crash":
63427
63653
  return "pause";
63654
+ case "review-incomplete":
63655
+ return "pause";
63428
63656
  case "session-failure":
63429
63657
  case "tests-failing":
63430
63658
  case "full-suite-gate-exhausted":
@@ -63663,10 +63891,10 @@ var init_merge_conflict_rectify = __esm(() => {
63663
63891
  });
63664
63892
 
63665
63893
  // src/execution/pipeline-result-handler.ts
63666
- import { join as join80 } from "path";
63894
+ import { join as join81 } from "path";
63667
63895
  async function removeWorktreeDirectory(projectRoot, storyId) {
63668
63896
  const logger = getSafeLogger();
63669
- const worktreePath = join80(projectRoot, ".nax-wt", storyId);
63897
+ const worktreePath = join81(projectRoot, ".nax-wt", storyId);
63670
63898
  try {
63671
63899
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
63672
63900
  cwd: projectRoot,
@@ -63883,7 +64111,7 @@ var init_pipeline_result_handler = __esm(() => {
63883
64111
 
63884
64112
  // src/execution/iteration-runner.ts
63885
64113
  import { existsSync as existsSync32 } from "fs";
63886
- import { join as join81 } from "path";
64114
+ import { join as join82 } from "path";
63887
64115
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
63888
64116
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
63889
64117
  if (ctx.dryRun) {
@@ -63908,7 +64136,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63908
64136
  const storyStartTime = Date.now();
63909
64137
  let effectiveWorkdir = ctx.workdir;
63910
64138
  if (ctx.config.execution.storyIsolation === "worktree") {
63911
- const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
64139
+ const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
63912
64140
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
63913
64141
  if (!worktreeExists) {
63914
64142
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -63928,7 +64156,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63928
64156
  }
63929
64157
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
63930
64158
  const profileOverride = profileOverrideFromConfig(ctx.config);
63931
- const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join81(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
64159
+ const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join82(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
63932
64160
  let dependencyContext;
63933
64161
  if (ctx.config.execution.storyIsolation === "worktree") {
63934
64162
  try {
@@ -63955,7 +64183,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
63955
64183
  };
63956
64184
  }
63957
64185
  }
63958
- 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;
64186
+ 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;
63959
64187
  const pipelineContext = {
63960
64188
  config: effectiveConfig,
63961
64189
  rootConfig: ctx.config,
@@ -64158,7 +64386,7 @@ __export(exports_parallel_worker, {
64158
64386
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
64159
64387
  _parallelWorkerDeps: () => _parallelWorkerDeps
64160
64388
  });
64161
- import { join as join82 } from "path";
64389
+ import { join as join83 } from "path";
64162
64390
  function buildWorktreePipelineContext(base, _story) {
64163
64391
  return { ...base, prd: structuredClone(base.prd) };
64164
64392
  }
@@ -64181,7 +64409,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
64181
64409
  story,
64182
64410
  stories: [story],
64183
64411
  projectDir: context.projectDir,
64184
- workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
64412
+ workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
64185
64413
  worktreeDependencyContext: dependencyContext,
64186
64414
  routing,
64187
64415
  storyGitRef: storyGitRef ?? undefined
@@ -65081,7 +65309,7 @@ async function writeStatusFile(filePath, status) {
65081
65309
  var init_status_file = () => {};
65082
65310
 
65083
65311
  // src/execution/status-writer.ts
65084
- import { join as join83 } from "path";
65312
+ import { join as join84 } from "path";
65085
65313
 
65086
65314
  class StatusWriter {
65087
65315
  statusFile;
@@ -65200,7 +65428,7 @@ class StatusWriter {
65200
65428
  if (!this._prd)
65201
65429
  return;
65202
65430
  const safeLogger = getSafeLogger();
65203
- const featureStatusPath = join83(featureDir, "status.json");
65431
+ const featureStatusPath = join84(featureDir, "status.json");
65204
65432
  const write = async () => {
65205
65433
  try {
65206
65434
  const base = this.getSnapshot(totalCost, iterations);
@@ -65634,7 +65862,7 @@ __export(exports_run_initialization, {
65634
65862
  initializeRun: () => initializeRun,
65635
65863
  _reconcileDeps: () => _reconcileDeps
65636
65864
  });
65637
- import { join as join84 } from "path";
65865
+ import { join as join85 } from "path";
65638
65866
  async function reconcileState(prd, prdPath, workdir, config2) {
65639
65867
  const logger = getSafeLogger();
65640
65868
  let reconciledCount = 0;
@@ -65651,7 +65879,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
65651
65879
  });
65652
65880
  continue;
65653
65881
  }
65654
- const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
65882
+ const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
65655
65883
  try {
65656
65884
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
65657
65885
  if (!reviewResult.success) {
@@ -95482,7 +95710,7 @@ __export(exports_curator, {
95482
95710
  });
95483
95711
  import { readdirSync as readdirSync8 } from "fs";
95484
95712
  import { unlink as unlink4 } from "fs/promises";
95485
- import { basename as basename15, join as join86 } from "path";
95713
+ import { basename as basename15, join as join87 } from "path";
95486
95714
  function getProjectKey(config2, projectDir) {
95487
95715
  return config2.name?.trim() || basename15(projectDir);
95488
95716
  }
@@ -95565,7 +95793,7 @@ async function curatorStatus(options) {
95565
95793
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95566
95794
  const projectKey = getProjectKey(config2, resolved.projectDir);
95567
95795
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95568
- const runsDir = join86(outputDir, "runs");
95796
+ const runsDir = join87(outputDir, "runs");
95569
95797
  const runIds = listRunIds(runsDir);
95570
95798
  let runId;
95571
95799
  if (options.run) {
@@ -95582,8 +95810,8 @@ async function curatorStatus(options) {
95582
95810
  runId = runIds[runIds.length - 1];
95583
95811
  }
95584
95812
  console.log(`Run: ${runId}`);
95585
- const runDir = join86(runsDir, runId);
95586
- const observationsPath = join86(runDir, "observations.jsonl");
95813
+ const runDir = join87(runsDir, runId);
95814
+ const observationsPath = join87(runDir, "observations.jsonl");
95587
95815
  const observations = await parseObservations(observationsPath);
95588
95816
  const counts = new Map;
95589
95817
  for (const obs of observations) {
@@ -95593,7 +95821,7 @@ async function curatorStatus(options) {
95593
95821
  for (const [kind, count] of counts.entries()) {
95594
95822
  console.log(` ${kind}: ${count}`);
95595
95823
  }
95596
- const proposalsPath = join86(runDir, "curator-proposals.md");
95824
+ const proposalsPath = join87(runDir, "curator-proposals.md");
95597
95825
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
95598
95826
  if (proposalText !== null) {
95599
95827
  console.log("");
@@ -95607,8 +95835,8 @@ async function curatorCommit(options) {
95607
95835
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95608
95836
  const projectKey = getProjectKey(config2, resolved.projectDir);
95609
95837
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95610
- const runDir = join86(outputDir, "runs", options.runId);
95611
- const proposalsPath = join86(runDir, "curator-proposals.md");
95838
+ const runDir = join87(outputDir, "runs", options.runId);
95839
+ const proposalsPath = join87(runDir, "curator-proposals.md");
95612
95840
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
95613
95841
  if (proposalText === null) {
95614
95842
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -95624,7 +95852,7 @@ async function curatorCommit(options) {
95624
95852
  const dropFileState = new Map;
95625
95853
  const skippedDrops = new Set;
95626
95854
  for (const drop2 of drops) {
95627
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
95855
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
95628
95856
  if (!dropFileState.has(targetPath)) {
95629
95857
  const fileExists2 = await Bun.file(targetPath).exists();
95630
95858
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -95658,7 +95886,7 @@ async function curatorCommit(options) {
95658
95886
  if (skippedDrops.has(drop2)) {
95659
95887
  continue;
95660
95888
  }
95661
- const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
95889
+ const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
95662
95890
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
95663
95891
  const filtered = filterDropContent(existing, drop2.description);
95664
95892
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -95667,7 +95895,7 @@ async function curatorCommit(options) {
95667
95895
  }
95668
95896
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
95669
95897
  for (const add2 of adds) {
95670
- const targetPath = join86(resolved.projectDir, add2.canonicalFile);
95898
+ const targetPath = join87(resolved.projectDir, add2.canonicalFile);
95671
95899
  const content = buildAddContent(add2);
95672
95900
  await _curatorCmdDeps.appendFile(targetPath, content);
95673
95901
  modifiedFiles.add(targetPath);
@@ -95704,7 +95932,7 @@ async function curatorDryrun(options) {
95704
95932
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95705
95933
  const projectKey = getProjectKey(config2, resolved.projectDir);
95706
95934
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95707
- const runsDir = join86(outputDir, "runs");
95935
+ const runsDir = join87(outputDir, "runs");
95708
95936
  const runIds = listRunIds(runsDir);
95709
95937
  if (runIds.length === 0) {
95710
95938
  console.log("No runs found.");
@@ -95715,7 +95943,7 @@ async function curatorDryrun(options) {
95715
95943
  console.log(`Run ${options.run} not found in ${runsDir}.`);
95716
95944
  return;
95717
95945
  }
95718
- const observationsPath = join86(runsDir, runId, "observations.jsonl");
95946
+ const observationsPath = join87(runsDir, runId, "observations.jsonl");
95719
95947
  const observations = await parseObservations(observationsPath);
95720
95948
  const thresholds = getThresholds(config2);
95721
95949
  const proposals = runHeuristics(observations, thresholds);
@@ -95756,12 +95984,12 @@ async function curatorGc(options) {
95756
95984
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
95757
95985
  const projectKey = getProjectKey(config2, resolved.projectDir);
95758
95986
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95759
- const perRunsDir = join86(outputDir, "runs");
95987
+ const perRunsDir = join87(outputDir, "runs");
95760
95988
  for (const runId of uniqueRunIds) {
95761
95989
  if (!keepSet.has(runId)) {
95762
- const runDir = join86(perRunsDir, runId);
95763
- await _curatorCmdDeps.removeFile(join86(runDir, "observations.jsonl"));
95764
- await _curatorCmdDeps.removeFile(join86(runDir, "curator-proposals.md"));
95990
+ const runDir = join87(perRunsDir, runId);
95991
+ await _curatorCmdDeps.removeFile(join87(runDir, "observations.jsonl"));
95992
+ await _curatorCmdDeps.removeFile(join87(runDir, "curator-proposals.md"));
95765
95993
  }
95766
95994
  }
95767
95995
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -95806,7 +96034,7 @@ var init_curator2 = __esm(() => {
95806
96034
  init_source();
95807
96035
  import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
95808
96036
  import { homedir as homedir3 } from "os";
95809
- import { basename as basename16, join as join87 } from "path";
96037
+ import { basename as basename16, join as join88 } from "path";
95810
96038
 
95811
96039
  // node_modules/commander/esm.mjs
95812
96040
  var import__ = __toESM(require_commander(), 1);
@@ -96930,6 +97158,7 @@ async function runsShowCommand(options) {
96930
97158
  init_prompts2();
96931
97159
  init_init2();
96932
97160
  init_setup();
97161
+ init_setup_write();
96933
97162
 
96934
97163
  // src/cli/plugins.ts
96935
97164
  init_paths();
@@ -97005,7 +97234,7 @@ init_source();
97005
97234
  init_loader();
97006
97235
  init_generator2();
97007
97236
  import { existsSync as existsSync24 } from "fs";
97008
- import { join as join62 } from "path";
97237
+ import { join as join63 } from "path";
97009
97238
  var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
97010
97239
  async function generateCommand(options) {
97011
97240
  const workdir = options.dir ?? process.cwd();
@@ -97048,7 +97277,7 @@ async function generateCommand(options) {
97048
97277
  return;
97049
97278
  }
97050
97279
  if (options.package) {
97051
- const packageDir = join62(workdir, options.package);
97280
+ const packageDir = join63(workdir, options.package);
97052
97281
  if (dryRun) {
97053
97282
  console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
97054
97283
  }
@@ -97068,8 +97297,8 @@ async function generateCommand(options) {
97068
97297
  process.exit(1);
97069
97298
  return;
97070
97299
  }
97071
- const contextPath = options.context ? join62(workdir, options.context) : join62(workdir, ".nax/context.md");
97072
- const outputDir = options.output ? join62(workdir, options.output) : workdir;
97300
+ const contextPath = options.context ? join63(workdir, options.context) : join63(workdir, ".nax/context.md");
97301
+ const outputDir = options.output ? join63(workdir, options.output) : workdir;
97073
97302
  const autoInject = !options.noAutoInject;
97074
97303
  if (!existsSync24(contextPath)) {
97075
97304
  console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
@@ -97175,7 +97404,7 @@ async function generateCommand(options) {
97175
97404
  // src/cli/config-display.ts
97176
97405
  init_loader();
97177
97406
  import { existsSync as existsSync26 } from "fs";
97178
- import { join as join64 } from "path";
97407
+ import { join as join65 } from "path";
97179
97408
 
97180
97409
  // src/cli/config-descriptions.ts
97181
97410
  var FIELD_DESCRIPTIONS = {
@@ -97428,7 +97657,7 @@ function deepEqual(a, b) {
97428
97657
  init_defaults();
97429
97658
  init_loader();
97430
97659
  import { existsSync as existsSync25 } from "fs";
97431
- import { join as join63 } from "path";
97660
+ import { join as join64 } from "path";
97432
97661
  async function loadConfigFile(path19) {
97433
97662
  if (!existsSync25(path19))
97434
97663
  return null;
@@ -97450,7 +97679,7 @@ async function loadProjectConfig() {
97450
97679
  const projectDir = findProjectDir();
97451
97680
  if (!projectDir)
97452
97681
  return null;
97453
- const projectPath = join63(projectDir, "config.json");
97682
+ const projectPath = join64(projectDir, "config.json");
97454
97683
  return await loadConfigFile(projectPath);
97455
97684
  }
97456
97685
 
@@ -97510,7 +97739,7 @@ async function configCommand(config2, options = {}) {
97510
97739
  function determineConfigSources() {
97511
97740
  const globalPath = globalConfigPath();
97512
97741
  const projectDir = findProjectDir();
97513
- const projectPath = projectDir ? join64(projectDir, "config.json") : null;
97742
+ const projectPath = projectDir ? join65(projectDir, "config.json") : null;
97514
97743
  return {
97515
97744
  global: fileExists(globalPath) ? globalPath : null,
97516
97745
  project: projectPath && fileExists(projectPath) ? projectPath : null
@@ -97659,15 +97888,15 @@ init_paths();
97659
97888
  init_profile();
97660
97889
  import { mkdirSync as mkdirSync5 } from "fs";
97661
97890
  import { readdirSync as readdirSync5 } from "fs";
97662
- import { join as join65 } from "path";
97891
+ import { join as join66 } from "path";
97663
97892
  var _profileCLIDeps = {
97664
97893
  env: process.env
97665
97894
  };
97666
97895
  var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
97667
97896
  var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
97668
97897
  async function profileListCommand(startDir) {
97669
- const globalProfilesDir = join65(globalConfigDir(), "profiles");
97670
- const projectProfilesDir = join65(projectConfigDir(startDir), "profiles");
97898
+ const globalProfilesDir = join66(globalConfigDir(), "profiles");
97899
+ const projectProfilesDir = join66(projectConfigDir(startDir), "profiles");
97671
97900
  const globalProfiles = scanProfileDir(globalProfilesDir);
97672
97901
  const projectProfiles = scanProfileDir(projectProfilesDir);
97673
97902
  const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
@@ -97726,7 +97955,7 @@ function maskProfileValues(obj) {
97726
97955
  return result;
97727
97956
  }
97728
97957
  async function profileUseCommand(profileName, startDir) {
97729
- const configPath = join65(projectConfigDir(startDir), "config.json");
97958
+ const configPath = join66(projectConfigDir(startDir), "config.json");
97730
97959
  const configFile = Bun.file(configPath);
97731
97960
  let existing = {};
97732
97961
  if (await configFile.exists()) {
@@ -97745,8 +97974,8 @@ async function profileCurrentCommand(startDir) {
97745
97974
  return resolveProfileName({}, _profileCLIDeps.env, startDir);
97746
97975
  }
97747
97976
  async function profileCreateCommand(profileName, startDir) {
97748
- const profilesDir = join65(projectConfigDir(startDir), "profiles");
97749
- const profilePath = join65(profilesDir, `${profileName}.json`);
97977
+ const profilesDir = join66(projectConfigDir(startDir), "profiles");
97978
+ const profilePath = join66(profilesDir, `${profileName}.json`);
97750
97979
  const profileFile = Bun.file(profilePath);
97751
97980
  if (await profileFile.exists()) {
97752
97981
  throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
@@ -97868,7 +98097,7 @@ async function contextInspectCommand(options) {
97868
98097
  init_canonical_loader();
97869
98098
  init_errors();
97870
98099
  import { mkdir as mkdir11 } from "fs/promises";
97871
- import { basename as basename12, join as join66 } from "path";
98100
+ import { basename as basename12, join as join67 } from "path";
97872
98101
  var _rulesCLIDeps = {
97873
98102
  readFile: async (path19) => Bun.file(path19).text(),
97874
98103
  writeFile: async (path19, content) => {
@@ -97877,7 +98106,7 @@ var _rulesCLIDeps = {
97877
98106
  fileExists: async (path19) => Bun.file(path19).exists(),
97878
98107
  globInDir: (dir) => {
97879
98108
  try {
97880
- return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join66(dir, f));
98109
+ return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join67(dir, f));
97881
98110
  } catch {
97882
98111
  return [];
97883
98112
  }
@@ -97926,7 +98155,7 @@ ${r.content}`).join(`
97926
98155
  `);
97927
98156
  const shimContent = `${header + body}
97928
98157
  `;
97929
- const shimPath = join66(workdir, shimFileName);
98158
+ const shimPath = join67(workdir, shimFileName);
97930
98159
  if (options.dryRun) {
97931
98160
  console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
97932
98161
  return;
@@ -97955,14 +98184,14 @@ function neutralizeContent(content) {
97955
98184
  }
97956
98185
  async function collectMigrationSources(workdir) {
97957
98186
  const sources = [];
97958
- const claudeMdPath = join66(workdir, "CLAUDE.md");
98187
+ const claudeMdPath = join67(workdir, "CLAUDE.md");
97959
98188
  if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
97960
98189
  const content = await _rulesCLIDeps.readFile(claudeMdPath);
97961
98190
  if (content.trim()) {
97962
98191
  sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
97963
98192
  }
97964
98193
  }
97965
- const rulesDir = join66(workdir, ".claude", "rules");
98194
+ const rulesDir = join67(workdir, ".claude", "rules");
97966
98195
  const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
97967
98196
  for (const filePath of ruleFiles) {
97968
98197
  try {
@@ -97982,7 +98211,7 @@ async function rulesMigrateCommand(options) {
97982
98211
  console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
97983
98212
  return;
97984
98213
  }
97985
- const targetDir = join66(workdir, CANONICAL_RULES_DIR);
98214
+ const targetDir = join67(workdir, CANONICAL_RULES_DIR);
97986
98215
  if (!options.dryRun) {
97987
98216
  try {
97988
98217
  await _rulesCLIDeps.mkdir(targetDir);
@@ -97993,7 +98222,7 @@ async function rulesMigrateCommand(options) {
97993
98222
  let written = 0;
97994
98223
  let skipped = 0;
97995
98224
  for (const { sourcePath, targetFileName, content } of sources) {
97996
- const targetPath = join66(targetDir, targetFileName);
98225
+ const targetPath = join67(targetDir, targetFileName);
97997
98226
  if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
97998
98227
  console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
97999
98228
  skipped++;
@@ -98032,7 +98261,7 @@ function collectCanonicalRuleRoots(workdir) {
98032
98261
  const packageRel = normalized.slice(0, idx);
98033
98262
  if (!packageRel)
98034
98263
  continue;
98035
- roots.add(join66(workdir, packageRel));
98264
+ roots.add(join67(workdir, packageRel));
98036
98265
  }
98037
98266
  return [...roots].sort();
98038
98267
  }
@@ -98084,7 +98313,7 @@ init_logger2();
98084
98313
  init_detect2();
98085
98314
  init_workspace();
98086
98315
  init_common();
98087
- import { join as join67 } from "path";
98316
+ import { join as join68 } from "path";
98088
98317
  function resolveEffective(detected, configPatterns) {
98089
98318
  if (configPatterns !== undefined)
98090
98319
  return "config";
@@ -98169,7 +98398,7 @@ async function detectCommand(options) {
98169
98398
  const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
98170
98399
  const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
98171
98400
  const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
98172
- const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
98401
+ const pkgConfigPath = join68(workdir, ".nax", "mono", dir, "config.json");
98173
98402
  const pkgRaw = await loadRawConfig(pkgConfigPath);
98174
98403
  const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
98175
98404
  const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
@@ -98223,13 +98452,13 @@ async function detectCommand(options) {
98223
98452
  if (rootDetected.confidence === "empty") {
98224
98453
  console.log(source_default.yellow(" root: skipped (empty detection)"));
98225
98454
  } else {
98226
- const rootConfigPath = join67(workdir, ".nax", "config.json");
98455
+ const rootConfigPath = join68(workdir, ".nax", "config.json");
98227
98456
  try {
98228
98457
  const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
98229
98458
  if (status === "skipped") {
98230
98459
  console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
98231
98460
  } else {
98232
- console.log(source_default.green(` root: ${status} \u2192 ${join67(".nax", "config.json")}`));
98461
+ console.log(source_default.green(` root: ${status} \u2192 ${join68(".nax", "config.json")}`));
98233
98462
  }
98234
98463
  } catch (err) {
98235
98464
  console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
@@ -98242,13 +98471,13 @@ async function detectCommand(options) {
98242
98471
  console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
98243
98472
  continue;
98244
98473
  }
98245
- const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
98474
+ const pkgConfigPath = join68(workdir, ".nax", "mono", dir, "config.json");
98246
98475
  try {
98247
98476
  const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
98248
98477
  if (status === "skipped") {
98249
98478
  console.log(source_default.dim(` ${dir}: skipped (already set)`));
98250
98479
  } else {
98251
- console.log(source_default.green(` ${dir}: ${status} \u2192 ${join67(".nax", "mono", dir, "config.json")}`));
98480
+ console.log(source_default.green(` ${dir}: ${status} \u2192 ${join68(".nax", "mono", dir, "config.json")}`));
98252
98481
  }
98253
98482
  } catch (err) {
98254
98483
  console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
@@ -98266,19 +98495,19 @@ async function detectCommand(options) {
98266
98495
  // src/commands/logs.ts
98267
98496
  init_common();
98268
98497
  import { existsSync as existsSync28 } from "fs";
98269
- import { join as join70 } from "path";
98498
+ import { join as join71 } from "path";
98270
98499
 
98271
98500
  // src/commands/logs-formatter.ts
98272
98501
  init_source();
98273
98502
  init_formatter();
98274
98503
  import { readdirSync as readdirSync7 } from "fs";
98275
- import { join as join69 } from "path";
98504
+ import { join as join70 } from "path";
98276
98505
 
98277
98506
  // src/commands/logs-reader.ts
98278
98507
  init_paths3();
98279
98508
  import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
98280
98509
  import { readdir as readdir3 } from "fs/promises";
98281
- import { join as join68 } from "path";
98510
+ import { join as join69 } from "path";
98282
98511
  var _logsReaderDeps = {
98283
98512
  getRunsDir
98284
98513
  };
@@ -98292,7 +98521,7 @@ async function resolveRunFileFromRegistry(runId) {
98292
98521
  }
98293
98522
  let matched = null;
98294
98523
  for (const entry of entries) {
98295
- const metaPath = join68(runsDir, entry, "meta.json");
98524
+ const metaPath = join69(runsDir, entry, "meta.json");
98296
98525
  try {
98297
98526
  const meta3 = await Bun.file(metaPath).json();
98298
98527
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -98314,14 +98543,14 @@ async function resolveRunFileFromRegistry(runId) {
98314
98543
  return null;
98315
98544
  }
98316
98545
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
98317
- return join68(matched.eventsDir, specificFile ?? files[0]);
98546
+ return join69(matched.eventsDir, specificFile ?? files[0]);
98318
98547
  }
98319
98548
  async function selectRunFile(runsDir) {
98320
98549
  const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
98321
98550
  if (files.length === 0) {
98322
98551
  return null;
98323
98552
  }
98324
- return join68(runsDir, files[0]);
98553
+ return join69(runsDir, files[0]);
98325
98554
  }
98326
98555
  async function extractRunSummary(filePath) {
98327
98556
  const file3 = Bun.file(filePath);
@@ -98407,7 +98636,7 @@ Runs:
98407
98636
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
98408
98637
  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"));
98409
98638
  for (const file3 of files) {
98410
- const filePath = join69(runsDir, file3);
98639
+ const filePath = join70(runsDir, file3);
98411
98640
  const summary = await extractRunSummary(filePath);
98412
98641
  const timestamp = file3.replace(".jsonl", "");
98413
98642
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -98521,7 +98750,7 @@ async function logsCommand(options) {
98521
98750
  return;
98522
98751
  }
98523
98752
  const resolved = resolveProject({ dir: options.dir });
98524
- const naxDir = join70(resolved.projectDir, ".nax");
98753
+ const naxDir = join71(resolved.projectDir, ".nax");
98525
98754
  const configPath = resolved.configPath;
98526
98755
  const configFile = Bun.file(configPath);
98527
98756
  const config2 = await configFile.json();
@@ -98529,8 +98758,8 @@ async function logsCommand(options) {
98529
98758
  if (!featureName) {
98530
98759
  throw new Error("No feature specified in config.json");
98531
98760
  }
98532
- const featureDir = join70(naxDir, "features", featureName);
98533
- const runsDir = join70(featureDir, "runs");
98761
+ const featureDir = join71(naxDir, "features", featureName);
98762
+ const runsDir = join71(featureDir, "runs");
98534
98763
  if (!existsSync28(runsDir)) {
98535
98764
  throw new Error(`No runs directory found for feature: ${featureName}`);
98536
98765
  }
@@ -98556,7 +98785,7 @@ init_prd();
98556
98785
  init_precheck();
98557
98786
  init_common();
98558
98787
  import { existsSync as existsSync29 } from "fs";
98559
- import { join as join71 } from "path";
98788
+ import { join as join72 } from "path";
98560
98789
  async function precheckCommand(options) {
98561
98790
  const resolved = resolveProject({
98562
98791
  dir: options.dir,
@@ -98578,9 +98807,9 @@ async function precheckCommand(options) {
98578
98807
  process.exit(1);
98579
98808
  }
98580
98809
  }
98581
- const naxDir = join71(resolved.projectDir, ".nax");
98582
- const featureDir = join71(naxDir, "features", featureName);
98583
- const prdPath = join71(featureDir, "prd.json");
98810
+ const naxDir = join72(resolved.projectDir, ".nax");
98811
+ const featureDir = join72(naxDir, "features", featureName);
98812
+ const prdPath = join72(featureDir, "prd.json");
98584
98813
  if (!existsSync29(featureDir)) {
98585
98814
  console.error(source_default.red(`Feature not found: ${featureName}`));
98586
98815
  process.exit(1);
@@ -98603,7 +98832,7 @@ async function precheckCommand(options) {
98603
98832
  init_source();
98604
98833
  init_paths3();
98605
98834
  import { readdir as readdir4 } from "fs/promises";
98606
- import { join as join72 } from "path";
98835
+ import { join as join73 } from "path";
98607
98836
  var DEFAULT_LIMIT = 20;
98608
98837
  var _runsCmdDeps = {
98609
98838
  getRunsDir
@@ -98658,7 +98887,7 @@ async function runsCommand(options = {}) {
98658
98887
  }
98659
98888
  const rows = [];
98660
98889
  for (const entry of entries) {
98661
- const metaPath = join72(runsDir, entry, "meta.json");
98890
+ const metaPath = join73(runsDir, entry, "meta.json");
98662
98891
  let meta3;
98663
98892
  try {
98664
98893
  meta3 = await Bun.file(metaPath).json();
@@ -98735,7 +98964,7 @@ async function runsCommand(options = {}) {
98735
98964
 
98736
98965
  // src/commands/unlock.ts
98737
98966
  init_source();
98738
- import { join as join73 } from "path";
98967
+ import { join as join74 } from "path";
98739
98968
  function isProcessAlive2(pid) {
98740
98969
  try {
98741
98970
  process.kill(pid, 0);
@@ -98750,7 +98979,7 @@ function formatLockAge(ageMs) {
98750
98979
  }
98751
98980
  async function unlockCommand(options) {
98752
98981
  const workdir = options.dir ?? process.cwd();
98753
- const lockPath = join73(workdir, "nax.lock");
98982
+ const lockPath = join74(workdir, "nax.lock");
98754
98983
  const lockFile = Bun.file(lockPath);
98755
98984
  const exists = await lockFile.exists();
98756
98985
  if (!exists) {
@@ -107188,7 +107417,7 @@ Next: nax generate --package ${options.package}`));
107188
107417
  }
107189
107418
  return;
107190
107419
  }
107191
- const naxDir = join87(workdir, ".nax");
107420
+ const naxDir = join88(workdir, ".nax");
107192
107421
  if (existsSync35(naxDir) && !options.force) {
107193
107422
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
107194
107423
  return;
@@ -107217,11 +107446,11 @@ Next: nax generate --package ${options.package}`));
107217
107446
  }
107218
107447
  }
107219
107448
  }
107220
- mkdirSync7(join87(naxDir, "features"), { recursive: true });
107221
- mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
107449
+ mkdirSync7(join88(naxDir, "features"), { recursive: true });
107450
+ mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
107222
107451
  const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
107223
- await Bun.write(join87(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
107224
- await Bun.write(join87(naxDir, "hooks.json"), JSON.stringify({
107452
+ await Bun.write(join88(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
107453
+ await Bun.write(join88(naxDir, "hooks.json"), JSON.stringify({
107225
107454
  hooks: {
107226
107455
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
107227
107456
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -107229,12 +107458,12 @@ Next: nax generate --package ${options.package}`));
107229
107458
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
107230
107459
  }
107231
107460
  }, null, 2));
107232
- await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
107461
+ await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
107233
107462
  *.tmp
107234
107463
  .paused.json
107235
107464
  .nax-verifier-verdict.json
107236
107465
  `);
107237
- await Bun.write(join87(naxDir, "context.md"), `# Project Context
107466
+ await Bun.write(join88(naxDir, "context.md"), `# Project Context
107238
107467
 
107239
107468
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
107240
107469
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -107376,7 +107605,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
107376
107605
  const cliOverrides = {};
107377
107606
  const cliProfiles = options.profile ?? [];
107378
107607
  const profileOverride = naxDir ? await resolveRunProfileOverride({
107379
- prdPath: join87(naxDir, "features", options.feature, "prd.json"),
107608
+ prdPath: join88(naxDir, "features", options.feature, "prd.json"),
107380
107609
  projectRoot: workdir,
107381
107610
  cliProfile: cliProfiles,
107382
107611
  envProfile: process.env.NAX_PROFILE
@@ -107389,8 +107618,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
107389
107618
  console.error(source_default.red("nax not initialized. Run: nax init"));
107390
107619
  process.exit(1);
107391
107620
  }
107392
- const featureDir = join87(naxDir, "features", options.feature);
107393
- const prdPath = join87(featureDir, "prd.json");
107621
+ const featureDir = join88(naxDir, "features", options.feature);
107622
+ const prdPath = join88(featureDir, "prd.json");
107394
107623
  if (options.plan && options.from) {
107395
107624
  if (existsSync35(prdPath) && !options.force) {
107396
107625
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
@@ -107412,10 +107641,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
107412
107641
  }
107413
107642
  }
107414
107643
  try {
107415
- const planLogDir = join87(featureDir, "plan");
107644
+ const planLogDir = join88(featureDir, "plan");
107416
107645
  mkdirSync7(planLogDir, { recursive: true });
107417
107646
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
107418
- const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
107647
+ const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
107419
107648
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
107420
107649
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
107421
107650
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -107461,10 +107690,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
107461
107690
  resetLogger();
107462
107691
  const projectKey = config2.name?.trim() || basename16(workdir);
107463
107692
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
107464
- const runsDir = join87(outputDir, "features", options.feature, "runs");
107693
+ const runsDir = join88(outputDir, "features", options.feature, "runs");
107465
107694
  mkdirSync7(runsDir, { recursive: true });
107466
107695
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
107467
- const logFilePath = join87(runsDir, `${runId}.jsonl`);
107696
+ const logFilePath = join88(runsDir, `${runId}.jsonl`);
107468
107697
  const isTTY = process.stdout.isTTY ?? false;
107469
107698
  const headlessFlag = options.headless ?? false;
107470
107699
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -107482,7 +107711,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
107482
107711
  config2.agent.default = options.agent;
107483
107712
  }
107484
107713
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
107485
- const globalNaxDir = join87(homedir3(), ".nax");
107714
+ const globalNaxDir = join88(homedir3(), ".nax");
107486
107715
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
107487
107716
  const eventEmitter = new PipelineEventEmitter;
107488
107717
  const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
@@ -107502,12 +107731,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
107502
107731
  events: eventEmitter,
107503
107732
  ptyOptions: null,
107504
107733
  agentStreamEvents,
107505
- queueFilePath: join87(workdir, ".queue.txt")
107734
+ queueFilePath: join88(workdir, ".queue.txt")
107506
107735
  });
107507
107736
  } else {
107508
107737
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
107509
107738
  }
107510
- const statusFilePath = join87(outputDir, "status.json");
107739
+ const statusFilePath = join88(outputDir, "status.json");
107511
107740
  let parallel;
107512
107741
  if (options.parallel !== undefined) {
107513
107742
  parallel = Number.parseInt(options.parallel, 10);
@@ -107534,7 +107763,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
107534
107763
  skipPrecheck: options.skipPrecheck ?? false,
107535
107764
  agentStreamEvents
107536
107765
  });
107537
- const latestSymlink = join87(runsDir, "latest.jsonl");
107766
+ const latestSymlink = join88(runsDir, "latest.jsonl");
107538
107767
  try {
107539
107768
  if (existsSync35(latestSymlink)) {
107540
107769
  Bun.spawnSync(["rm", latestSymlink]);
@@ -107595,9 +107824,9 @@ features.command("create <name>").description("Create a new feature").option("-d
107595
107824
  console.error(source_default.red("nax not initialized. Run: nax init"));
107596
107825
  process.exit(1);
107597
107826
  }
107598
- const featureDir = join87(naxDir, "features", name);
107827
+ const featureDir = join88(naxDir, "features", name);
107599
107828
  mkdirSync7(featureDir, { recursive: true });
107600
- await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
107829
+ await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
107601
107830
 
107602
107831
  ## Overview
107603
107832
 
@@ -107630,7 +107859,7 @@ features.command("create <name>").description("Create a new feature").option("-d
107630
107859
 
107631
107860
  <!-- What this feature explicitly does NOT cover. -->
107632
107861
  `);
107633
- await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
107862
+ await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
107634
107863
 
107635
107864
  Created: ${new Date().toISOString()}
107636
107865
 
@@ -107656,7 +107885,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
107656
107885
  console.error(source_default.red("nax not initialized."));
107657
107886
  process.exit(1);
107658
107887
  }
107659
- const featuresDir = join87(naxDir, "features");
107888
+ const featuresDir = join88(naxDir, "features");
107660
107889
  if (!existsSync35(featuresDir)) {
107661
107890
  console.log(source_default.dim("No features yet."));
107662
107891
  return;
@@ -107671,7 +107900,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
107671
107900
  Features:
107672
107901
  `));
107673
107902
  for (const name of entries) {
107674
- const prdPath = join87(featuresDir, name, "prd.json");
107903
+ const prdPath = join88(featuresDir, name, "prd.json");
107675
107904
  if (existsSync35(prdPath)) {
107676
107905
  const prd = await loadPRD(prdPath);
107677
107906
  const c = countStories(prd);
@@ -107707,10 +107936,10 @@ Use: nax plan -f <feature> --from <spec>`));
107707
107936
  cliOverrides.profile = cliProfiles;
107708
107937
  }
107709
107938
  const config2 = await loadConfig(workdir, cliOverrides);
107710
- const featureLogDir = join87(naxDir, "features", options.feature, "plan");
107939
+ const featureLogDir = join88(naxDir, "features", options.feature, "plan");
107711
107940
  mkdirSync7(featureLogDir, { recursive: true });
107712
107941
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
107713
- const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
107942
+ const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
107714
107943
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
107715
107944
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
107716
107945
  try {