@nathapp/nax 0.68.4 → 0.68.6

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 +1142 -1247
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -16837,6 +16837,7 @@ var init_schemas_execution = __esm(() => {
16837
16837
  fullSuiteTimeoutSeconds: exports_external.number().int().min(10).max(600).default(120),
16838
16838
  maxFailureSummaryChars: exports_external.number().int().min(500).max(1e4).default(2000),
16839
16839
  abortOnIncreasingFailures: exports_external.boolean().default(true),
16840
+ consecutiveIncreasesToBail: exports_external.number().int().min(1).max(10).default(2),
16840
16841
  escalateOnExhaustion: exports_external.boolean().optional().default(true),
16841
16842
  rethinkAtAttempt: exports_external.number().int().min(1).default(2),
16842
16843
  urgencyAtAttempt: exports_external.number().int().min(1).default(3)
@@ -17321,6 +17322,7 @@ var init_schemas3 = __esm(() => {
17321
17322
  fullSuiteTimeoutSeconds: 300,
17322
17323
  maxFailureSummaryChars: 2000,
17323
17324
  abortOnIncreasingFailures: true,
17325
+ consecutiveIncreasesToBail: 2,
17324
17326
  escalateOnExhaustion: true,
17325
17327
  rethinkAtAttempt: 2,
17326
17328
  urgencyAtAttempt: 3
@@ -18378,16 +18380,13 @@ function deepMergeConfig(base, override) {
18378
18380
  for (const hookName of allHookNames) {
18379
18381
  const baseHook = baseHookDefs[hookName];
18380
18382
  const overrideHook = overrideHookDefs[hookName];
18381
- if (baseHook && overrideHook) {
18382
- mergedHookDefs[hookName] = [baseHook, overrideHook];
18383
- } else if (overrideHook) {
18384
- mergedHookDefs[hookName] = overrideHook;
18385
- } else {
18386
- mergedHookDefs[hookName] = baseHook;
18387
- }
18383
+ const baseItems = Array.isArray(baseHook) ? baseHook : baseHook ? [baseHook] : [];
18384
+ const overrideItems = Array.isArray(overrideHook) ? overrideHook : overrideHook ? [overrideHook] : [];
18385
+ const combined = [...baseItems, ...overrideItems];
18386
+ mergedHookDefs[hookName] = combined.length === 1 ? combined[0] : combined;
18388
18387
  }
18389
18388
  merged.hooks = mergedHookDefs;
18390
- } else if (overrideHooks.hooks) {
18389
+ } else if (isPlainObject2(overrideHooks.hooks)) {
18391
18390
  merged.hooks = overrideHooks.hooks;
18392
18391
  }
18393
18392
  for (const hookKey of Object.keys(overrideHooks)) {
@@ -19086,6 +19085,12 @@ function resolveTestStrategy(raw) {
19086
19085
  return "three-session-tdd-lite";
19087
19086
  return "test-after";
19088
19087
  }
19088
+ function isThreeSessionStrategy(strategy) {
19089
+ return strategy !== undefined && THREE_SESSION_STRATEGIES.has(strategy);
19090
+ }
19091
+ function isSingleSessionTestOwningStrategy(strategy) {
19092
+ return strategy !== undefined && SINGLE_SESSION_TEST_OWNING_STRATEGIES.has(strategy);
19093
+ }
19089
19094
  function getAcQualityRules(profile) {
19090
19095
  const langSection = profile?.language ? LANGUAGE_PATTERNS[profile.language] : undefined;
19091
19096
  const typeSection = profile?.type ? TYPE_PATTERNS[profile.type] : undefined;
@@ -19098,7 +19103,7 @@ function getAcQualityRules(profile) {
19098
19103
 
19099
19104
  ${extras}`;
19100
19105
  }
19101
- var VALID_TEST_STRATEGIES, COMPLEXITY_GUIDE = `## Complexity Classification Guide
19106
+ var VALID_TEST_STRATEGIES, THREE_SESSION_STRATEGIES, SINGLE_SESSION_TEST_OWNING_STRATEGIES, COMPLEXITY_GUIDE = `## Complexity Classification Guide
19102
19107
 
19103
19108
  Classify each story's complexity based on scope and risk \u2014 NOT acceptance criteria count.
19104
19109
  A story with 10 simple "add field" ACs is simpler than one with 3 ACs involving concurrent
@@ -19239,6 +19244,11 @@ var init_test_strategy = __esm(() => {
19239
19244
  "three-session-tdd",
19240
19245
  "three-session-tdd-lite"
19241
19246
  ];
19247
+ THREE_SESSION_STRATEGIES = new Set([
19248
+ "three-session-tdd",
19249
+ "three-session-tdd-lite"
19250
+ ]);
19251
+ SINGLE_SESSION_TEST_OWNING_STRATEGIES = new Set(["tdd-simple", "test-after"]);
19242
19252
  LANGUAGE_PATTERNS = {
19243
19253
  go: `### Go-Specific AC Patterns
19244
19254
 
@@ -28043,16 +28053,17 @@ function deriveTestPatterns(contextFiles, resolvedGlobs) {
28043
28053
  async function detectTestDir(workdir, resolvedGlobs) {
28044
28054
  const resolvedDirs = resolvedGlobs ? extractTestDirs(resolvedGlobs) : [];
28045
28055
  const candidateDirs = resolvedDirs.length > 0 ? [...new Set([...resolvedDirs, ...DEFAULT_SCAN_TEST_DIRS])] : DEFAULT_SCAN_TEST_DIRS;
28046
- for (const dir of candidateDirs) {
28056
+ const checks3 = await Promise.all(candidateDirs.map(async (dir) => {
28047
28057
  const fullPath = path.join(workdir, dir);
28048
28058
  try {
28049
28059
  const proc = Bun.spawn(["test", "-d", fullPath], { stdout: "pipe", stderr: "pipe" });
28050
28060
  const exitCode = await proc.exited;
28051
- if (exitCode === 0)
28052
- return dir;
28053
- } catch {}
28054
- }
28055
- return null;
28061
+ return exitCode === 0 ? dir : null;
28062
+ } catch {
28063
+ return null;
28064
+ }
28065
+ }));
28066
+ return checks3.find((dir) => dir !== null) ?? null;
28056
28067
  }
28057
28068
  function deriveScanGlob(resolvedTestGlobs, testDir) {
28058
28069
  const prefix = `${testDir}/`;
@@ -29765,9 +29776,10 @@ Your task: make the failing tests pass by writing real source code.
29765
29776
  Workflow:
29766
29777
  1. Read every failing test in scope. The tests are the contract \u2014 understand what each one asserts before editing source.
29767
29778
  2. Run the scoped test files once to establish the baseline (which fail, which pass, and why).
29768
- 3. Implement source code in the package's source location (the project context names it).
29769
- 4. After each meaningful change, re-run only the scoped test files \u2014 never the full suite.
29770
- 5. When all scoped tests pass, stage and commit ALL changed files: \`git commit -m '${commitMsg}'\`.
29779
+ 3. Break the work into small steps before writing: for each step, note the source change and the failing test that verifies it (step -> verify). Resolve ambiguities now, not mid-edit.
29780
+ 4. Implement source code in the package's source location (the project context names it).
29781
+ 5. After each meaningful change, re-run only the scoped test files \u2014 never the full suite.
29782
+ 6. When all scoped tests pass, stage and commit ALL changed files: \`git commit -m '${commitMsg}'\`.
29771
29783
 
29772
29784
  Rules:
29773
29785
  - Do NOT modify test files. Three narrow exceptions: (a) a lint-only fix to a test, (b) a contract drift where the test imports a removed/renamed symbol, (c) a sibling test file rename forced by your source change. Name which exception applies in the commit body before editing any test file.
@@ -29782,10 +29794,11 @@ Context: A test-writer session has already created tests and may have added mini
29782
29794
  Workflow:
29783
29795
  1. Run the existing scoped tests to see which fail and why (assertion failure vs import error).
29784
29796
  2. Read each failing test. Note which ACs they cover and which they DON'T.
29785
- 3. Replace stubs with real implementations. A stub is one of: a type-only declaration, a function returning a placeholder/throwing "not implemented", or a const placeholder.
29786
- 4. If any AC has no test, add one before implementing \u2014 do not implement uncovered behavior.
29787
- 5. Re-run only the scoped test files after each meaningful change.
29788
- 6. When all scoped tests pass, stage and commit ALL changed files: \`git commit -m '${commitMsg}'\`.
29797
+ 3. Break the work into small steps before writing: for each stub, note the real implementation that replaces it and the test that verifies it (step -> verify); flag any AC still missing a test. Resolve ambiguities now, not mid-edit.
29798
+ 4. Replace stubs with real implementations. A stub is one of: a type-only declaration, a function returning a placeholder/throwing "not implemented", or a const placeholder.
29799
+ 5. If any AC has no test, add one before implementing \u2014 do not implement uncovered behavior.
29800
+ 6. Re-run only the scoped test files after each meaningful change.
29801
+ 7. When all scoped tests pass, stage and commit ALL changed files: \`git commit -m '${commitMsg}'\`.
29789
29802
 
29790
29803
  Rules:
29791
29804
  - Three test-modification exceptions apply (lint-only fix, contract drift, sibling rename). Name the exception in the commit body before editing any test the test-writer wrote.
@@ -29802,10 +29815,11 @@ Context: You are session 1 of a multi-session workflow. An implementer will foll
29802
29815
 
29803
29816
  Workflow:
29804
29817
  1. Re-read the acceptance criteria above.
29805
- 2. Create test files in the location the project uses for tests.
29806
- 3. Create stubs in the package's source location so the tests can import and compile. A stub is one of: a type/interface declaration, a function returning a placeholder/throwing "not implemented" (no more than 3 lines of body), or a const placeholder. If a stub body needs real logic, you have crossed into implementer territory \u2014 stop.
29807
- 4. For each AC: at least one success-path test and one boundary/failure-path test.
29808
- 5. Run the new test files. Confirm tests compile (stubs work) AND fail with ASSERTION failures \u2014 NOT import errors or compile errors. A test that errors before reaching its assertion does not prove the behavior is missing.
29818
+ 2. Break the work into small tasks before writing: treat each AC as one task and note the test name(s) you will add (success + boundary) and the minimal stub each test needs to compile. This per-AC list is your checklist.
29819
+ 3. Create test files in the location the project uses for tests.
29820
+ 4. Create stubs in the package's source location so the tests can import and compile. A stub is one of: a type/interface declaration, a function returning a placeholder/throwing "not implemented" (no more than 3 lines of body), or a const placeholder. If a stub body needs real logic, you have crossed into implementer territory \u2014 stop.
29821
+ 5. For each AC: at least one success-path test and one boundary/failure-path test.
29822
+ 6. Run the new test files. Confirm tests compile (stubs work) AND fail with ASSERTION failures \u2014 NOT import errors or compile errors. A test that errors before reaching its assertion does not prove the behavior is missing.
29809
29823
 
29810
29824
  Rules:
29811
29825
  - Stubs are NOT implementations. The implementer in the next session writes real logic.
@@ -29822,9 +29836,10 @@ Context: You are session 1 of a multi-session workflow.
29822
29836
 
29823
29837
  Workflow:
29824
29838
  1. Re-read the acceptance criteria above.
29825
- 2. Create test files in the location the project uses for tests (project context names it).
29826
- 3. For each AC: write at least one test for the success path AND at least one for a boundary/failure path (zero, empty, negative, missing, throws). ACs worded as "throws X" require a test asserting the throw.
29827
- 4. Run the new test files. Confirm every test fails with an ASSERTION failure \u2014 NOT an import error, compile error, or runtime crash before assertion. A test that errors before reaching its assertion does not prove the behavior is missing.
29839
+ 2. Break the work into small tasks before writing: treat each AC as one task and note the test name(s) you will write (success + boundary) and which file they belong in. This per-AC list is your checklist.
29840
+ 3. Create test files in the location the project uses for tests (project context names it).
29841
+ 4. For each AC: write at least one test for the success path AND at least one for a boundary/failure path (zero, empty, negative, missing, throws). ACs worded as "throws X" require a test asserting the throw.
29842
+ 5. Run the new test files. Confirm every test fails with an ASSERTION failure \u2014 NOT an import error, compile error, or runtime crash before assertion. A test that errors before reaching its assertion does not prove the behavior is missing.
29828
29843
 
29829
29844
  Rules:
29830
29845
  - Do NOT create or modify any source files. Read source for types/interfaces only.
@@ -36046,7 +36061,7 @@ var init_autofix_test_writer = __esm(() => {
36046
36061
  kind: "run",
36047
36062
  name: "autofix-test-writer",
36048
36063
  stage: "rectification",
36049
- session: { role: "test-writer", lifetime: "fresh" },
36064
+ session: { role: "test-writer", lifetime: "warm" },
36050
36065
  config: autofixConfigSelector,
36051
36066
  build(input, _ctx) {
36052
36067
  const prompt = RectifierPromptBuilder.testWriterRectification(input.failedChecks, input.story, {
@@ -36838,18 +36853,36 @@ var init__session_output = __esm(() => {
36838
36853
  EMPTY = { success: false, filesChanged: [], output: "", parsed: false };
36839
36854
  });
36840
36855
 
36856
+ // src/operations/execution-gates.ts
36857
+ function shouldRunReview(config2) {
36858
+ return config2.review?.enabled === true;
36859
+ }
36860
+ function shouldRunRectification(config2) {
36861
+ return config2.execution?.rectification?.enabled === true;
36862
+ }
36863
+ function shouldKeepSessionOpen(config2, role) {
36864
+ return SESSION_CONTINUITY_ROLES.has(role) && (shouldRunReview(config2) || shouldRunRectification(config2));
36865
+ }
36866
+ var SESSION_CONTINUITY_ROLES;
36867
+ var init_execution_gates = __esm(() => {
36868
+ init_config();
36869
+ SESSION_CONTINUITY_ROLES = new Set(["implementer", "test-writer"]);
36870
+ });
36871
+
36841
36872
  // src/operations/write-test.ts
36842
36873
  var testWriterOp;
36843
36874
  var init_write_test = __esm(() => {
36844
36875
  init_config();
36845
36876
  init_isolation();
36846
36877
  init__session_output();
36878
+ init_execution_gates();
36847
36879
  testWriterOp = {
36848
36880
  kind: "run",
36849
36881
  name: "test-writer",
36850
36882
  stage: "run",
36851
- session: { role: "test-writer", lifetime: "fresh" },
36883
+ session: { role: "test-writer", lifetime: "warm" },
36852
36884
  config: tddConfigSelector,
36885
+ keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "test-writer"),
36853
36886
  build(input, _ctx) {
36854
36887
  if (input.promptMarkdown?.trim()) {
36855
36888
  return {
@@ -36896,20 +36929,6 @@ var init_write_test = __esm(() => {
36896
36929
  };
36897
36930
  });
36898
36931
 
36899
- // src/operations/execution-gates.ts
36900
- function shouldRunReview(config2) {
36901
- return config2.review?.enabled === true;
36902
- }
36903
- function shouldRunRectification(config2) {
36904
- return config2.execution?.rectification?.enabled === true;
36905
- }
36906
- function shouldKeepSessionOpen(config2, role) {
36907
- return role === "implementer" && (shouldRunReview(config2) || shouldRunRectification(config2));
36908
- }
36909
- var init_execution_gates = __esm(() => {
36910
- init_config();
36911
- });
36912
-
36913
36932
  // src/operations/implement.ts
36914
36933
  var implementerOp;
36915
36934
  var init_implement = __esm(() => {
@@ -38416,10 +38435,11 @@ var init__finding_to_check = __esm(() => {
38416
38435
  });
38417
38436
 
38418
38437
  // src/operations/autofix-implementer-strategy.ts
38419
- function makeAutofixImplementerStrategy(story, config2, sink) {
38438
+ function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
38439
+ const claimsAdversarial = opts.includeAdversarialReview === true;
38420
38440
  return {
38421
38441
  name: "autofix-implementer",
38422
- appliesTo: (f) => f.fixTarget === "source" && IMPLEMENTER_SOURCES.has(f.source),
38442
+ appliesTo: (f) => f.fixTarget === "source" && IMPLEMENTER_SOURCES.has(f.source) || claimsAdversarial && f.source === "adversarial-review",
38423
38443
  fixOp: implementerRectifyOp,
38424
38444
  buildInput: (findings, _prior, _cycleCtx) => ({
38425
38445
  failedChecks: findingsToFailedChecks(findings),
@@ -41078,18 +41098,18 @@ ${exceptions.join(`
41078
41098
  `)}`;
41079
41099
  }
41080
41100
  function implementerOwnsTests(story) {
41081
- return SINGLE_SESSION_TEST_OWNING_STRATEGIES.has(story.routing?.testStrategy ?? "");
41101
+ return isSingleSessionTestOwningStrategy(story.routing?.testStrategy);
41082
41102
  }
41083
41103
  function testEditHeadline(story, prohibition) {
41084
41104
  return implementerOwnsTests(story) ? SINGLE_SESSION_PERMIT_HEADLINE : prohibition;
41085
41105
  }
41086
41106
  function exceptionCountWord(story) {
41087
- return THREE_SESSION_STRATEGIES.has(story.routing?.testStrategy ?? "") ? "four" : "three";
41107
+ return isThreeSessionStrategy(story.routing?.testStrategy) ? "four" : "three";
41088
41108
  }
41089
41109
  function escapeHatchFor(story) {
41090
41110
  if (implementerOwnsTests(story))
41091
41111
  return SINGLE_SESSION_TEST_EDIT_POLICY;
41092
- const isTdd = THREE_SESSION_STRATEGIES.has(story.routing?.testStrategy ?? "");
41112
+ const isTdd = isThreeSessionStrategy(story.routing?.testStrategy);
41093
41113
  return buildEscapeHatch({ includeMockHandoff: isTdd });
41094
41114
  }
41095
41115
  function noTestIsolationBlock(story) {
@@ -41159,6 +41179,7 @@ ${errors3}
41159
41179
  1. Read the relevant files to verify each finding is a real issue
41160
41180
  2. Only fix findings that are actually valid problems
41161
41181
  3. Do NOT add keys, functions, or imports that already exist \u2014 check first
41182
+ 4. Break the fix into one small step per valid finding before touching code, each verified by re-running the relevant check
41162
41183
 
41163
41184
  ${testEditHeadline(story, `Do NOT change test files or test behavior \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below.`)}
41164
41185
  Do NOT add new features \u2014 only fix valid issues.
@@ -41182,6 +41203,7 @@ ${errors3}
41182
41203
  1. Read the relevant files to verify each finding is a real issue
41183
41204
  2. Only fix findings that are actually valid problems
41184
41205
  3. Do NOT add keys, functions, or imports that already exist \u2014 check first
41206
+ 4. Break the fix into one small step per valid finding before touching code, each verified by re-running the relevant check
41185
41207
 
41186
41208
  Do NOT add new features \u2014 only fix valid issues.
41187
41209
  Commit your fixes when done.${scopeConstraint}${noTestIsolationBlock(story)}${escapeHatchFor(story)}`;
@@ -41208,6 +41230,7 @@ ${adversarialErrors}
41208
41230
  1. Read the relevant files to verify each finding is a real issue
41209
41231
  2. Only fix findings that are actually valid problems
41210
41232
  3. Do NOT add keys, functions, or imports that already exist \u2014 check first
41233
+ 4. Break the fix into one small step per valid finding before touching code, each verified by re-running the relevant check
41211
41234
 
41212
41235
  Do NOT add new features \u2014 only fix valid issues.
41213
41236
  Commit your fixes when done.${scopeConstraint}${noTestIsolationBlock(story)}${escapeHatchFor(story)}`;
@@ -41294,7 +41317,7 @@ REASON: <one paragraph: which mock is wrong vs which dispatch the new code uses,
41294
41317
  Rules:
41295
41318
  - Do NOT make any edits yourself; the test-writer will fulfill.
41296
41319
  - Do NOT also emit \`UNRESOLVED:\` in the same turn \u2014 this declaration IS the handoff.
41297
- - FILES must list real test files. Each path must exist and be a test file.`, THREE_SESSION_STRATEGIES, SINGLE_SESSION_TEST_OWNING_STRATEGIES, SINGLE_SESSION_PERMIT_HEADLINE = "You authored these tests in the same session as the implementation, so you MAY edit test files \u2014 but ONLY to resolve a genuine contradiction between a test and this story's acceptance criteria (or between two acceptance criteria). NEVER weaken, delete, loosen, or skip a test merely to make it pass. See the test-edit guidance appended below.", SINGLE_SESSION_TEST_EDIT_POLICY = `
41320
+ - FILES must list real test files. Each path must exist and be a test file.`, SINGLE_SESSION_PERMIT_HEADLINE = "You authored these tests in the same session as the implementation, so you MAY edit test files \u2014 but ONLY to resolve a genuine contradiction between a test and this story's acceptance criteria (or between two acceptance criteria). NEVER weaken, delete, loosen, or skip a test merely to make it pass. See the test-edit guidance appended below.", SINGLE_SESSION_TEST_EDIT_POLICY = `
41298
41321
 
41299
41322
  ## Test-edit guidance (single-session implementer)
41300
41323
 
@@ -41313,10 +41336,9 @@ If two findings or two acceptance criteria contradict each other and you cannot
41313
41336
  both even after adjusting tests, do not guess. Emit:
41314
41337
  UNRESOLVED: <which findings/ACs conflicted and why they cannot both be satisfied>`, CONTRADICTION_ESCAPE_HATCH, MAX_STRUCTURED_FINDINGS = 10, RAW_WITH_FINDINGS_LIMIT = 1000, RAW_FALLBACK_LIMIT = 4000;
41315
41338
  var init_rectifier_builder_helpers = __esm(() => {
41339
+ init_config();
41316
41340
  init_review();
41317
41341
  init_sections2();
41318
- THREE_SESSION_STRATEGIES = new Set(["three-session-tdd", "three-session-tdd-lite"]);
41319
- SINGLE_SESSION_TEST_OWNING_STRATEGIES = new Set(["tdd-simple", "test-after"]);
41320
41342
  CONTRADICTION_ESCAPE_HATCH = buildEscapeHatch({ includeMockHandoff: false });
41321
41343
  });
41322
41344
 
@@ -41474,6 +41496,8 @@ ${findingLines}
41474
41496
 
41475
41497
  **Task:** For each bug above, write a NEW failing test that asserts the spec-correct behavior described in the finding. The test should FAIL with the current (buggy) implementation and PASS once the implementer fixes the source.
41476
41498
 
41499
+ Break the work into one small task per bug before writing: for each, note the failing test's name and which spec-correct behavior it pins down.
41500
+
41477
41501
  Rules:
41478
41502
  1. Write the test against the SPECIFICATION, not the current behavior.
41479
41503
  2. Do NOT fix the source files \u2014 only write test files.
@@ -41501,6 +41525,8 @@ ${fileList}
41501
41525
  ### Implementer handoff
41502
41526
  ${reason}
41503
41527
 
41528
+ Break the work into one small task per listed file before editing: for each, note how its mock setup and dispatch wiring must change to match the AC-mandated dispatch shape.
41529
+
41504
41530
  Rules:
41505
41531
  1. Modify ONLY the files listed above.
41506
41532
  2. Do NOT modify any source file.
@@ -41539,7 +41565,8 @@ Before making any changes:
41539
41565
  2. Do NOT loosen assertions to match current implementation behavior. If a test is failing because the source is wrong, the source is the suspect \u2014 not the test.
41540
41566
  3. Do NOT delete a failing test because the implementation makes it hard to assert on. Refactor the test structure if needed; never silently drop coverage.
41541
41567
  4. If the current behavior disagrees with the acceptance criteria, write the test against the spec and let the implementer fix the source.
41542
- 5. Do NOT modify source implementation files.`;
41568
+ 5. Do NOT modify source implementation files.
41569
+ 6. Break the work into one small change per flagged test before editing, each verified by re-running that test.`;
41543
41570
  return `${opener}
41544
41571
 
41545
41572
  Story: ${story.title} (${story.id})
@@ -41729,7 +41756,7 @@ ${acList}
41729
41756
  ### Findings
41730
41757
  ${llmSection}
41731
41758
 
41732
- **Important:** LLM reviewers may flag false positives. Before making changes for LLM review findings, read the relevant files to verify each finding is a real issue. Do NOT add keys, functions, or imports that already exist.
41759
+ **Important:** LLM reviewers may flag false positives. Before making changes for LLM review findings, read the relevant files to verify each finding is a real issue, then break the fix into one small step per valid finding before touching code, each verified by re-running the relevant check. Do NOT add keys, functions, or imports that already exist.
41733
41760
 
41734
41761
  Do NOT add new features \u2014 only fix the identified issues.
41735
41762
  Commit your fixes when done.${scopeConstraint}${escapeHatchFor(story)}`;
@@ -41887,7 +41914,7 @@ Tests are failing. Fix the source so all tests pass \u2014 not just the ones lis
41887
41914
  ${detail}`);
41888
41915
  }
41889
41916
  lines.push(`
41890
- Fix the implementation to resolve all verifier findings.`);
41917
+ Before editing, break the work into one small fix per finding above (fix -> re-run to verify). Then fix the implementation to resolve all verifier findings.`);
41891
41918
  return lines.join(`
41892
41919
  `);
41893
41920
  }
@@ -48144,8 +48171,9 @@ ${request.summary}
48144
48171
  if (!this.rl) {
48145
48172
  throw new Error("CLI plugin not initialized");
48146
48173
  }
48174
+ let timeoutId;
48147
48175
  const timeoutPromise = new Promise((resolve13) => {
48148
- setTimeout(() => {
48176
+ timeoutId = setTimeout(() => {
48149
48177
  resolve13({
48150
48178
  requestId: request.id,
48151
48179
  action: "skip",
@@ -48155,8 +48183,12 @@ ${request.summary}
48155
48183
  }, timeout);
48156
48184
  });
48157
48185
  const userPromise = this.getUserInput(request);
48158
- const response = await Promise.race([userPromise, timeoutPromise]);
48159
- return response;
48186
+ try {
48187
+ return await Promise.race([userPromise, timeoutPromise]);
48188
+ } finally {
48189
+ if (timeoutId !== undefined)
48190
+ clearTimeout(timeoutId);
48191
+ }
48160
48192
  }
48161
48193
  async getUserInput(request) {
48162
48194
  if (!this.rl) {
@@ -53430,6 +53462,11 @@ function phasesToRevalidate(strategiesRun, allPhases) {
53430
53462
  }
53431
53463
  return allPhases.filter((p) => needed.has(p.kind));
53432
53464
  }
53465
+ function orderGateLast(phases) {
53466
+ const rest = phases.filter((p) => p.kind !== "full-suite-gate");
53467
+ const gates = phases.filter((p) => p.kind === "full-suite-gate");
53468
+ return [...rest, ...gates];
53469
+ }
53433
53470
  function toReviewDecisionPayload(opName, output) {
53434
53471
  if (output === null || output === undefined || typeof output !== "object")
53435
53472
  return null;
@@ -53644,18 +53681,24 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
53644
53681
  scope.close();
53645
53682
  }
53646
53683
  }
53647
- function withIncreasingFailuresBail(strategies, enabled) {
53684
+ function withIncreasingFailuresBail(strategies, enabled, consecutiveIncreases) {
53648
53685
  if (!enabled)
53649
53686
  return strategies;
53687
+ const threshold = Math.max(1, consecutiveIncreases);
53650
53688
  return strategies.map((strategy) => ({
53651
53689
  ...strategy,
53652
53690
  bailWhen: (iterations) => {
53653
53691
  const userReason = strategy.bailWhen?.(iterations) ?? null;
53654
53692
  if (userReason !== null)
53655
53693
  return userReason;
53656
- const last = iterations[iterations.length - 1];
53657
- if (last && last.findingsAfter.length > last.findingsBefore.length) {
53658
- return `failure count increased: ${last.findingsBefore.length} -> ${last.findingsAfter.length}`;
53694
+ if (iterations.length < threshold)
53695
+ return null;
53696
+ const trailing = iterations.slice(-threshold);
53697
+ const allRegressed = trailing.every((it) => it.findingsAfter.length > it.findingsBefore.length);
53698
+ if (allRegressed) {
53699
+ const first = trailing[0];
53700
+ const last = trailing[trailing.length - 1];
53701
+ return `failure count increased for ${threshold} consecutive iteration(s): ${first.findingsBefore.length} -> ${last.findingsAfter.length}`;
53659
53702
  }
53660
53703
  return null;
53661
53704
  }
@@ -53685,13 +53728,14 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53685
53728
  const cycle = {
53686
53729
  findings: [...initialFindings],
53687
53730
  iterations: [],
53688
- strategies: withIncreasingFailuresBail(rectification.strategies, rectification.abortOnIncreasingFailures),
53731
+ strategies: withIncreasingFailuresBail(rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
53689
53732
  config: { maxAttemptsTotal: rectification.maxAttempts, validatorRetries: 1 },
53690
53733
  validate: async (_validateCtx, opts) => {
53691
53734
  if (ctx.runtime.signal?.aborted)
53692
53735
  return { findings: [], shortCircuited: false };
53693
53736
  const lite = (opts?.mode ?? "full") === "lite";
53694
- const phases = phasesToRevalidate(opts?.strategiesRun, validationPhases);
53737
+ const selected = phasesToRevalidate(opts?.strategiesRun, validationPhases);
53738
+ const phases = lite ? orderGateLast(selected) : selected;
53695
53739
  getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
53696
53740
  storyId: ctx.storyId,
53697
53741
  mode: opts?.mode ?? "full",
@@ -53701,9 +53745,6 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53701
53745
  const findings = [];
53702
53746
  let shortCircuited = false;
53703
53747
  for (const phase of phases) {
53704
- if (lite && phase.kind === "full-suite-gate") {
53705
- continue;
53706
- }
53707
53748
  await runPhase(ctx, phase.slot, phaseCosts, phaseOutputs);
53708
53749
  if (shouldSkipPhaseForRectification(phase, state, phaseOutputs))
53709
53750
  continue;
@@ -54019,9 +54060,6 @@ var init_story_orchestrator = __esm(() => {
54019
54060
 
54020
54061
  // src/execution/build-plan-for-strategy.ts
54021
54062
  import { join as join46 } from "path";
54022
- function isThreeSessionStrategy(strategy) {
54023
- return THREE_SESSION_STRATEGIES2.has(strategy);
54024
- }
54025
54063
  function requiresInitialRefCapture(strategy) {
54026
54064
  return isThreeSessionStrategy(strategy);
54027
54065
  }
@@ -54080,8 +54118,12 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
54080
54118
  strategies.push(makeFullSuiteRectifyStrategy(story, config2));
54081
54119
  }
54082
54120
  if (config2.quality.autofix?.enabled !== false) {
54083
- strategies.push(makeAutofixImplementerStrategy(story, config2, sink));
54084
- strategies.push(makeAutofixTestWriterStrategy(story, config2, sink));
54121
+ strategies.push(makeAutofixImplementerStrategy(story, config2, sink, {
54122
+ includeAdversarialReview: !isThreeSession
54123
+ }));
54124
+ if (isThreeSession) {
54125
+ strategies.push(makeAutofixTestWriterStrategy(story, config2, sink));
54126
+ }
54085
54127
  }
54086
54128
  const postValidate = async (findings, _validateCtx) => {
54087
54129
  if (sink.testEdits.length === 0 && sink.mockHandoffs.length === 0)
@@ -54107,14 +54149,13 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
54107
54149
  }
54108
54150
  return builder.build(ctx, { isThreeSession });
54109
54151
  }
54110
- var THREE_SESSION_STRATEGIES2;
54111
54152
  var init_build_plan_for_strategy = __esm(() => {
54153
+ init_config();
54112
54154
  init_operations();
54113
54155
  init_execution_gates();
54114
54156
  init_full_suite_rectify();
54115
54157
  init_test_runners();
54116
54158
  init_story_orchestrator();
54117
- THREE_SESSION_STRATEGIES2 = new Set(["three-session-tdd", "three-session-tdd-lite"]);
54118
54159
  });
54119
54160
 
54120
54161
  // src/execution/plan-inputs.ts
@@ -54314,7 +54355,8 @@ async function assemblePlanInputsFromCtx(ctx) {
54314
54355
  const rectificationInput = ctx.config.execution?.rectification?.enabled === true ? {
54315
54356
  maxAttempts: ctx.config.execution.rectification.maxAttemptsTotal,
54316
54357
  strategies: [],
54317
- abortOnIncreasingFailures: ctx.config.execution.rectification.abortOnIncreasingFailures
54358
+ abortOnIncreasingFailures: ctx.config.execution.rectification.abortOnIncreasingFailures,
54359
+ consecutiveIncreasesToBail: ctx.config.execution.rectification.consecutiveIncreasesToBail
54318
54360
  } : undefined;
54319
54361
  return {
54320
54362
  story,
@@ -54333,12 +54375,12 @@ async function assemblePlanInputsFromCtx(ctx) {
54333
54375
  };
54334
54376
  }
54335
54377
  var init_plan_inputs = __esm(() => {
54378
+ init_config();
54336
54379
  init_context();
54337
54380
  init_errors();
54338
54381
  init_prompts();
54339
54382
  init_review();
54340
54383
  init_resolver();
54341
- init_build_plan_for_strategy();
54342
54384
  });
54343
54385
 
54344
54386
  // src/pipeline/stages/execution-helpers.ts
@@ -54865,6 +54907,7 @@ var init_post_run = __esm(() => {
54865
54907
  // src/pipeline/stages/execution.ts
54866
54908
  var RUNTIME_CRASH_CODES, executionStage, _executionDeps;
54867
54909
  var init_execution = __esm(() => {
54910
+ init_config();
54868
54911
  init_agents();
54869
54912
  init_errors();
54870
54913
  init_build_plan_for_strategy();
@@ -55455,7 +55498,16 @@ var init_routing2 = __esm(() => {
55455
55498
  const TIER_RANK = { fast: 0, balanced: 1, powerful: 2 };
55456
55499
  const derivedTier = decision.modelTier;
55457
55500
  const previousTier = ctx.story.routing?.modelTier;
55458
- const isEscalated = previousTier !== undefined && (TIER_RANK[previousTier] ?? 0) > (TIER_RANK[derivedTier] ?? 0);
55501
+ const previousRank = previousTier !== undefined ? TIER_RANK[previousTier] : undefined;
55502
+ const derivedRank = TIER_RANK[derivedTier];
55503
+ if (previousTier !== undefined && previousRank === undefined) {
55504
+ logger?.warn("routing", "Ignoring unknown previousTier \u2014 not escalating", {
55505
+ storyId: ctx.story.id,
55506
+ previousTier,
55507
+ derivedTier
55508
+ });
55509
+ }
55510
+ const isEscalated = previousTier !== undefined && previousRank !== undefined && derivedRank !== undefined && previousRank > derivedRank;
55459
55511
  const modelTier = isEscalated ? previousTier : derivedTier;
55460
55512
  const routing = { ...decision, modelTier, agent: ctx.story.routing?.agent };
55461
55513
  ctx.story.routing = {
@@ -55562,6 +55614,7 @@ var init_pipeline = __esm(() => {
55562
55614
  init_runner4();
55563
55615
  init_events();
55564
55616
  init_stages();
55617
+ init_event_bus();
55565
55618
  });
55566
55619
 
55567
55620
  // src/cli/prompts-shared.ts
@@ -58122,12 +58175,12 @@ var init_loader4 = __esm(() => {
58122
58175
  });
58123
58176
 
58124
58177
  // src/utils/paths.ts
58125
- import { join as join64 } from "path";
58178
+ import { join as join63 } from "path";
58126
58179
  function getRunsDir() {
58127
- return process.env.NAX_RUNS_DIR ?? join64(globalConfigDir(), "runs");
58180
+ return process.env.NAX_RUNS_DIR ?? join63(globalConfigDir(), "runs");
58128
58181
  }
58129
58182
  function getEventsRootDir() {
58130
- return join64(globalConfigDir(), "events");
58183
+ return join63(globalConfigDir(), "events");
58131
58184
  }
58132
58185
  var init_paths3 = __esm(() => {
58133
58186
  init_paths();
@@ -58187,7 +58240,7 @@ var init_command_argv = __esm(() => {
58187
58240
  });
58188
58241
 
58189
58242
  // src/hooks/runner.ts
58190
- import { join as join71 } from "path";
58243
+ import { join as join70 } from "path";
58191
58244
  function createDrainDeadline2(deadlineMs) {
58192
58245
  let timeoutId;
58193
58246
  const promise2 = new Promise((resolve16) => {
@@ -58206,14 +58259,14 @@ async function loadHooksConfig(projectDir, globalDir) {
58206
58259
  let globalHooks = { hooks: {} };
58207
58260
  let projectHooks = { hooks: {} };
58208
58261
  let skipGlobal = false;
58209
- const projectPath = join71(projectDir, "hooks.json");
58262
+ const projectPath = join70(projectDir, "hooks.json");
58210
58263
  const projectData = await loadJsonFile(projectPath, "hooks");
58211
58264
  if (projectData) {
58212
58265
  projectHooks = projectData;
58213
58266
  skipGlobal = projectData.skipGlobal ?? false;
58214
58267
  }
58215
58268
  if (!skipGlobal && globalDir) {
58216
- const globalPath = join71(globalDir, "hooks.json");
58269
+ const globalPath = join70(globalDir, "hooks.json");
58217
58270
  const globalData = await loadJsonFile(globalPath, "hooks");
58218
58271
  if (globalData) {
58219
58272
  globalHooks = globalData;
@@ -58383,7 +58436,7 @@ var package_default;
58383
58436
  var init_package = __esm(() => {
58384
58437
  package_default = {
58385
58438
  name: "@nathapp/nax",
58386
- version: "0.68.4",
58439
+ version: "0.68.6",
58387
58440
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
58388
58441
  type: "module",
58389
58442
  bin: {
@@ -58478,8 +58531,8 @@ var init_version = __esm(() => {
58478
58531
  NAX_VERSION = package_default.version;
58479
58532
  NAX_COMMIT = (() => {
58480
58533
  try {
58481
- if (/^[0-9a-f]{6,10}$/.test("197c6530"))
58482
- return "197c6530";
58534
+ if (/^[0-9a-f]{6,10}$/.test("acc1d4bd"))
58535
+ return "acc1d4bd";
58483
58536
  } catch {}
58484
58537
  try {
58485
58538
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -58500,9 +58553,9 @@ var init_version = __esm(() => {
58500
58553
  // src/execution/crash-heartbeat.ts
58501
58554
  import { appendFileSync as appendFileSync2 } from "fs";
58502
58555
  async function heartbeatLoop(statusWriter, getTotalCost, getIterations, jsonlFilePath) {
58503
- const logger = getSafeLogger();
58556
+ const logger = _heartbeatDeps.getSafeLogger();
58504
58557
  while (heartbeatActive) {
58505
- await Bun.sleep(60000);
58558
+ await _heartbeatDeps.sleep(60000);
58506
58559
  if (!heartbeatActive)
58507
58560
  break;
58508
58561
  try {
@@ -58531,10 +58584,14 @@ async function heartbeatLoop(statusWriter, getTotalCost, getIterations, jsonlFil
58531
58584
  }
58532
58585
  }
58533
58586
  function startHeartbeat(statusWriter, getTotalCost, getIterations, jsonlFilePath) {
58534
- const logger = getSafeLogger();
58587
+ const logger = _heartbeatDeps.getSafeLogger();
58535
58588
  stopHeartbeat();
58536
58589
  heartbeatActive = true;
58537
- heartbeatLoop(statusWriter, getTotalCost, getIterations, jsonlFilePath).catch(() => {});
58590
+ heartbeatLoop(statusWriter, getTotalCost, getIterations, jsonlFilePath).catch((err) => {
58591
+ _heartbeatDeps.getSafeLogger()?.warn("crash-recovery", "Heartbeat loop crashed; status updates stopped", {
58592
+ error: err instanceof Error ? err.message : String(err)
58593
+ });
58594
+ });
58538
58595
  logger?.debug("crash-recovery", "Heartbeat started (60s interval)");
58539
58596
  }
58540
58597
  function stopHeartbeat() {
@@ -58543,9 +58600,13 @@ function stopHeartbeat() {
58543
58600
  getSafeLogger()?.debug("crash-recovery", "Heartbeat stopped");
58544
58601
  }
58545
58602
  }
58546
- var heartbeatActive = false;
58603
+ var _heartbeatDeps, heartbeatActive = false;
58547
58604
  var init_crash_heartbeat = __esm(() => {
58548
58605
  init_logger2();
58606
+ _heartbeatDeps = {
58607
+ sleep: async (ms) => Bun.sleep(ms),
58608
+ getSafeLogger
58609
+ };
58549
58610
  });
58550
58611
 
58551
58612
  // src/execution/crash-writer.ts
@@ -59348,15 +59409,15 @@ var init_acceptance_loop = __esm(() => {
59348
59409
 
59349
59410
  // src/session/scratch-purge.ts
59350
59411
  import { mkdir as mkdir13, rename, rm } from "fs/promises";
59351
- import { dirname as dirname12, join as join72 } from "path";
59412
+ import { dirname as dirname12, join as join71 } from "path";
59352
59413
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
59353
- const sessionsDir = join72(projectDir, ".nax", "features", featureName, "sessions");
59414
+ const sessionsDir = join71(projectDir, ".nax", "features", featureName, "sessions");
59354
59415
  const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
59355
59416
  const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
59356
59417
  let purged = 0;
59357
59418
  for (const sessionId of sessionIds) {
59358
- const sessionDir = join72(sessionsDir, sessionId);
59359
- const descriptorPath = join72(sessionDir, "descriptor.json");
59419
+ const sessionDir = join71(sessionsDir, sessionId);
59420
+ const descriptorPath = join71(sessionDir, "descriptor.json");
59360
59421
  if (!await _scratchPurgeDeps.fileExists(descriptorPath))
59361
59422
  continue;
59362
59423
  let lastActivityAt;
@@ -59372,7 +59433,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
59372
59433
  if (new Date(lastActivityAt).getTime() >= cutoffMs)
59373
59434
  continue;
59374
59435
  if (archiveInsteadOfDelete) {
59375
- const archiveDest = join72(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
59436
+ const archiveDest = join71(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
59376
59437
  await _scratchPurgeDeps.move(sessionDir, archiveDest);
59377
59438
  } else {
59378
59439
  await _scratchPurgeDeps.remove(sessionDir);
@@ -59595,6 +59656,13 @@ async function runDeferredRegression(options) {
59595
59656
  storyOutcomes: {}
59596
59657
  };
59597
59658
  }
59659
+ for (const storyId of affectedStories) {
59660
+ pipelineEventBus.emit({
59661
+ type: "regression:detected",
59662
+ storyId,
59663
+ failedTests: testSummary.failed
59664
+ });
59665
+ }
59598
59666
  let rectificationAttempts = 0;
59599
59667
  let storiesRectified = 0;
59600
59668
  let currentTestOutput = fullSuiteResult.output;
@@ -59708,6 +59776,7 @@ var init_run_regression = __esm(() => {
59708
59776
  init_findings();
59709
59777
  init_logger2();
59710
59778
  init_operations();
59779
+ init_pipeline();
59711
59780
  init_prd();
59712
59781
  init_test_runners();
59713
59782
  init_git();
@@ -60105,12 +60174,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
60105
60174
 
60106
60175
  // src/pipeline/subscribers/events-writer.ts
60107
60176
  import { appendFile as appendFile4, mkdir as mkdir14 } from "fs/promises";
60108
- import { basename as basename14, join as join73 } from "path";
60177
+ import { basename as basename13, join as join72 } from "path";
60109
60178
  function wireEventsWriter(bus, feature, runId, workdir) {
60110
60179
  const logger = getSafeLogger();
60111
- const project = basename14(workdir);
60112
- const eventsDir = join73(getEventsRootDir(), project);
60113
- const eventsFile = join73(eventsDir, "events.jsonl");
60180
+ const project = basename13(workdir);
60181
+ const eventsDir = join72(getEventsRootDir(), project);
60182
+ const eventsFile = join72(eventsDir, "events.jsonl");
60114
60183
  let dirReady = false;
60115
60184
  const write = (line) => {
60116
60185
  return (async () => {
@@ -60291,12 +60360,12 @@ var init_interaction2 = __esm(() => {
60291
60360
 
60292
60361
  // src/pipeline/subscribers/registry.ts
60293
60362
  import { mkdir as mkdir15, writeFile as writeFile2 } from "fs/promises";
60294
- import { basename as basename15, join as join74 } from "path";
60363
+ import { basename as basename14, join as join73 } from "path";
60295
60364
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
60296
60365
  const logger = getSafeLogger();
60297
- const project = basename15(workdir);
60298
- const runDir = join74(getRunsDir(), `${project}-${feature}-${runId}`);
60299
- const metaFile = join74(runDir, "meta.json");
60366
+ const project = basename14(workdir);
60367
+ const runDir = join73(getRunsDir(), `${project}-${feature}-${runId}`);
60368
+ const metaFile = join73(runDir, "meta.json");
60300
60369
  const unsub = bus.on("run:started", (_ev) => {
60301
60370
  return (async () => {
60302
60371
  try {
@@ -60306,8 +60375,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
60306
60375
  project,
60307
60376
  feature,
60308
60377
  workdir,
60309
- statusPath: join74(outputDir, "features", feature, "status.json"),
60310
- eventsDir: join74(outputDir, "features", feature, "runs"),
60378
+ statusPath: join73(outputDir, "features", feature, "status.json"),
60379
+ eventsDir: join73(outputDir, "features", feature, "runs"),
60311
60380
  registeredAt: new Date().toISOString()
60312
60381
  };
60313
60382
  await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
@@ -60552,8 +60621,8 @@ var init_types9 = __esm(() => {
60552
60621
  });
60553
60622
 
60554
60623
  // src/worktree/dependencies.ts
60555
- import { existsSync as existsSync32 } from "fs";
60556
- import { join as join75 } from "path";
60624
+ import { existsSync as existsSync31 } from "fs";
60625
+ import { join as join74 } from "path";
60557
60626
  async function prepareWorktreeDependencies(options) {
60558
60627
  const mode = options.config.execution.worktreeDependencies.mode;
60559
60628
  const resolvedCwd = resolveDependencyCwd(options);
@@ -60567,7 +60636,7 @@ async function prepareWorktreeDependencies(options) {
60567
60636
  }
60568
60637
  }
60569
60638
  function resolveDependencyCwd(options) {
60570
- return options.storyWorkdir ? join75(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
60639
+ return options.storyWorkdir ? join74(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
60571
60640
  }
60572
60641
  function resolveInheritedDependencies(options, resolvedCwd) {
60573
60642
  if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
@@ -60577,7 +60646,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
60577
60646
  }
60578
60647
  function hasDependencyManifests(worktreeRoot, resolvedCwd) {
60579
60648
  const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
60580
- return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join75(directory, filename))));
60649
+ return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join74(directory, filename))));
60581
60650
  }
60582
60651
  async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
60583
60652
  const setupCommand = config2.execution.worktreeDependencies.setupCommand;
@@ -60628,7 +60697,7 @@ var init_dependencies = __esm(() => {
60628
60697
  "build.gradle.kts"
60629
60698
  ];
60630
60699
  _worktreeDependencyDeps = {
60631
- existsSync: existsSync32,
60700
+ existsSync: existsSync31,
60632
60701
  spawn
60633
60702
  };
60634
60703
  });
@@ -60639,19 +60708,19 @@ __export(exports_manager, {
60639
60708
  _managerDeps: () => _managerDeps,
60640
60709
  WorktreeManager: () => WorktreeManager
60641
60710
  });
60642
- import { existsSync as existsSync33, symlinkSync } from "fs";
60711
+ import { existsSync as existsSync32, symlinkSync } from "fs";
60643
60712
  import { mkdir as mkdir16 } from "fs/promises";
60644
- import { join as join76 } from "path";
60713
+ import { join as join75 } from "path";
60645
60714
 
60646
60715
  class WorktreeManager {
60647
60716
  async ensureGitExcludes(projectRoot) {
60648
60717
  const logger = getSafeLogger();
60649
- const infoDir = join76(projectRoot, ".git", "info");
60650
- const excludePath = join76(infoDir, "exclude");
60718
+ const infoDir = join75(projectRoot, ".git", "info");
60719
+ const excludePath = join75(infoDir, "exclude");
60651
60720
  try {
60652
60721
  await mkdir16(infoDir, { recursive: true });
60653
60722
  let existing = "";
60654
- if (existsSync33(excludePath)) {
60723
+ if (existsSync32(excludePath)) {
60655
60724
  existing = await Bun.file(excludePath).text();
60656
60725
  }
60657
60726
  const missing = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
@@ -60674,7 +60743,7 @@ ${missing.join(`
60674
60743
  }
60675
60744
  async create(projectRoot, storyId) {
60676
60745
  validateStoryId(storyId);
60677
- const worktreePath = join76(projectRoot, ".nax-wt", storyId);
60746
+ const worktreePath = join75(projectRoot, ".nax-wt", storyId);
60678
60747
  const branchName = `nax/${storyId}`;
60679
60748
  try {
60680
60749
  const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
@@ -60715,9 +60784,9 @@ ${missing.join(`
60715
60784
  }
60716
60785
  throw new Error(`Failed to create worktree: ${String(error48)}`);
60717
60786
  }
60718
- const envSource = join76(projectRoot, ".env");
60719
- if (existsSync33(envSource)) {
60720
- const envTarget = join76(worktreePath, ".env");
60787
+ const envSource = join75(projectRoot, ".env");
60788
+ if (existsSync32(envSource)) {
60789
+ const envTarget = join75(worktreePath, ".env");
60721
60790
  try {
60722
60791
  symlinkSync(envSource, envTarget, "file");
60723
60792
  } catch (error48) {
@@ -60728,7 +60797,7 @@ ${missing.join(`
60728
60797
  }
60729
60798
  async remove(projectRoot, storyId) {
60730
60799
  validateStoryId(storyId);
60731
- const worktreePath = join76(projectRoot, ".nax-wt", storyId);
60800
+ const worktreePath = join75(projectRoot, ".nax-wt", storyId);
60732
60801
  const branchName = `nax/${storyId}`;
60733
60802
  try {
60734
60803
  const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
@@ -61406,6 +61475,12 @@ async function handleTierEscalation(ctx) {
61406
61475
  runtime: ctx.runtime
61407
61476
  });
61408
61477
  }
61478
+ pipelineEventBus.emit({
61479
+ type: "story:escalated",
61480
+ storyId: ctx.story.id,
61481
+ fromTier: ctx.routing.modelTier,
61482
+ toTier: escalatedTier
61483
+ });
61409
61484
  return {
61410
61485
  outcome: "escalated",
61411
61486
  prdDirty: true,
@@ -61416,6 +61491,7 @@ var _tierEscalationDeps;
61416
61491
  var init_tier_escalation = __esm(() => {
61417
61492
  init_hooks();
61418
61493
  init_logger2();
61494
+ init_event_bus();
61419
61495
  init_prd();
61420
61496
  init_routing();
61421
61497
  init_llm();
@@ -61531,10 +61607,10 @@ var init_merge_conflict_rectify = __esm(() => {
61531
61607
  });
61532
61608
 
61533
61609
  // src/execution/pipeline-result-handler.ts
61534
- import { join as join77 } from "path";
61610
+ import { join as join76 } from "path";
61535
61611
  async function removeWorktreeDirectory(projectRoot, storyId) {
61536
61612
  const logger = getSafeLogger();
61537
- const worktreePath = join77(projectRoot, ".nax-wt", storyId);
61613
+ const worktreePath = join76(projectRoot, ".nax-wt", storyId);
61538
61614
  try {
61539
61615
  const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
61540
61616
  cwd: projectRoot,
@@ -61664,6 +61740,11 @@ async function handlePipelineFailure(ctx, pipelineResult) {
61664
61740
  break;
61665
61741
  case "skip":
61666
61742
  logger?.warn("pipeline", "Story skipped", { storyId: ctx.story.id, reason: pipelineResult.reason });
61743
+ pipelineEventBus.emit({
61744
+ type: "story:skipped",
61745
+ storyId: ctx.story.id,
61746
+ reason: pipelineResult.reason || "Story skipped"
61747
+ });
61667
61748
  prdDirty = true;
61668
61749
  break;
61669
61750
  case "fail":
@@ -61744,8 +61825,8 @@ var init_pipeline_result_handler = __esm(() => {
61744
61825
  });
61745
61826
 
61746
61827
  // src/execution/iteration-runner.ts
61747
- import { existsSync as existsSync34 } from "fs";
61748
- import { join as join78 } from "path";
61828
+ import { existsSync as existsSync33 } from "fs";
61829
+ import { join as join77 } from "path";
61749
61830
  async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
61750
61831
  const { story, storiesToExecute, routing, isBatchExecution } = selection;
61751
61832
  if (ctx.dryRun) {
@@ -61770,7 +61851,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
61770
61851
  const storyStartTime = Date.now();
61771
61852
  let effectiveWorkdir = ctx.workdir;
61772
61853
  if (ctx.config.execution.storyIsolation === "worktree") {
61773
- const worktreePath = join78(ctx.workdir, ".nax-wt", story.id);
61854
+ const worktreePath = join77(ctx.workdir, ".nax-wt", story.id);
61774
61855
  const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
61775
61856
  if (!worktreeExists) {
61776
61857
  await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
@@ -61790,7 +61871,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
61790
61871
  }
61791
61872
  const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
61792
61873
  const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
61793
- const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join78(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
61874
+ const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join77(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
61794
61875
  let dependencyContext;
61795
61876
  if (ctx.config.execution.storyIsolation === "worktree") {
61796
61877
  try {
@@ -61817,7 +61898,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
61817
61898
  };
61818
61899
  }
61819
61900
  }
61820
- const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join78(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join78(ctx.workdir, story.workdir) : ctx.workdir;
61901
+ const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join77(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join77(ctx.workdir, story.workdir) : ctx.workdir;
61821
61902
  const pipelineContext = {
61822
61903
  config: effectiveConfig,
61823
61904
  rootConfig: ctx.config,
@@ -61949,7 +62030,7 @@ var init_iteration_runner = __esm(() => {
61949
62030
  loadConfigForWorkdir,
61950
62031
  prepareWorktreeDependencies,
61951
62032
  runPipeline,
61952
- existsSync: existsSync34,
62033
+ existsSync: existsSync33,
61953
62034
  worktreeManager: new WorktreeManager
61954
62035
  };
61955
62036
  });
@@ -61960,7 +62041,7 @@ function selectNextStories(prd, config2, batchPlan, currentBatchIndex, lastStory
61960
62041
  const batch = batchPlan[currentBatchIndex];
61961
62042
  const storiesToExecute = batch.stories.filter((s) => !s.passes && s.status !== "passed" && s.status !== "skipped" && s.status !== "blocked" && s.status !== "failed" && s.status !== "paused" && s.status !== "decomposed");
61962
62043
  if (storiesToExecute.length === 0) {
61963
- return { selection: null, nextBatchIndex: currentBatchIndex + 1 };
62044
+ return null;
61964
62045
  }
61965
62046
  const story2 = storiesToExecute[0];
61966
62047
  return {
@@ -62019,7 +62100,7 @@ __export(exports_parallel_worker, {
62019
62100
  buildWorktreePipelineContext: () => buildWorktreePipelineContext,
62020
62101
  _parallelWorkerDeps: () => _parallelWorkerDeps
62021
62102
  });
62022
- import { join as join79 } from "path";
62103
+ import { join as join78 } from "path";
62023
62104
  function buildWorktreePipelineContext(base, _story) {
62024
62105
  return { ...base, prd: structuredClone(base.prd) };
62025
62106
  }
@@ -62042,7 +62123,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
62042
62123
  story,
62043
62124
  stories: [story],
62044
62125
  projectDir: context.projectDir,
62045
- workdir: dependencyContext.cwd ?? (story.workdir ? join79(worktreePath, story.workdir) : worktreePath),
62126
+ workdir: dependencyContext.cwd ?? (story.workdir ? join78(worktreePath, story.workdir) : worktreePath),
62046
62127
  worktreeDependencyContext: dependencyContext,
62047
62128
  routing,
62048
62129
  storyGitRef: storyGitRef ?? undefined
@@ -62167,10 +62248,27 @@ async function runParallelBatch(options) {
62167
62248
  const rootConfigPath = path21.join(workdir, ".nax", "config.json");
62168
62249
  const profileOverride = config2.profile && config2.profile !== "default" ? { profile: config2.profile } : undefined;
62169
62250
  const storyEffectiveConfigs = new Map;
62170
- await Promise.all(stories.filter((story) => story.workdir).map(async (story) => {
62171
- const effectiveConfig = await loadConfigForWorkdir(rootConfigPath, story.workdir, profileOverride);
62172
- storyEffectiveConfigs.set(story.id, effectiveConfig);
62251
+ const configResults = await Promise.allSettled(stories.filter((story) => story.workdir).map(async (story) => {
62252
+ try {
62253
+ const effectiveConfig = await _parallelBatchDeps.loadConfigForWorkdir(rootConfigPath, story.workdir, profileOverride);
62254
+ return { storyId: story.id, effectiveConfig };
62255
+ } catch (err) {
62256
+ const enriched = new Error(err instanceof Error ? err.message : String(err));
62257
+ enriched.storyId = story.id;
62258
+ throw enriched;
62259
+ }
62173
62260
  }));
62261
+ for (const result of configResults) {
62262
+ if (result.status === "fulfilled") {
62263
+ storyEffectiveConfigs.set(result.value.storyId, result.value.effectiveConfig);
62264
+ } else {
62265
+ const storyId = result.reason?.storyId ?? "(unknown)";
62266
+ logger?.warn("parallel-batch", "Failed to load per-story config; using root config", {
62267
+ storyId,
62268
+ reason: result.reason instanceof Error ? result.reason.message : String(result.reason)
62269
+ });
62270
+ }
62271
+ }
62174
62272
  const dependencyContexts = new Map;
62175
62273
  const readyStories = [];
62176
62274
  const preExecutionFailures = [];
@@ -62329,7 +62427,8 @@ var init_parallel_batch = __esm(() => {
62329
62427
  const { rectifyConflictedStory: rectifyConflictedStory2 } = await Promise.resolve().then(() => (init_merge_conflict_rectify(), exports_merge_conflict_rectify));
62330
62428
  return rectifyConflictedStory2(opts);
62331
62429
  },
62332
- prepareWorktreeDependencies
62430
+ prepareWorktreeDependencies,
62431
+ loadConfigForWorkdir
62333
62432
  };
62334
62433
  });
62335
62434
 
@@ -62697,6 +62796,8 @@ async function executeUnified(ctx, initialPrd) {
62697
62796
  if (!selected)
62698
62797
  return buildResult2("no-stories");
62699
62798
  const { selection } = selected;
62799
+ if (!selection)
62800
+ return buildResult2("no-stories");
62700
62801
  if (!ctx.useBatch)
62701
62802
  lastStoryId = selection.story.id;
62702
62803
  {
@@ -62903,7 +63004,7 @@ async function writeStatusFile(filePath, status) {
62903
63004
  var init_status_file = () => {};
62904
63005
 
62905
63006
  // src/execution/status-writer.ts
62906
- import { join as join80 } from "path";
63007
+ import { join as join79 } from "path";
62907
63008
 
62908
63009
  class StatusWriter {
62909
63010
  statusFile;
@@ -63022,7 +63123,7 @@ class StatusWriter {
63022
63123
  if (!this._prd)
63023
63124
  return;
63024
63125
  const safeLogger = getSafeLogger();
63025
- const featureStatusPath = join80(featureDir, "status.json");
63126
+ const featureStatusPath = join79(featureDir, "status.json");
63026
63127
  const write = async () => {
63027
63128
  try {
63028
63129
  const base = this.getSnapshot(totalCost, iterations);
@@ -63053,11 +63154,11 @@ __export(exports_migrate, {
63053
63154
  migrateCommand: () => migrateCommand,
63054
63155
  detectGeneratedContent: () => detectGeneratedContent
63055
63156
  });
63056
- import { existsSync as existsSync35 } from "fs";
63157
+ import { existsSync as existsSync34 } from "fs";
63057
63158
  import { mkdir as mkdir17, readdir as readdir6, rename as rename3 } from "fs/promises";
63058
63159
  import path22 from "path";
63059
63160
  async function detectGeneratedContent(naxDir) {
63060
- if (!existsSync35(naxDir))
63161
+ if (!existsSync34(naxDir))
63061
63162
  return [];
63062
63163
  const candidates = [];
63063
63164
  let entries = [];
@@ -63072,7 +63173,7 @@ async function detectGeneratedContent(naxDir) {
63072
63173
  }
63073
63174
  }
63074
63175
  const featuresDir = path22.join(naxDir, "features");
63075
- if (existsSync35(featuresDir)) {
63176
+ if (existsSync34(featuresDir)) {
63076
63177
  let featureDirs = [];
63077
63178
  try {
63078
63179
  featureDirs = await readdir6(featuresDir);
@@ -63134,7 +63235,7 @@ async function migrateCommand(options) {
63134
63235
  });
63135
63236
  }
63136
63237
  const src = path22.join(globalConfigDir(), options.reclaim);
63137
- if (!existsSync35(src)) {
63238
+ if (!existsSync34(src)) {
63138
63239
  throw new NaxError(`Nothing to reclaim: ~/.nax/${options.reclaim} does not exist`, "MIGRATE_RECLAIM_NOT_FOUND", {
63139
63240
  stage: "migrate",
63140
63241
  name: options.reclaim
@@ -63180,7 +63281,7 @@ async function migrateCommand(options) {
63180
63281
  }
63181
63282
  const naxDir = path22.join(options.workdir, ".nax");
63182
63283
  const configPath = path22.join(naxDir, "config.json");
63183
- if (!existsSync35(configPath)) {
63284
+ if (!existsSync34(configPath)) {
63184
63285
  throw new NaxError("No .nax/config.json found \u2014 run nax init first", "MIGRATE_NO_CONFIG", {
63185
63286
  stage: "migrate",
63186
63287
  workdir: options.workdir
@@ -63215,7 +63316,7 @@ async function migrateCommand(options) {
63215
63316
  for (const candidate of candidates) {
63216
63317
  const dest = path22.join(destBase, candidate.name);
63217
63318
  await mkdir17(path22.dirname(dest), { recursive: true });
63218
- if (existsSync35(dest)) {
63319
+ if (existsSync34(dest)) {
63219
63320
  throw new NaxError(`Migration conflict: destination already exists.
63220
63321
  Source: ${candidate.srcPath}
63221
63322
  Destination: ${dest}
@@ -63456,7 +63557,7 @@ __export(exports_run_initialization, {
63456
63557
  initializeRun: () => initializeRun,
63457
63558
  _reconcileDeps: () => _reconcileDeps
63458
63559
  });
63459
- import { join as join81 } from "path";
63560
+ import { join as join80 } from "path";
63460
63561
  async function reconcileState(prd, prdPath, workdir, config2) {
63461
63562
  const logger = getSafeLogger();
63462
63563
  let reconciledCount = 0;
@@ -63473,7 +63574,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
63473
63574
  });
63474
63575
  continue;
63475
63576
  }
63476
- const effectiveWorkdir = story.workdir ? join81(workdir, story.workdir) : workdir;
63577
+ const effectiveWorkdir = story.workdir ? join80(workdir, story.workdir) : workdir;
63477
63578
  try {
63478
63579
  const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
63479
63580
  if (!reviewResult.success) {
@@ -93031,6 +93132,229 @@ var require_stack_utils = __commonJS((exports, module) => {
93031
93132
  module.exports = StackUtils;
93032
93133
  });
93033
93134
 
93135
+ // node_modules/react/cjs/react-jsx-dev-runtime.development.js
93136
+ var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
93137
+ var React11 = __toESM(require_react());
93138
+ (function() {
93139
+ function getComponentNameFromType(type) {
93140
+ if (type == null)
93141
+ return null;
93142
+ if (typeof type === "function")
93143
+ return type.$$typeof === REACT_CLIENT_REFERENCE ? null : type.displayName || type.name || null;
93144
+ if (typeof type === "string")
93145
+ return type;
93146
+ switch (type) {
93147
+ case REACT_FRAGMENT_TYPE:
93148
+ return "Fragment";
93149
+ case REACT_PROFILER_TYPE:
93150
+ return "Profiler";
93151
+ case REACT_STRICT_MODE_TYPE:
93152
+ return "StrictMode";
93153
+ case REACT_SUSPENSE_TYPE:
93154
+ return "Suspense";
93155
+ case REACT_SUSPENSE_LIST_TYPE:
93156
+ return "SuspenseList";
93157
+ case REACT_ACTIVITY_TYPE:
93158
+ return "Activity";
93159
+ }
93160
+ if (typeof type === "object")
93161
+ switch (typeof type.tag === "number" && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), type.$$typeof) {
93162
+ case REACT_PORTAL_TYPE:
93163
+ return "Portal";
93164
+ case REACT_CONTEXT_TYPE:
93165
+ return type.displayName || "Context";
93166
+ case REACT_CONSUMER_TYPE:
93167
+ return (type._context.displayName || "Context") + ".Consumer";
93168
+ case REACT_FORWARD_REF_TYPE:
93169
+ var innerType = type.render;
93170
+ type = type.displayName;
93171
+ type || (type = innerType.displayName || innerType.name || "", type = type !== "" ? "ForwardRef(" + type + ")" : "ForwardRef");
93172
+ return type;
93173
+ case REACT_MEMO_TYPE:
93174
+ return innerType = type.displayName || null, innerType !== null ? innerType : getComponentNameFromType(type.type) || "Memo";
93175
+ case REACT_LAZY_TYPE:
93176
+ innerType = type._payload;
93177
+ type = type._init;
93178
+ try {
93179
+ return getComponentNameFromType(type(innerType));
93180
+ } catch (x) {}
93181
+ }
93182
+ return null;
93183
+ }
93184
+ function testStringCoercion(value) {
93185
+ return "" + value;
93186
+ }
93187
+ function checkKeyStringCoercion(value) {
93188
+ try {
93189
+ testStringCoercion(value);
93190
+ var JSCompiler_inline_result = false;
93191
+ } catch (e) {
93192
+ JSCompiler_inline_result = true;
93193
+ }
93194
+ if (JSCompiler_inline_result) {
93195
+ JSCompiler_inline_result = console;
93196
+ var JSCompiler_temp_const = JSCompiler_inline_result.error;
93197
+ var JSCompiler_inline_result$jscomp$0 = typeof Symbol === "function" && Symbol.toStringTag && value[Symbol.toStringTag] || value.constructor.name || "Object";
93198
+ JSCompiler_temp_const.call(JSCompiler_inline_result, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", JSCompiler_inline_result$jscomp$0);
93199
+ return testStringCoercion(value);
93200
+ }
93201
+ }
93202
+ function getTaskName(type) {
93203
+ if (type === REACT_FRAGMENT_TYPE)
93204
+ return "<>";
93205
+ if (typeof type === "object" && type !== null && type.$$typeof === REACT_LAZY_TYPE)
93206
+ return "<...>";
93207
+ try {
93208
+ var name = getComponentNameFromType(type);
93209
+ return name ? "<" + name + ">" : "<...>";
93210
+ } catch (x) {
93211
+ return "<...>";
93212
+ }
93213
+ }
93214
+ function getOwner() {
93215
+ var dispatcher = ReactSharedInternals.A;
93216
+ return dispatcher === null ? null : dispatcher.getOwner();
93217
+ }
93218
+ function UnknownOwner() {
93219
+ return Error("react-stack-top-frame");
93220
+ }
93221
+ function hasValidKey(config2) {
93222
+ if (hasOwnProperty.call(config2, "key")) {
93223
+ var getter = Object.getOwnPropertyDescriptor(config2, "key").get;
93224
+ if (getter && getter.isReactWarning)
93225
+ return false;
93226
+ }
93227
+ return config2.key !== undefined;
93228
+ }
93229
+ function defineKeyPropWarningGetter(props, displayName) {
93230
+ function warnAboutAccessingKey() {
93231
+ specialPropKeyWarningShown || (specialPropKeyWarningShown = true, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", displayName));
93232
+ }
93233
+ warnAboutAccessingKey.isReactWarning = true;
93234
+ Object.defineProperty(props, "key", {
93235
+ get: warnAboutAccessingKey,
93236
+ configurable: true
93237
+ });
93238
+ }
93239
+ function elementRefGetterWithDeprecationWarning() {
93240
+ var componentName = getComponentNameFromType(this.type);
93241
+ didWarnAboutElementRef[componentName] || (didWarnAboutElementRef[componentName] = true, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."));
93242
+ componentName = this.props.ref;
93243
+ return componentName !== undefined ? componentName : null;
93244
+ }
93245
+ function ReactElement(type, key, props, owner, debugStack, debugTask) {
93246
+ var refProp = props.ref;
93247
+ type = {
93248
+ $$typeof: REACT_ELEMENT_TYPE,
93249
+ type,
93250
+ key,
93251
+ props,
93252
+ _owner: owner
93253
+ };
93254
+ (refProp !== undefined ? refProp : null) !== null ? Object.defineProperty(type, "ref", {
93255
+ enumerable: false,
93256
+ get: elementRefGetterWithDeprecationWarning
93257
+ }) : Object.defineProperty(type, "ref", { enumerable: false, value: null });
93258
+ type._store = {};
93259
+ Object.defineProperty(type._store, "validated", {
93260
+ configurable: false,
93261
+ enumerable: false,
93262
+ writable: true,
93263
+ value: 0
93264
+ });
93265
+ Object.defineProperty(type, "_debugInfo", {
93266
+ configurable: false,
93267
+ enumerable: false,
93268
+ writable: true,
93269
+ value: null
93270
+ });
93271
+ Object.defineProperty(type, "_debugStack", {
93272
+ configurable: false,
93273
+ enumerable: false,
93274
+ writable: true,
93275
+ value: debugStack
93276
+ });
93277
+ Object.defineProperty(type, "_debugTask", {
93278
+ configurable: false,
93279
+ enumerable: false,
93280
+ writable: true,
93281
+ value: debugTask
93282
+ });
93283
+ Object.freeze && (Object.freeze(type.props), Object.freeze(type));
93284
+ return type;
93285
+ }
93286
+ function jsxDEVImpl(type, config2, maybeKey, isStaticChildren, debugStack, debugTask) {
93287
+ var children = config2.children;
93288
+ if (children !== undefined)
93289
+ if (isStaticChildren)
93290
+ if (isArrayImpl(children)) {
93291
+ for (isStaticChildren = 0;isStaticChildren < children.length; isStaticChildren++)
93292
+ validateChildKeys(children[isStaticChildren]);
93293
+ Object.freeze && Object.freeze(children);
93294
+ } else
93295
+ console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
93296
+ else
93297
+ validateChildKeys(children);
93298
+ if (hasOwnProperty.call(config2, "key")) {
93299
+ children = getComponentNameFromType(type);
93300
+ var keys2 = Object.keys(config2).filter(function(k) {
93301
+ return k !== "key";
93302
+ });
93303
+ isStaticChildren = 0 < keys2.length ? "{key: someKey, " + keys2.join(": ..., ") + ": ...}" : "{key: someKey}";
93304
+ didWarnAboutKeySpread[children + isStaticChildren] || (keys2 = 0 < keys2.length ? "{" + keys2.join(": ..., ") + ": ...}" : "{}", console.error(`A props object containing a "key" prop is being spread into JSX:
93305
+ let props = %s;
93306
+ <%s {...props} />
93307
+ React keys must be passed directly to JSX without using spread:
93308
+ let props = %s;
93309
+ <%s key={someKey} {...props} />`, isStaticChildren, children, keys2, children), didWarnAboutKeySpread[children + isStaticChildren] = true);
93310
+ }
93311
+ children = null;
93312
+ maybeKey !== undefined && (checkKeyStringCoercion(maybeKey), children = "" + maybeKey);
93313
+ hasValidKey(config2) && (checkKeyStringCoercion(config2.key), children = "" + config2.key);
93314
+ if ("key" in config2) {
93315
+ maybeKey = {};
93316
+ for (var propName in config2)
93317
+ propName !== "key" && (maybeKey[propName] = config2[propName]);
93318
+ } else
93319
+ maybeKey = config2;
93320
+ children && defineKeyPropWarningGetter(maybeKey, typeof type === "function" ? type.displayName || type.name || "Unknown" : type);
93321
+ return ReactElement(type, children, maybeKey, getOwner(), debugStack, debugTask);
93322
+ }
93323
+ function validateChildKeys(node) {
93324
+ isValidElement(node) ? node._store && (node._store.validated = 1) : typeof node === "object" && node !== null && node.$$typeof === REACT_LAZY_TYPE && (node._payload.status === "fulfilled" ? isValidElement(node._payload.value) && node._payload.value._store && (node._payload.value._store.validated = 1) : node._store && (node._store.validated = 1));
93325
+ }
93326
+ function isValidElement(object2) {
93327
+ return typeof object2 === "object" && object2 !== null && object2.$$typeof === REACT_ELEMENT_TYPE;
93328
+ }
93329
+ var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = React11.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, hasOwnProperty = Object.prototype.hasOwnProperty, isArrayImpl = Array.isArray, createTask = console.createTask ? console.createTask : function() {
93330
+ return null;
93331
+ };
93332
+ React11 = {
93333
+ react_stack_bottom_frame: function(callStackForError) {
93334
+ return callStackForError();
93335
+ }
93336
+ };
93337
+ var specialPropKeyWarningShown;
93338
+ var didWarnAboutElementRef = {};
93339
+ var unknownOwnerDebugStack = React11.react_stack_bottom_frame.bind(React11, UnknownOwner)();
93340
+ var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
93341
+ var didWarnAboutKeySpread = {};
93342
+ exports.Fragment = REACT_FRAGMENT_TYPE;
93343
+ exports.jsxDEV = function(type, config2, maybeKey, isStaticChildren) {
93344
+ var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
93345
+ return jsxDEVImpl(type, config2, maybeKey, isStaticChildren, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask);
93346
+ };
93347
+ })();
93348
+ });
93349
+
93350
+ // node_modules/react/jsx-dev-runtime.js
93351
+ var require_jsx_dev_runtime = __commonJS((exports, module) => {
93352
+ var react_jsx_dev_runtime_development = __toESM(require_react_jsx_dev_runtime_development());
93353
+ if (false) {} else {
93354
+ module.exports = react_jsx_dev_runtime_development;
93355
+ }
93356
+ });
93357
+
93034
93358
  // node_modules/cli-spinners/spinners.json
93035
93359
  var require_spinners = __commonJS((exports, module) => {
93036
93360
  module.exports = {
@@ -94671,229 +94995,6 @@ var require_cli_spinners = __commonJS((exports, module) => {
94671
94995
  module.exports = spinners;
94672
94996
  });
94673
94997
 
94674
- // node_modules/react/cjs/react-jsx-dev-runtime.development.js
94675
- var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
94676
- var React12 = __toESM(require_react());
94677
- (function() {
94678
- function getComponentNameFromType(type) {
94679
- if (type == null)
94680
- return null;
94681
- if (typeof type === "function")
94682
- return type.$$typeof === REACT_CLIENT_REFERENCE ? null : type.displayName || type.name || null;
94683
- if (typeof type === "string")
94684
- return type;
94685
- switch (type) {
94686
- case REACT_FRAGMENT_TYPE:
94687
- return "Fragment";
94688
- case REACT_PROFILER_TYPE:
94689
- return "Profiler";
94690
- case REACT_STRICT_MODE_TYPE:
94691
- return "StrictMode";
94692
- case REACT_SUSPENSE_TYPE:
94693
- return "Suspense";
94694
- case REACT_SUSPENSE_LIST_TYPE:
94695
- return "SuspenseList";
94696
- case REACT_ACTIVITY_TYPE:
94697
- return "Activity";
94698
- }
94699
- if (typeof type === "object")
94700
- switch (typeof type.tag === "number" && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), type.$$typeof) {
94701
- case REACT_PORTAL_TYPE:
94702
- return "Portal";
94703
- case REACT_CONTEXT_TYPE:
94704
- return type.displayName || "Context";
94705
- case REACT_CONSUMER_TYPE:
94706
- return (type._context.displayName || "Context") + ".Consumer";
94707
- case REACT_FORWARD_REF_TYPE:
94708
- var innerType = type.render;
94709
- type = type.displayName;
94710
- type || (type = innerType.displayName || innerType.name || "", type = type !== "" ? "ForwardRef(" + type + ")" : "ForwardRef");
94711
- return type;
94712
- case REACT_MEMO_TYPE:
94713
- return innerType = type.displayName || null, innerType !== null ? innerType : getComponentNameFromType(type.type) || "Memo";
94714
- case REACT_LAZY_TYPE:
94715
- innerType = type._payload;
94716
- type = type._init;
94717
- try {
94718
- return getComponentNameFromType(type(innerType));
94719
- } catch (x) {}
94720
- }
94721
- return null;
94722
- }
94723
- function testStringCoercion(value) {
94724
- return "" + value;
94725
- }
94726
- function checkKeyStringCoercion(value) {
94727
- try {
94728
- testStringCoercion(value);
94729
- var JSCompiler_inline_result = false;
94730
- } catch (e) {
94731
- JSCompiler_inline_result = true;
94732
- }
94733
- if (JSCompiler_inline_result) {
94734
- JSCompiler_inline_result = console;
94735
- var JSCompiler_temp_const = JSCompiler_inline_result.error;
94736
- var JSCompiler_inline_result$jscomp$0 = typeof Symbol === "function" && Symbol.toStringTag && value[Symbol.toStringTag] || value.constructor.name || "Object";
94737
- JSCompiler_temp_const.call(JSCompiler_inline_result, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", JSCompiler_inline_result$jscomp$0);
94738
- return testStringCoercion(value);
94739
- }
94740
- }
94741
- function getTaskName(type) {
94742
- if (type === REACT_FRAGMENT_TYPE)
94743
- return "<>";
94744
- if (typeof type === "object" && type !== null && type.$$typeof === REACT_LAZY_TYPE)
94745
- return "<...>";
94746
- try {
94747
- var name = getComponentNameFromType(type);
94748
- return name ? "<" + name + ">" : "<...>";
94749
- } catch (x) {
94750
- return "<...>";
94751
- }
94752
- }
94753
- function getOwner() {
94754
- var dispatcher = ReactSharedInternals.A;
94755
- return dispatcher === null ? null : dispatcher.getOwner();
94756
- }
94757
- function UnknownOwner() {
94758
- return Error("react-stack-top-frame");
94759
- }
94760
- function hasValidKey(config2) {
94761
- if (hasOwnProperty.call(config2, "key")) {
94762
- var getter = Object.getOwnPropertyDescriptor(config2, "key").get;
94763
- if (getter && getter.isReactWarning)
94764
- return false;
94765
- }
94766
- return config2.key !== undefined;
94767
- }
94768
- function defineKeyPropWarningGetter(props, displayName) {
94769
- function warnAboutAccessingKey() {
94770
- specialPropKeyWarningShown || (specialPropKeyWarningShown = true, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", displayName));
94771
- }
94772
- warnAboutAccessingKey.isReactWarning = true;
94773
- Object.defineProperty(props, "key", {
94774
- get: warnAboutAccessingKey,
94775
- configurable: true
94776
- });
94777
- }
94778
- function elementRefGetterWithDeprecationWarning() {
94779
- var componentName = getComponentNameFromType(this.type);
94780
- didWarnAboutElementRef[componentName] || (didWarnAboutElementRef[componentName] = true, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."));
94781
- componentName = this.props.ref;
94782
- return componentName !== undefined ? componentName : null;
94783
- }
94784
- function ReactElement(type, key, props, owner, debugStack, debugTask) {
94785
- var refProp = props.ref;
94786
- type = {
94787
- $$typeof: REACT_ELEMENT_TYPE,
94788
- type,
94789
- key,
94790
- props,
94791
- _owner: owner
94792
- };
94793
- (refProp !== undefined ? refProp : null) !== null ? Object.defineProperty(type, "ref", {
94794
- enumerable: false,
94795
- get: elementRefGetterWithDeprecationWarning
94796
- }) : Object.defineProperty(type, "ref", { enumerable: false, value: null });
94797
- type._store = {};
94798
- Object.defineProperty(type._store, "validated", {
94799
- configurable: false,
94800
- enumerable: false,
94801
- writable: true,
94802
- value: 0
94803
- });
94804
- Object.defineProperty(type, "_debugInfo", {
94805
- configurable: false,
94806
- enumerable: false,
94807
- writable: true,
94808
- value: null
94809
- });
94810
- Object.defineProperty(type, "_debugStack", {
94811
- configurable: false,
94812
- enumerable: false,
94813
- writable: true,
94814
- value: debugStack
94815
- });
94816
- Object.defineProperty(type, "_debugTask", {
94817
- configurable: false,
94818
- enumerable: false,
94819
- writable: true,
94820
- value: debugTask
94821
- });
94822
- Object.freeze && (Object.freeze(type.props), Object.freeze(type));
94823
- return type;
94824
- }
94825
- function jsxDEVImpl(type, config2, maybeKey, isStaticChildren, debugStack, debugTask) {
94826
- var children = config2.children;
94827
- if (children !== undefined)
94828
- if (isStaticChildren)
94829
- if (isArrayImpl(children)) {
94830
- for (isStaticChildren = 0;isStaticChildren < children.length; isStaticChildren++)
94831
- validateChildKeys(children[isStaticChildren]);
94832
- Object.freeze && Object.freeze(children);
94833
- } else
94834
- console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
94835
- else
94836
- validateChildKeys(children);
94837
- if (hasOwnProperty.call(config2, "key")) {
94838
- children = getComponentNameFromType(type);
94839
- var keys2 = Object.keys(config2).filter(function(k) {
94840
- return k !== "key";
94841
- });
94842
- isStaticChildren = 0 < keys2.length ? "{key: someKey, " + keys2.join(": ..., ") + ": ...}" : "{key: someKey}";
94843
- didWarnAboutKeySpread[children + isStaticChildren] || (keys2 = 0 < keys2.length ? "{" + keys2.join(": ..., ") + ": ...}" : "{}", console.error(`A props object containing a "key" prop is being spread into JSX:
94844
- let props = %s;
94845
- <%s {...props} />
94846
- React keys must be passed directly to JSX without using spread:
94847
- let props = %s;
94848
- <%s key={someKey} {...props} />`, isStaticChildren, children, keys2, children), didWarnAboutKeySpread[children + isStaticChildren] = true);
94849
- }
94850
- children = null;
94851
- maybeKey !== undefined && (checkKeyStringCoercion(maybeKey), children = "" + maybeKey);
94852
- hasValidKey(config2) && (checkKeyStringCoercion(config2.key), children = "" + config2.key);
94853
- if ("key" in config2) {
94854
- maybeKey = {};
94855
- for (var propName in config2)
94856
- propName !== "key" && (maybeKey[propName] = config2[propName]);
94857
- } else
94858
- maybeKey = config2;
94859
- children && defineKeyPropWarningGetter(maybeKey, typeof type === "function" ? type.displayName || type.name || "Unknown" : type);
94860
- return ReactElement(type, children, maybeKey, getOwner(), debugStack, debugTask);
94861
- }
94862
- function validateChildKeys(node) {
94863
- isValidElement(node) ? node._store && (node._store.validated = 1) : typeof node === "object" && node !== null && node.$$typeof === REACT_LAZY_TYPE && (node._payload.status === "fulfilled" ? isValidElement(node._payload.value) && node._payload.value._store && (node._payload.value._store.validated = 1) : node._store && (node._store.validated = 1));
94864
- }
94865
- function isValidElement(object2) {
94866
- return typeof object2 === "object" && object2 !== null && object2.$$typeof === REACT_ELEMENT_TYPE;
94867
- }
94868
- var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = React12.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, hasOwnProperty = Object.prototype.hasOwnProperty, isArrayImpl = Array.isArray, createTask = console.createTask ? console.createTask : function() {
94869
- return null;
94870
- };
94871
- React12 = {
94872
- react_stack_bottom_frame: function(callStackForError) {
94873
- return callStackForError();
94874
- }
94875
- };
94876
- var specialPropKeyWarningShown;
94877
- var didWarnAboutElementRef = {};
94878
- var unknownOwnerDebugStack = React12.react_stack_bottom_frame.bind(React12, UnknownOwner)();
94879
- var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
94880
- var didWarnAboutKeySpread = {};
94881
- exports.Fragment = REACT_FRAGMENT_TYPE;
94882
- exports.jsxDEV = function(type, config2, maybeKey, isStaticChildren) {
94883
- var trackActualOwner = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
94884
- return jsxDEVImpl(type, config2, maybeKey, isStaticChildren, trackActualOwner ? Error("react-stack-top-frame") : unknownOwnerDebugStack, trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask);
94885
- };
94886
- })();
94887
- });
94888
-
94889
- // node_modules/react/jsx-dev-runtime.js
94890
- var require_jsx_dev_runtime = __commonJS((exports, module) => {
94891
- var react_jsx_dev_runtime_development = __toESM(require_react_jsx_dev_runtime_development());
94892
- if (false) {} else {
94893
- module.exports = react_jsx_dev_runtime_development;
94894
- }
94895
- });
94896
-
94897
94998
  // src/commands/curator.ts
94898
94999
  var exports_curator = {};
94899
95000
  __export(exports_curator, {
@@ -94903,15 +95004,15 @@ __export(exports_curator, {
94903
95004
  curatorCommit: () => curatorCommit,
94904
95005
  _curatorCmdDeps: () => _curatorCmdDeps
94905
95006
  });
94906
- import { readdirSync as readdirSync9 } from "fs";
95007
+ import { readdirSync as readdirSync8 } from "fs";
94907
95008
  import { unlink as unlink4 } from "fs/promises";
94908
- import { basename as basename16, join as join83 } from "path";
95009
+ import { basename as basename15, join as join82 } from "path";
94909
95010
  function getProjectKey(config2, projectDir) {
94910
- return config2.name?.trim() || basename16(projectDir);
95011
+ return config2.name?.trim() || basename15(projectDir);
94911
95012
  }
94912
95013
  function listRunIds(runsDir) {
94913
95014
  try {
94914
- return readdirSync9(runsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
95015
+ return readdirSync8(runsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
94915
95016
  } catch {
94916
95017
  return [];
94917
95018
  }
@@ -94988,7 +95089,7 @@ async function curatorStatus(options) {
94988
95089
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
94989
95090
  const projectKey = getProjectKey(config2, resolved.projectDir);
94990
95091
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
94991
- const runsDir = join83(outputDir, "runs");
95092
+ const runsDir = join82(outputDir, "runs");
94992
95093
  const runIds = listRunIds(runsDir);
94993
95094
  let runId;
94994
95095
  if (options.run) {
@@ -95005,8 +95106,8 @@ async function curatorStatus(options) {
95005
95106
  runId = runIds[runIds.length - 1];
95006
95107
  }
95007
95108
  console.log(`Run: ${runId}`);
95008
- const runDir = join83(runsDir, runId);
95009
- const observationsPath = join83(runDir, "observations.jsonl");
95109
+ const runDir = join82(runsDir, runId);
95110
+ const observationsPath = join82(runDir, "observations.jsonl");
95010
95111
  const observations = await parseObservations(observationsPath);
95011
95112
  const counts = new Map;
95012
95113
  for (const obs of observations) {
@@ -95016,7 +95117,7 @@ async function curatorStatus(options) {
95016
95117
  for (const [kind, count] of counts.entries()) {
95017
95118
  console.log(` ${kind}: ${count}`);
95018
95119
  }
95019
- const proposalsPath = join83(runDir, "curator-proposals.md");
95120
+ const proposalsPath = join82(runDir, "curator-proposals.md");
95020
95121
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
95021
95122
  if (proposalText !== null) {
95022
95123
  console.log("");
@@ -95030,8 +95131,8 @@ async function curatorCommit(options) {
95030
95131
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95031
95132
  const projectKey = getProjectKey(config2, resolved.projectDir);
95032
95133
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95033
- const runDir = join83(outputDir, "runs", options.runId);
95034
- const proposalsPath = join83(runDir, "curator-proposals.md");
95134
+ const runDir = join82(outputDir, "runs", options.runId);
95135
+ const proposalsPath = join82(runDir, "curator-proposals.md");
95035
95136
  const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
95036
95137
  if (proposalText === null) {
95037
95138
  console.log(`curator-proposals.md not found for run ${options.runId}.`);
@@ -95047,7 +95148,7 @@ async function curatorCommit(options) {
95047
95148
  const dropFileState = new Map;
95048
95149
  const skippedDrops = new Set;
95049
95150
  for (const drop2 of drops) {
95050
- const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
95151
+ const targetPath = join82(resolved.projectDir, drop2.canonicalFile);
95051
95152
  if (!dropFileState.has(targetPath)) {
95052
95153
  const fileExists2 = await Bun.file(targetPath).exists();
95053
95154
  const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
@@ -95081,7 +95182,7 @@ async function curatorCommit(options) {
95081
95182
  if (skippedDrops.has(drop2)) {
95082
95183
  continue;
95083
95184
  }
95084
- const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
95185
+ const targetPath = join82(resolved.projectDir, drop2.canonicalFile);
95085
95186
  const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
95086
95187
  const filtered = filterDropContent(existing, drop2.description);
95087
95188
  await _curatorCmdDeps.writeFile(targetPath, filtered);
@@ -95090,7 +95191,7 @@ async function curatorCommit(options) {
95090
95191
  }
95091
95192
  const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
95092
95193
  for (const add2 of adds) {
95093
- const targetPath = join83(resolved.projectDir, add2.canonicalFile);
95194
+ const targetPath = join82(resolved.projectDir, add2.canonicalFile);
95094
95195
  const content = buildAddContent(add2);
95095
95196
  await _curatorCmdDeps.appendFile(targetPath, content);
95096
95197
  modifiedFiles.add(targetPath);
@@ -95127,7 +95228,7 @@ async function curatorDryrun(options) {
95127
95228
  const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
95128
95229
  const projectKey = getProjectKey(config2, resolved.projectDir);
95129
95230
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95130
- const runsDir = join83(outputDir, "runs");
95231
+ const runsDir = join82(outputDir, "runs");
95131
95232
  const runIds = listRunIds(runsDir);
95132
95233
  if (runIds.length === 0) {
95133
95234
  console.log("No runs found.");
@@ -95138,7 +95239,7 @@ async function curatorDryrun(options) {
95138
95239
  console.log(`Run ${options.run} not found in ${runsDir}.`);
95139
95240
  return;
95140
95241
  }
95141
- const observationsPath = join83(runsDir, runId, "observations.jsonl");
95242
+ const observationsPath = join82(runsDir, runId, "observations.jsonl");
95142
95243
  const observations = await parseObservations(observationsPath);
95143
95244
  const thresholds = getThresholds(config2);
95144
95245
  const proposals = runHeuristics(observations, thresholds);
@@ -95179,12 +95280,12 @@ async function curatorGc(options) {
95179
95280
  await _curatorCmdDeps.writeFile(rollupPath, newContent);
95180
95281
  const projectKey = getProjectKey(config2, resolved.projectDir);
95181
95282
  const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
95182
- const perRunsDir = join83(outputDir, "runs");
95283
+ const perRunsDir = join82(outputDir, "runs");
95183
95284
  for (const runId of uniqueRunIds) {
95184
95285
  if (!keepSet.has(runId)) {
95185
- const runDir = join83(perRunsDir, runId);
95186
- await _curatorCmdDeps.removeFile(join83(runDir, "observations.jsonl"));
95187
- await _curatorCmdDeps.removeFile(join83(runDir, "curator-proposals.md"));
95286
+ const runDir = join82(perRunsDir, runId);
95287
+ await _curatorCmdDeps.removeFile(join82(runDir, "observations.jsonl"));
95288
+ await _curatorCmdDeps.removeFile(join82(runDir, "curator-proposals.md"));
95188
95289
  }
95189
95290
  }
95190
95291
  console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
@@ -95227,9 +95328,9 @@ var init_curator2 = __esm(() => {
95227
95328
 
95228
95329
  // bin/nax.ts
95229
95330
  init_source();
95230
- import { existsSync as existsSync37, mkdirSync as mkdirSync7 } from "fs";
95331
+ import { existsSync as existsSync36, mkdirSync as mkdirSync7 } from "fs";
95231
95332
  import { homedir as homedir3 } from "os";
95232
- import { basename as basename17, join as join84 } from "path";
95333
+ import { basename as basename16, join as join83 } from "path";
95233
95334
 
95234
95335
  // node_modules/commander/esm.mjs
95235
95336
  var import__ = __toESM(require_commander(), 1);
@@ -96385,324 +96486,6 @@ function formatSource(type, sourcePath) {
96385
96486
  function pad(str, width) {
96386
96487
  return str.padEnd(width);
96387
96488
  }
96388
- // src/cli/diagnose.ts
96389
- init_config();
96390
- init_logger2();
96391
- init_prd();
96392
- init_runtime();
96393
- import { existsSync as existsSync25, readdirSync as readdirSync5 } from "fs";
96394
- import { basename as basename12, join as join57 } from "path";
96395
-
96396
- // src/cli/diagnose-analysis.ts
96397
- function detectFailurePattern(story, _prd, status) {
96398
- if (story.status === "passed" && story.priorErrors?.some((err) => err.toLowerCase().includes("greenfield-no-tests"))) {
96399
- return "AUTO_RECOVERED";
96400
- }
96401
- if (story.status !== "failed" && story.status !== "blocked" && story.status !== "paused") {
96402
- return "UNKNOWN";
96403
- }
96404
- if (story.failureCategory === "greenfield-no-tests" || story.priorErrors?.some((err) => err.toLowerCase().includes("greenfield-no-tests"))) {
96405
- return "GREENFIELD_TDD";
96406
- }
96407
- const testFailingCount = story.priorErrors?.filter((err) => err.toLowerCase().includes("tests-failing")).length || 0;
96408
- if (testFailingCount >= 2)
96409
- return "TEST_MISMATCH";
96410
- if (story.priorErrors?.some((err) => err.toLowerCase().includes("precheck-failed")) || (status?.progress.blocked ?? 0) > 0) {
96411
- return "ENVIRONMENTAL";
96412
- }
96413
- if (story.priorErrors?.some((err) => err.toLowerCase().includes("ratelimited")))
96414
- return "RATE_LIMITED";
96415
- if (story.failureCategory === "isolation-violation")
96416
- return "ISOLATION_VIOLATION";
96417
- if (status?.run.status === "stalled")
96418
- return "STALLED";
96419
- if (story.priorErrors?.some((err) => err.toLowerCase().includes("session-failure")))
96420
- return "SESSION_CRASH";
96421
- if (story.attempts > 3)
96422
- return "MAX_TIERS_EXHAUSTED";
96423
- return "UNKNOWN";
96424
- }
96425
- function getPatternSymptom(pattern) {
96426
- const symptoms = {
96427
- GREENFIELD_TDD: "Story attempted in greenfield project with no existing tests",
96428
- TEST_MISMATCH: "Multiple test failures across attempts",
96429
- ENVIRONMENTAL: "Environment prechecks failed or blockers detected",
96430
- RATE_LIMITED: "API rate limit exceeded",
96431
- ISOLATION_VIOLATION: "Story modified files outside its scope",
96432
- MAX_TIERS_EXHAUSTED: "Story attempted at all configured model tiers without success",
96433
- SESSION_CRASH: "Agent session crashed without producing commits",
96434
- STALLED: "All stories blocked or paused -- no forward progress possible",
96435
- LOCK_STALE: "Lock file present but process is dead",
96436
- AUTO_RECOVERED: "Greenfield issue detected but S5 auto-recovery succeeded",
96437
- UNKNOWN: "Unknown failure pattern"
96438
- };
96439
- return symptoms[pattern] ?? "Unknown failure pattern";
96440
- }
96441
- function getPatternFixSuggestion(pattern, _story) {
96442
- const fixes = {
96443
- GREENFIELD_TDD: "Add --greenfield flag or bootstrap with scaffolding tests first",
96444
- TEST_MISMATCH: "Review acceptance criteria; tests may be too strict or story underspecified",
96445
- ENVIRONMENTAL: "Fix precheck issues (deps, env, build) before re-running",
96446
- RATE_LIMITED: "Wait for rate limit to reset or increase tier limits",
96447
- ISOLATION_VIOLATION: "Narrow story scope or adjust expectedFiles to allow cross-file changes",
96448
- MAX_TIERS_EXHAUSTED: "Simplify story or split into smaller sub-stories",
96449
- SESSION_CRASH: "Check agent logs for crash details; may need manual intervention",
96450
- STALLED: "Resolve blocked stories or skip them to unblock dependencies",
96451
- LOCK_STALE: "Run: rm nax.lock",
96452
- AUTO_RECOVERED: "No action needed -- S5 successfully handled greenfield scenario",
96453
- UNKNOWN: "Review logs and prior errors for clues"
96454
- };
96455
- return fixes[pattern] ?? "Review logs and prior errors for clues";
96456
- }
96457
- function generateRecommendations(report) {
96458
- const recommendations = [];
96459
- if (report.lockCheck.lockPresent && report.lockCheck.pidAlive === false) {
96460
- recommendations.push(`Remove stale lock: ${report.lockCheck.fixCommand}`);
96461
- }
96462
- const criticalPatterns = report.failureAnalysis.filter((f) => ["ENVIRONMENTAL", "STALLED", "SESSION_CRASH"].includes(f.pattern));
96463
- if (criticalPatterns.length > 0) {
96464
- recommendations.push(`Fix ${criticalPatterns.length} critical blocker(s): ${criticalPatterns.map((f) => f.storyId).join(", ")}`);
96465
- }
96466
- if (report.failureAnalysis.some((f) => f.pattern === "RATE_LIMITED")) {
96467
- recommendations.push("Wait for rate limits to reset before re-running");
96468
- }
96469
- if (report.failureAnalysis.some((f) => f.pattern === "GREENFIELD_TDD")) {
96470
- recommendations.push("Consider adding --greenfield flag or bootstrap tests for greenfield stories");
96471
- }
96472
- if (report.runSummary.storiesFailed > 0 && recommendations.length === 0) {
96473
- recommendations.push(`Re-run with: nax run -f ${report.runSummary.feature}`);
96474
- }
96475
- if (report.runSummary.storiesFailed === 0 && report.runSummary.storiesPending === 0) {
96476
- recommendations.push("All stories passed -- feature is complete!");
96477
- }
96478
- return recommendations;
96479
- }
96480
- function diagnoseStories(prd, status) {
96481
- const storyBreakdown = [];
96482
- const failureAnalysis = [];
96483
- for (const story of prd.userStories) {
96484
- const pattern = detectFailurePattern(story, prd, status);
96485
- const diagnosis = {
96486
- storyId: story.id,
96487
- title: story.title,
96488
- status: story.status,
96489
- attempts: story.attempts,
96490
- tier: story.routing?.modelTier,
96491
- strategy: story.routing?.testStrategy,
96492
- pattern
96493
- };
96494
- storyBreakdown.push(diagnosis);
96495
- if (story.status === "failed" || story.status === "blocked" || story.status === "paused" || pattern === "AUTO_RECOVERED") {
96496
- diagnosis.symptom = getPatternSymptom(pattern);
96497
- diagnosis.fixSuggestion = getPatternFixSuggestion(pattern, story);
96498
- failureAnalysis.push(diagnosis);
96499
- }
96500
- }
96501
- return { storyBreakdown, failureAnalysis };
96502
- }
96503
-
96504
- // src/cli/diagnose-formatter.ts
96505
- init_source();
96506
- function formatReport(report, verbose) {
96507
- const lines = [];
96508
- lines.push(source_default.bold(`
96509
- Diagnosis Report: ${report.runSummary.feature}
96510
- `));
96511
- if (!report.dataSources.eventsFound) {
96512
- lines.push(source_default.yellow(`[WARN] events.jsonl not found -- diagnosis limited to PRD + git log
96513
- `));
96514
- }
96515
- lines.push(source_default.bold(`Run Summary
96516
- `));
96517
- if (report.runSummary.lastRunTime) {
96518
- lines.push(source_default.dim(` Last Run: ${report.runSummary.lastRunTime}`));
96519
- }
96520
- lines.push(source_default.dim(` Status: ${report.runSummary.status}`));
96521
- lines.push(source_default.green(` Passed: ${report.runSummary.storiesPassed}`));
96522
- lines.push(source_default.red(` Failed: ${report.runSummary.storiesFailed}`));
96523
- lines.push(source_default.dim(` Pending: ${report.runSummary.storiesPending}`));
96524
- if (report.runSummary.cost !== undefined) {
96525
- lines.push(source_default.dim(` Cost: $${report.runSummary.cost.toFixed(4)}`));
96526
- }
96527
- lines.push(source_default.dim(` Commits: ${report.runSummary.commitsProduced}`));
96528
- lines.push("");
96529
- if (verbose && report.storyBreakdown.length > 0) {
96530
- lines.push(source_default.bold(`Story Breakdown
96531
- `));
96532
- for (const story of report.storyBreakdown) {
96533
- const icon = story.status === "passed" ? "[OK]" : story.status === "failed" ? "[FAIL]" : "[ ]";
96534
- const pattern = story.pattern !== "UNKNOWN" ? source_default.yellow(` [${story.pattern}]`) : "";
96535
- lines.push(` ${icon} ${story.storyId}: ${story.title}${pattern}`);
96536
- if (verbose && story.tier) {
96537
- lines.push(source_default.dim(` Tier: ${story.tier}, Strategy: ${story.strategy}, Attempts: ${story.attempts}`));
96538
- }
96539
- }
96540
- lines.push("");
96541
- }
96542
- if (report.failureAnalysis.length > 0) {
96543
- lines.push(source_default.bold(`Failure Analysis
96544
- `));
96545
- for (const failure of report.failureAnalysis) {
96546
- const level = failure.pattern === "AUTO_RECOVERED" ? source_default.green("INFO") : source_default.red("ERROR");
96547
- lines.push(` ${level} ${failure.storyId}: ${failure.title}`);
96548
- lines.push(source_default.dim(` Pattern: ${failure.pattern}`));
96549
- if (failure.symptom) {
96550
- lines.push(source_default.dim(` Symptom: ${failure.symptom}`));
96551
- }
96552
- if (failure.fixSuggestion) {
96553
- lines.push(source_default.yellow(` Fix: ${failure.fixSuggestion}`));
96554
- }
96555
- lines.push("");
96556
- }
96557
- }
96558
- lines.push(source_default.bold(`Lock Check
96559
- `));
96560
- if (!report.lockCheck.lockPresent) {
96561
- lines.push(source_default.dim(` No lock file present
96562
- `));
96563
- } else if (report.lockCheck.pidAlive === false) {
96564
- lines.push(source_default.red(` [FAIL] Stale lock detected (PID ${report.lockCheck.pid} is dead)`));
96565
- lines.push(source_default.yellow(` Fix: ${report.lockCheck.fixCommand}
96566
- `));
96567
- } else {
96568
- lines.push(source_default.green(` [OK] Active lock (PID ${report.lockCheck.pid})
96569
- `));
96570
- }
96571
- if (report.recommendations.length > 0) {
96572
- lines.push(source_default.bold(`Recommendations
96573
- `));
96574
- for (let i = 0;i < report.recommendations.length; i++) {
96575
- lines.push(` ${i + 1}. ${report.recommendations[i]}`);
96576
- }
96577
- lines.push("");
96578
- }
96579
- return lines.join(`
96580
- `);
96581
- }
96582
-
96583
- // src/cli/diagnose.ts
96584
- var _diagnoseDeps = {
96585
- projectOutputDir,
96586
- loadConfig
96587
- };
96588
- function isProcessAlive2(pid) {
96589
- try {
96590
- const result = Bun.spawnSync(["ps", "-p", String(pid)], { stdout: "ignore", stderr: "ignore" });
96591
- return result.exitCode === 0;
96592
- } catch {
96593
- return false;
96594
- }
96595
- }
96596
- async function loadStatusFile2(outputDir) {
96597
- const statusPath = join57(outputDir, "status.json");
96598
- if (!existsSync25(statusPath))
96599
- return null;
96600
- try {
96601
- return await Bun.file(statusPath).json();
96602
- } catch {
96603
- return null;
96604
- }
96605
- }
96606
- async function countCommitsSince(workdir, since) {
96607
- if (!since)
96608
- return 0;
96609
- try {
96610
- const result = Bun.spawnSync(["git", "log", "--oneline", `--since=${since}`, "--all"], {
96611
- cwd: workdir,
96612
- stdout: "pipe",
96613
- stderr: "ignore"
96614
- });
96615
- if (result.exitCode !== 0)
96616
- return 0;
96617
- const output = new TextDecoder().decode(result.stdout);
96618
- return output.trim().split(`
96619
- `).filter((line) => line.length > 0).length;
96620
- } catch {
96621
- return 0;
96622
- }
96623
- }
96624
- async function checkLock(workdir) {
96625
- const lockFile = Bun.file(join57(workdir, "nax.lock"));
96626
- if (!await lockFile.exists())
96627
- return { lockPresent: false };
96628
- try {
96629
- const lockData = JSON.parse(await lockFile.text());
96630
- const pid = lockData.pid;
96631
- const pidAlive = isProcessAlive2(pid);
96632
- if (!pidAlive)
96633
- return { lockPresent: true, pidAlive: false, pid, fixCommand: "rm nax.lock" };
96634
- return { lockPresent: true, pidAlive: true, pid };
96635
- } catch {
96636
- return { lockPresent: true };
96637
- }
96638
- }
96639
- async function diagnoseCommand(options = {}) {
96640
- const logger = getLogger();
96641
- const workdir = options.workdir ?? process.cwd();
96642
- const naxSubdir = findProjectDir(workdir);
96643
- let projectDir = naxSubdir ? join57(naxSubdir, "..") : null;
96644
- if (!projectDir && existsSync25(join57(workdir, ".nax"))) {
96645
- projectDir = workdir;
96646
- }
96647
- if (!projectDir)
96648
- throw new Error("Not in a nax project directory");
96649
- const config2 = await _diagnoseDeps.loadConfig(projectDir).catch(() => null);
96650
- const projectKey = config2?.name?.trim() || basename12(projectDir);
96651
- const outputDir = _diagnoseDeps.projectOutputDir(projectKey, config2?.outputDir);
96652
- let feature = options.feature;
96653
- if (!feature) {
96654
- const status2 = await loadStatusFile2(outputDir);
96655
- if (status2) {
96656
- feature = status2.run.feature;
96657
- } else {
96658
- const featuresDir = join57(outputDir, "features");
96659
- if (!existsSync25(featuresDir))
96660
- throw new Error("No features found in project");
96661
- const features = readdirSync5(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
96662
- if (features.length === 0)
96663
- throw new Error("No features found");
96664
- feature = features[0];
96665
- logger.info("diagnose", "No feature specified, using first found", { feature });
96666
- }
96667
- }
96668
- const featureDir = join57(outputDir, "features", feature);
96669
- const prdPath = join57(featureDir, "prd.json");
96670
- if (!existsSync25(prdPath))
96671
- throw new Error(`Feature not found: ${feature}`);
96672
- const prd = await loadPRD(prdPath);
96673
- const status = await loadStatusFile2(outputDir);
96674
- const lockCheck = await checkLock(projectDir);
96675
- const commitCount = await countCommitsSince(projectDir, status?.run.startedAt);
96676
- const { storyBreakdown, failureAnalysis } = diagnoseStories(prd, status);
96677
- const report = {
96678
- runSummary: {
96679
- feature,
96680
- lastRunTime: status?.run.startedAt,
96681
- status: status?.run.status ?? "unknown",
96682
- storiesPassed: prd.userStories.filter((s) => s.status === "passed").length,
96683
- storiesFailed: prd.userStories.filter((s) => s.status === "failed").length,
96684
- storiesPending: prd.userStories.filter((s) => s.status !== "passed" && s.status !== "failed" && s.status !== "skipped").length,
96685
- cost: status?.cost.spent,
96686
- commitsProduced: commitCount
96687
- },
96688
- storyBreakdown,
96689
- failureAnalysis,
96690
- lockCheck,
96691
- recommendations: [],
96692
- dataSources: {
96693
- prdFound: true,
96694
- statusFound: status !== null,
96695
- eventsFound: false,
96696
- gitLogFound: commitCount > 0
96697
- }
96698
- };
96699
- report.recommendations = generateRecommendations(report);
96700
- if (options.json) {
96701
- console.log(JSON.stringify(report, null, 2));
96702
- } else {
96703
- console.log(formatReport(report, options.verbose ?? false));
96704
- }
96705
- }
96706
96489
  // src/cli/interact.ts
96707
96490
  init_common();
96708
96491
  init_interaction();
@@ -96710,8 +96493,8 @@ init_interaction();
96710
96493
  init_source();
96711
96494
  init_loader();
96712
96495
  init_generator2();
96713
- import { existsSync as existsSync26 } from "fs";
96714
- import { join as join58 } from "path";
96496
+ import { existsSync as existsSync25 } from "fs";
96497
+ import { join as join57 } from "path";
96715
96498
  var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
96716
96499
  async function generateCommand(options) {
96717
96500
  const workdir = options.dir ?? process.cwd();
@@ -96754,7 +96537,7 @@ async function generateCommand(options) {
96754
96537
  return;
96755
96538
  }
96756
96539
  if (options.package) {
96757
- const packageDir = join58(workdir, options.package);
96540
+ const packageDir = join57(workdir, options.package);
96758
96541
  if (dryRun) {
96759
96542
  console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
96760
96543
  }
@@ -96774,10 +96557,10 @@ async function generateCommand(options) {
96774
96557
  process.exit(1);
96775
96558
  return;
96776
96559
  }
96777
- const contextPath = options.context ? join58(workdir, options.context) : join58(workdir, ".nax/context.md");
96778
- const outputDir = options.output ? join58(workdir, options.output) : workdir;
96560
+ const contextPath = options.context ? join57(workdir, options.context) : join57(workdir, ".nax/context.md");
96561
+ const outputDir = options.output ? join57(workdir, options.output) : workdir;
96779
96562
  const autoInject = !options.noAutoInject;
96780
- if (!existsSync26(contextPath)) {
96563
+ if (!existsSync25(contextPath)) {
96781
96564
  console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
96782
96565
  console.error(source_default.yellow(" Create .nax/context.md first, or run `nax init` to scaffold it."));
96783
96566
  process.exit(1);
@@ -96880,8 +96663,8 @@ async function generateCommand(options) {
96880
96663
  }
96881
96664
  // src/cli/config-display.ts
96882
96665
  init_loader();
96883
- import { existsSync as existsSync28 } from "fs";
96884
- import { join as join60 } from "path";
96666
+ import { existsSync as existsSync27 } from "fs";
96667
+ import { join as join59 } from "path";
96885
96668
 
96886
96669
  // src/cli/config-descriptions.ts
96887
96670
  var FIELD_DESCRIPTIONS = {
@@ -96937,6 +96720,7 @@ var FIELD_DESCRIPTIONS = {
96937
96720
  "execution.rectification.fullSuiteTimeoutSeconds": "Timeout for full test suite run in seconds",
96938
96721
  "execution.rectification.maxFailureSummaryChars": "Max characters in failure summary",
96939
96722
  "execution.rectification.abortOnIncreasingFailures": "Abort if failure count increases",
96723
+ "execution.rectification.consecutiveIncreasesToBail": "Consecutive regressing iterations required before abortOnIncreasingFailures bails (default: 2; 1 = legacy behaviour)",
96940
96724
  "execution.rectification.escalateOnExhaustion": "Enable model tier escalation when attempts are exhausted with remaining failures",
96941
96725
  "execution.rectification.rethinkAtAttempt": "Attempt number at which 'rethink your approach' language is injected into the prompt (default: 2)",
96942
96726
  "execution.rectification.urgencyAtAttempt": "Attempt number at which 'final chance before escalation' urgency is added (default: 3)",
@@ -97131,10 +96915,10 @@ function deepEqual(a, b) {
97131
96915
  // src/cli/config-get.ts
97132
96916
  init_defaults();
97133
96917
  init_loader();
97134
- import { existsSync as existsSync27 } from "fs";
97135
- import { join as join59 } from "path";
96918
+ import { existsSync as existsSync26 } from "fs";
96919
+ import { join as join58 } from "path";
97136
96920
  async function loadConfigFile(path18) {
97137
- if (!existsSync27(path18))
96921
+ if (!existsSync26(path18))
97138
96922
  return null;
97139
96923
  try {
97140
96924
  return await Bun.file(path18).json();
@@ -97154,7 +96938,7 @@ async function loadProjectConfig() {
97154
96938
  const projectDir = findProjectDir();
97155
96939
  if (!projectDir)
97156
96940
  return null;
97157
- const projectPath = join59(projectDir, "config.json");
96941
+ const projectPath = join58(projectDir, "config.json");
97158
96942
  return await loadConfigFile(projectPath);
97159
96943
  }
97160
96944
 
@@ -97214,14 +96998,14 @@ async function configCommand(config2, options = {}) {
97214
96998
  function determineConfigSources() {
97215
96999
  const globalPath = globalConfigPath();
97216
97000
  const projectDir = findProjectDir();
97217
- const projectPath = projectDir ? join60(projectDir, "config.json") : null;
97001
+ const projectPath = projectDir ? join59(projectDir, "config.json") : null;
97218
97002
  return {
97219
97003
  global: fileExists(globalPath) ? globalPath : null,
97220
97004
  project: projectPath && fileExists(projectPath) ? projectPath : null
97221
97005
  };
97222
97006
  }
97223
97007
  function fileExists(path18) {
97224
- return existsSync28(path18);
97008
+ return existsSync27(path18);
97225
97009
  }
97226
97010
  function displayConfigWithDescriptions(obj, path18, sources, indent = 0) {
97227
97011
  const indentStr = " ".repeat(indent);
@@ -97362,16 +97146,16 @@ function formatValueForTable(value) {
97362
97146
  init_paths();
97363
97147
  init_profile();
97364
97148
  import { mkdirSync as mkdirSync5 } from "fs";
97365
- import { readdirSync as readdirSync6 } from "fs";
97366
- import { join as join61 } from "path";
97149
+ import { readdirSync as readdirSync5 } from "fs";
97150
+ import { join as join60 } from "path";
97367
97151
  var _profileCLIDeps = {
97368
97152
  env: process.env
97369
97153
  };
97370
97154
  var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
97371
97155
  var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
97372
97156
  async function profileListCommand(startDir) {
97373
- const globalProfilesDir = join61(globalConfigDir(), "profiles");
97374
- const projectProfilesDir = join61(projectConfigDir(startDir), "profiles");
97157
+ const globalProfilesDir = join60(globalConfigDir(), "profiles");
97158
+ const projectProfilesDir = join60(projectConfigDir(startDir), "profiles");
97375
97159
  const globalProfiles = scanProfileDir(globalProfilesDir);
97376
97160
  const projectProfiles = scanProfileDir(projectProfilesDir);
97377
97161
  const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
@@ -97397,7 +97181,7 @@ async function profileListCommand(startDir) {
97397
97181
  }
97398
97182
  function scanProfileDir(dir) {
97399
97183
  try {
97400
- return readdirSync6(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
97184
+ return readdirSync5(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
97401
97185
  } catch {
97402
97186
  return [];
97403
97187
  }
@@ -97430,7 +97214,7 @@ function maskProfileValues(obj) {
97430
97214
  return result;
97431
97215
  }
97432
97216
  async function profileUseCommand(profileName, startDir) {
97433
- const configPath = join61(projectConfigDir(startDir), "config.json");
97217
+ const configPath = join60(projectConfigDir(startDir), "config.json");
97434
97218
  const configFile = Bun.file(configPath);
97435
97219
  let existing = {};
97436
97220
  if (await configFile.exists()) {
@@ -97449,8 +97233,8 @@ async function profileCurrentCommand(startDir) {
97449
97233
  return resolveProfileName({}, _profileCLIDeps.env, startDir);
97450
97234
  }
97451
97235
  async function profileCreateCommand(profileName, startDir) {
97452
- const profilesDir = join61(projectConfigDir(startDir), "profiles");
97453
- const profilePath = join61(profilesDir, `${profileName}.json`);
97236
+ const profilesDir = join60(projectConfigDir(startDir), "profiles");
97237
+ const profilePath = join60(profilesDir, `${profileName}.json`);
97454
97238
  const profileFile = Bun.file(profilePath);
97455
97239
  if (await profileFile.exists()) {
97456
97240
  throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
@@ -97572,7 +97356,7 @@ async function contextInspectCommand(options) {
97572
97356
  init_canonical_loader();
97573
97357
  init_errors();
97574
97358
  import { mkdir as mkdir12 } from "fs/promises";
97575
- import { basename as basename13, join as join62 } from "path";
97359
+ import { basename as basename12, join as join61 } from "path";
97576
97360
  var _rulesCLIDeps = {
97577
97361
  readFile: async (path18) => Bun.file(path18).text(),
97578
97362
  writeFile: async (path18, content) => {
@@ -97581,7 +97365,7 @@ var _rulesCLIDeps = {
97581
97365
  fileExists: async (path18) => Bun.file(path18).exists(),
97582
97366
  globInDir: (dir) => {
97583
97367
  try {
97584
- return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join62(dir, f));
97368
+ return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join61(dir, f));
97585
97369
  } catch {
97586
97370
  return [];
97587
97371
  }
@@ -97630,7 +97414,7 @@ ${r.content}`).join(`
97630
97414
  `);
97631
97415
  const shimContent = `${header + body}
97632
97416
  `;
97633
- const shimPath = join62(workdir, shimFileName);
97417
+ const shimPath = join61(workdir, shimFileName);
97634
97418
  if (options.dryRun) {
97635
97419
  console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
97636
97420
  return;
@@ -97659,20 +97443,20 @@ function neutralizeContent(content) {
97659
97443
  }
97660
97444
  async function collectMigrationSources(workdir) {
97661
97445
  const sources = [];
97662
- const claudeMdPath = join62(workdir, "CLAUDE.md");
97446
+ const claudeMdPath = join61(workdir, "CLAUDE.md");
97663
97447
  if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
97664
97448
  const content = await _rulesCLIDeps.readFile(claudeMdPath);
97665
97449
  if (content.trim()) {
97666
97450
  sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
97667
97451
  }
97668
97452
  }
97669
- const rulesDir = join62(workdir, ".claude", "rules");
97453
+ const rulesDir = join61(workdir, ".claude", "rules");
97670
97454
  const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
97671
97455
  for (const filePath of ruleFiles) {
97672
97456
  try {
97673
97457
  const content = await _rulesCLIDeps.readFile(filePath);
97674
97458
  if (content.trim()) {
97675
- sources.push({ sourcePath: filePath, targetFileName: basename13(filePath), content });
97459
+ sources.push({ sourcePath: filePath, targetFileName: basename12(filePath), content });
97676
97460
  }
97677
97461
  } catch {}
97678
97462
  }
@@ -97686,7 +97470,7 @@ async function rulesMigrateCommand(options) {
97686
97470
  console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
97687
97471
  return;
97688
97472
  }
97689
- const targetDir = join62(workdir, CANONICAL_RULES_DIR);
97473
+ const targetDir = join61(workdir, CANONICAL_RULES_DIR);
97690
97474
  if (!options.dryRun) {
97691
97475
  try {
97692
97476
  await _rulesCLIDeps.mkdir(targetDir);
@@ -97697,7 +97481,7 @@ async function rulesMigrateCommand(options) {
97697
97481
  let written = 0;
97698
97482
  let skipped = 0;
97699
97483
  for (const { sourcePath, targetFileName, content } of sources) {
97700
- const targetPath = join62(targetDir, targetFileName);
97484
+ const targetPath = join61(targetDir, targetFileName);
97701
97485
  if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
97702
97486
  console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
97703
97487
  skipped++;
@@ -97736,7 +97520,7 @@ function collectCanonicalRuleRoots(workdir) {
97736
97520
  const packageRel = normalized.slice(0, idx);
97737
97521
  if (!packageRel)
97738
97522
  continue;
97739
- roots.add(join62(workdir, packageRel));
97523
+ roots.add(join61(workdir, packageRel));
97740
97524
  }
97741
97525
  return [...roots].sort();
97742
97526
  }
@@ -97758,7 +97542,7 @@ init_logger2();
97758
97542
  init_detect2();
97759
97543
  init_workspace();
97760
97544
  init_common();
97761
- import { join as join63 } from "path";
97545
+ import { join as join62 } from "path";
97762
97546
  function resolveEffective(detected, configPatterns) {
97763
97547
  if (configPatterns !== undefined)
97764
97548
  return "config";
@@ -97843,7 +97627,7 @@ async function detectCommand(options) {
97843
97627
  const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
97844
97628
  const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
97845
97629
  const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
97846
- const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
97630
+ const pkgConfigPath = join62(workdir, ".nax", "mono", dir, "config.json");
97847
97631
  const pkgRaw = await loadRawConfig(pkgConfigPath);
97848
97632
  const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
97849
97633
  const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
@@ -97897,13 +97681,13 @@ async function detectCommand(options) {
97897
97681
  if (rootDetected.confidence === "empty") {
97898
97682
  console.log(source_default.yellow(" root: skipped (empty detection)"));
97899
97683
  } else {
97900
- const rootConfigPath = join63(workdir, ".nax", "config.json");
97684
+ const rootConfigPath = join62(workdir, ".nax", "config.json");
97901
97685
  try {
97902
97686
  const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
97903
97687
  if (status === "skipped") {
97904
97688
  console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
97905
97689
  } else {
97906
- console.log(source_default.green(` root: ${status} \u2192 ${join63(".nax", "config.json")}`));
97690
+ console.log(source_default.green(` root: ${status} \u2192 ${join62(".nax", "config.json")}`));
97907
97691
  }
97908
97692
  } catch (err) {
97909
97693
  console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
@@ -97916,13 +97700,13 @@ async function detectCommand(options) {
97916
97700
  console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
97917
97701
  continue;
97918
97702
  }
97919
- const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
97703
+ const pkgConfigPath = join62(workdir, ".nax", "mono", dir, "config.json");
97920
97704
  try {
97921
97705
  const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
97922
97706
  if (status === "skipped") {
97923
97707
  console.log(source_default.dim(` ${dir}: skipped (already set)`));
97924
97708
  } else {
97925
- console.log(source_default.green(` ${dir}: ${status} \u2192 ${join63(".nax", "mono", dir, "config.json")}`));
97709
+ console.log(source_default.green(` ${dir}: ${status} \u2192 ${join62(".nax", "mono", dir, "config.json")}`));
97926
97710
  }
97927
97711
  } catch (err) {
97928
97712
  console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
@@ -97937,27 +97721,22 @@ async function detectCommand(options) {
97937
97721
  process.exitCode = allEmpty ? 1 : 0;
97938
97722
  }
97939
97723
 
97940
- // src/commands/diagnose.ts
97941
- async function diagnose(options) {
97942
- await diagnoseCommand(options);
97943
- }
97944
-
97945
97724
  // src/commands/logs.ts
97946
97725
  init_common();
97947
- import { existsSync as existsSync30 } from "fs";
97948
- import { join as join67 } from "path";
97726
+ import { existsSync as existsSync29 } from "fs";
97727
+ import { join as join66 } from "path";
97949
97728
 
97950
97729
  // src/commands/logs-formatter.ts
97951
97730
  init_source();
97952
97731
  init_formatter();
97953
- import { readdirSync as readdirSync8 } from "fs";
97954
- import { join as join66 } from "path";
97732
+ import { readdirSync as readdirSync7 } from "fs";
97733
+ import { join as join65 } from "path";
97955
97734
 
97956
97735
  // src/commands/logs-reader.ts
97957
97736
  init_paths3();
97958
- import { existsSync as existsSync29, readdirSync as readdirSync7 } from "fs";
97737
+ import { existsSync as existsSync28, readdirSync as readdirSync6 } from "fs";
97959
97738
  import { readdir as readdir4 } from "fs/promises";
97960
- import { join as join65 } from "path";
97739
+ import { join as join64 } from "path";
97961
97740
  var _logsReaderDeps = {
97962
97741
  getRunsDir
97963
97742
  };
@@ -97971,7 +97750,7 @@ async function resolveRunFileFromRegistry(runId) {
97971
97750
  }
97972
97751
  let matched = null;
97973
97752
  for (const entry of entries) {
97974
- const metaPath = join65(runsDir, entry, "meta.json");
97753
+ const metaPath = join64(runsDir, entry, "meta.json");
97975
97754
  try {
97976
97755
  const meta3 = await Bun.file(metaPath).json();
97977
97756
  if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
@@ -97983,24 +97762,24 @@ async function resolveRunFileFromRegistry(runId) {
97983
97762
  if (!matched) {
97984
97763
  throw new Error(`Run not found in registry: ${runId}`);
97985
97764
  }
97986
- if (!existsSync29(matched.eventsDir)) {
97765
+ if (!existsSync28(matched.eventsDir)) {
97987
97766
  console.log(`Log directory unavailable for run: ${runId}`);
97988
97767
  return null;
97989
97768
  }
97990
- const files = readdirSync7(matched.eventsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97769
+ const files = readdirSync6(matched.eventsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97991
97770
  if (files.length === 0) {
97992
97771
  console.log(`No log files found for run: ${runId}`);
97993
97772
  return null;
97994
97773
  }
97995
97774
  const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
97996
- return join65(matched.eventsDir, specificFile ?? files[0]);
97775
+ return join64(matched.eventsDir, specificFile ?? files[0]);
97997
97776
  }
97998
97777
  async function selectRunFile(runsDir) {
97999
- const files = readdirSync7(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97778
+ const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
98000
97779
  if (files.length === 0) {
98001
97780
  return null;
98002
97781
  }
98003
- return join65(runsDir, files[0]);
97782
+ return join64(runsDir, files[0]);
98004
97783
  }
98005
97784
  async function extractRunSummary(filePath) {
98006
97785
  const file3 = Bun.file(filePath);
@@ -98075,7 +97854,7 @@ var LOG_LEVEL_PRIORITY2 = {
98075
97854
  error: 3
98076
97855
  };
98077
97856
  async function displayRunsList(runsDir) {
98078
- const files = readdirSync8(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
97857
+ const files = readdirSync7(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
98079
97858
  if (files.length === 0) {
98080
97859
  console.log(source_default.dim("No runs found"));
98081
97860
  return;
@@ -98086,7 +97865,7 @@ Runs:
98086
97865
  console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
98087
97866
  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"));
98088
97867
  for (const file3 of files) {
98089
- const filePath = join66(runsDir, file3);
97868
+ const filePath = join65(runsDir, file3);
98090
97869
  const summary = await extractRunSummary(filePath);
98091
97870
  const timestamp = file3.replace(".jsonl", "");
98092
97871
  const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
@@ -98200,7 +97979,7 @@ async function logsCommand(options) {
98200
97979
  return;
98201
97980
  }
98202
97981
  const resolved = resolveProject({ dir: options.dir });
98203
- const naxDir = join67(resolved.projectDir, ".nax");
97982
+ const naxDir = join66(resolved.projectDir, ".nax");
98204
97983
  const configPath = resolved.configPath;
98205
97984
  const configFile = Bun.file(configPath);
98206
97985
  const config2 = await configFile.json();
@@ -98208,9 +97987,9 @@ async function logsCommand(options) {
98208
97987
  if (!featureName) {
98209
97988
  throw new Error("No feature specified in config.json");
98210
97989
  }
98211
- const featureDir = join67(naxDir, "features", featureName);
98212
- const runsDir = join67(featureDir, "runs");
98213
- if (!existsSync30(runsDir)) {
97990
+ const featureDir = join66(naxDir, "features", featureName);
97991
+ const runsDir = join66(featureDir, "runs");
97992
+ if (!existsSync29(runsDir)) {
98214
97993
  throw new Error(`No runs directory found for feature: ${featureName}`);
98215
97994
  }
98216
97995
  if (options.list) {
@@ -98234,8 +98013,8 @@ init_config();
98234
98013
  init_prd();
98235
98014
  init_precheck();
98236
98015
  init_common();
98237
- import { existsSync as existsSync31 } from "fs";
98238
- import { join as join68 } from "path";
98016
+ import { existsSync as existsSync30 } from "fs";
98017
+ import { join as join67 } from "path";
98239
98018
  async function precheckCommand(options) {
98240
98019
  const resolved = resolveProject({
98241
98020
  dir: options.dir,
@@ -98257,14 +98036,14 @@ async function precheckCommand(options) {
98257
98036
  process.exit(1);
98258
98037
  }
98259
98038
  }
98260
- const naxDir = join68(resolved.projectDir, ".nax");
98261
- const featureDir = join68(naxDir, "features", featureName);
98262
- const prdPath = join68(featureDir, "prd.json");
98263
- if (!existsSync31(featureDir)) {
98039
+ const naxDir = join67(resolved.projectDir, ".nax");
98040
+ const featureDir = join67(naxDir, "features", featureName);
98041
+ const prdPath = join67(featureDir, "prd.json");
98042
+ if (!existsSync30(featureDir)) {
98264
98043
  console.error(source_default.red(`Feature not found: ${featureName}`));
98265
98044
  process.exit(1);
98266
98045
  }
98267
- if (!existsSync31(prdPath)) {
98046
+ if (!existsSync30(prdPath)) {
98268
98047
  console.error(source_default.red(`Missing prd.json for feature: ${featureName}`));
98269
98048
  console.error(source_default.dim(`Run: nax plan -f ${featureName} --from spec.md --auto`));
98270
98049
  process.exit(EXIT_CODES.INVALID_PRD);
@@ -98282,7 +98061,7 @@ async function precheckCommand(options) {
98282
98061
  init_source();
98283
98062
  init_paths3();
98284
98063
  import { readdir as readdir5 } from "fs/promises";
98285
- import { join as join69 } from "path";
98064
+ import { join as join68 } from "path";
98286
98065
  var DEFAULT_LIMIT = 20;
98287
98066
  var _runsCmdDeps = {
98288
98067
  getRunsDir
@@ -98337,7 +98116,7 @@ async function runsCommand(options = {}) {
98337
98116
  }
98338
98117
  const rows = [];
98339
98118
  for (const entry of entries) {
98340
- const metaPath = join69(runsDir, entry, "meta.json");
98119
+ const metaPath = join68(runsDir, entry, "meta.json");
98341
98120
  let meta3;
98342
98121
  try {
98343
98122
  meta3 = await Bun.file(metaPath).json();
@@ -98414,8 +98193,8 @@ async function runsCommand(options = {}) {
98414
98193
 
98415
98194
  // src/commands/unlock.ts
98416
98195
  init_source();
98417
- import { join as join70 } from "path";
98418
- function isProcessAlive3(pid) {
98196
+ import { join as join69 } from "path";
98197
+ function isProcessAlive2(pid) {
98419
98198
  try {
98420
98199
  process.kill(pid, 0);
98421
98200
  return true;
@@ -98429,7 +98208,7 @@ function formatLockAge(ageMs) {
98429
98208
  }
98430
98209
  async function unlockCommand(options) {
98431
98210
  const workdir = options.dir ?? process.cwd();
98432
- const lockPath = join70(workdir, "nax.lock");
98211
+ const lockPath = join69(workdir, "nax.lock");
98433
98212
  const lockFile = Bun.file(lockPath);
98434
98213
  const exists = await lockFile.exists();
98435
98214
  if (!exists) {
@@ -98447,7 +98226,7 @@ async function unlockCommand(options) {
98447
98226
  const { pid, timestamp } = lockData;
98448
98227
  const ageMs = Date.now() - timestamp;
98449
98228
  if (!options.force) {
98450
- if (isProcessAlive3(pid)) {
98229
+ if (isProcessAlive2(pid)) {
98451
98230
  console.error(source_default.red(`nax is still running (PID ${pid}). Use --force to override.`));
98452
98231
  process.exit(1);
98453
98232
  }
@@ -105127,7 +104906,7 @@ var import_react27 = __toESM(require_react(), 1);
105127
104906
  // node_modules/ink/build/hooks/use-cursor.js
105128
104907
  var import_react28 = __toESM(require_react(), 1);
105129
104908
  // src/tui/App.tsx
105130
- var import_react35 = __toESM(require_react(), 1);
104909
+ var import_react36 = __toESM(require_react(), 1);
105131
104910
 
105132
104911
  // src/utils/queue-writer.ts
105133
104912
  async function writeQueueCommand(queueFilePath, command) {
@@ -105156,151 +104935,8 @@ ${commandLine2}
105156
104935
  await Bun.write(queueFilePath, newContent);
105157
104936
  }
105158
104937
 
105159
- // node_modules/ink-spinner/build/index.js
105160
- var import_react29 = __toESM(require_react(), 1);
105161
- var import_cli_spinners = __toESM(require_cli_spinners(), 1);
105162
- function Spinner({ type = "dots" }) {
105163
- const [frame, setFrame] = import_react29.useState(0);
105164
- const spinner = import_cli_spinners.default[type];
105165
- import_react29.useEffect(() => {
105166
- const timer = setInterval(() => {
105167
- setFrame((previousFrame) => {
105168
- const isLastFrame = previousFrame === spinner.frames.length - 1;
105169
- return isLastFrame ? 0 : previousFrame + 1;
105170
- });
105171
- }, spinner.interval);
105172
- return () => {
105173
- clearInterval(timer);
105174
- };
105175
- }, [spinner]);
105176
- return import_react29.default.createElement(Text, null, spinner.frames[frame]);
105177
- }
105178
- var build_default = Spinner;
105179
-
105180
- // src/tui/components/AgentPanel.tsx
105181
- var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
105182
- var MAX_OUTPUT_LINES = 500;
105183
- function AgentPanel({ focused = false, outputLines = [], activeCalls }) {
105184
- const borderColor = focused ? "cyan" : "gray";
105185
- const bufferedLines = outputLines.length > MAX_OUTPUT_LINES ? outputLines.slice(-MAX_OUTPUT_LINES) : outputLines;
105186
- const activeCallList = activeCalls ? Array.from(activeCalls.values()) : [];
105187
- const hasActiveCalls = activeCallList.length > 0;
105188
- const hasOutput = bufferedLines.length > 0;
105189
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105190
- flexDirection: "column",
105191
- flexGrow: 1,
105192
- borderStyle: "single",
105193
- borderColor,
105194
- children: [
105195
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105196
- paddingX: 1,
105197
- borderStyle: "single",
105198
- borderBottom: true,
105199
- borderColor,
105200
- children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105201
- bold: true,
105202
- color: focused ? "cyan" : undefined,
105203
- children: [
105204
- "Agent ",
105205
- focused && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105206
- dimColor: true,
105207
- children: "(focused)"
105208
- }, undefined, false, undefined, this)
105209
- ]
105210
- }, undefined, true, undefined, this)
105211
- }, undefined, false, undefined, this),
105212
- hasActiveCalls && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105213
- flexDirection: "column",
105214
- paddingX: 1,
105215
- paddingY: 1,
105216
- children: activeCallList.map((call) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV(AgentCallRow, {
105217
- call
105218
- }, call.callId, false, undefined, this))
105219
- }, undefined, false, undefined, this),
105220
- !hasActiveCalls && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105221
- flexDirection: "column",
105222
- paddingX: 1,
105223
- paddingY: 1,
105224
- children: hasOutput ? bufferedLines.map((line, i) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105225
- children: line
105226
- }, `line-${i}-${line.slice(0, 20)}`, false, undefined, this)) : /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105227
- dimColor: true,
105228
- children: [
105229
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(build_default, {
105230
- type: "dots"
105231
- }, undefined, false, undefined, this),
105232
- " Waiting for agent..."
105233
- ]
105234
- }, undefined, true, undefined, this)
105235
- }, undefined, false, undefined, this)
105236
- ]
105237
- }, undefined, true, undefined, this);
105238
- }
105239
- function formatMs(ms) {
105240
- if (ms < 1000)
105241
- return `${ms}ms`;
105242
- return `${Math.floor(ms / 1000)}s`;
105243
- }
105244
- function AgentCallRow({ call }) {
105245
- const now3 = Date.now();
105246
- const elapsedMs = now3 - call.startedAt;
105247
- const idleMs = now3 - call.lastActivityAt;
105248
- return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105249
- flexDirection: "row",
105250
- gap: 1,
105251
- children: [
105252
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105253
- color: "cyan",
105254
- children: call.agentName
105255
- }, undefined, false, undefined, this),
105256
- call.storyId && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105257
- dimColor: true,
105258
- children: call.storyId
105259
- }, undefined, false, undefined, this),
105260
- call.stage && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105261
- dimColor: true,
105262
- children: [
105263
- "[",
105264
- call.stage,
105265
- "]"
105266
- ]
105267
- }, undefined, true, undefined, this),
105268
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105269
- children: [
105270
- " elapsed:",
105271
- formatMs(elapsedMs)
105272
- ]
105273
- }, undefined, true, undefined, this),
105274
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105275
- children: [
105276
- " idle:",
105277
- formatMs(idleMs)
105278
- ]
105279
- }, undefined, true, undefined, this),
105280
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105281
- children: [
105282
- " msg:",
105283
- call.messageUpdates
105284
- ]
105285
- }, undefined, true, undefined, this),
105286
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105287
- children: [
105288
- " think:",
105289
- call.thinkingUpdates
105290
- ]
105291
- }, undefined, true, undefined, this),
105292
- /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105293
- children: [
105294
- " usage:",
105295
- call.usageUpdates
105296
- ]
105297
- }, undefined, true, undefined, this)
105298
- ]
105299
- }, undefined, true, undefined, this);
105300
- }
105301
-
105302
104938
  // src/tui/components/CostOverlay.tsx
105303
- var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
104939
+ var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
105304
104940
  function formatCost2(cost) {
105305
104941
  return `$${cost.toFixed(4)}`;
105306
104942
  }
@@ -105309,7 +104945,7 @@ function CostOverlay({ visible = false, stories = [], totalCost = 0 }) {
105309
104945
  return null;
105310
104946
  }
105311
104947
  const executedStories = stories.filter((s) => s.cost && s.cost > 0 || s.status !== "pending");
105312
- return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104948
+ return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105313
104949
  flexDirection: "column",
105314
104950
  borderStyle: "double",
105315
104951
  borderColor: "cyan",
@@ -105317,87 +104953,87 @@ function CostOverlay({ visible = false, stories = [], totalCost = 0 }) {
105317
104953
  paddingY: 1,
105318
104954
  minWidth: 60,
105319
104955
  children: [
105320
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104956
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105321
104957
  paddingBottom: 1,
105322
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
104958
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105323
104959
  bold: true,
105324
104960
  color: "cyan",
105325
104961
  children: "Cost Breakdown"
105326
104962
  }, undefined, false, undefined, this)
105327
104963
  }, undefined, false, undefined, this),
105328
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104964
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105329
104965
  paddingBottom: 1,
105330
104966
  borderBottom: true,
105331
104967
  borderColor: "gray",
105332
104968
  children: [
105333
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104969
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105334
104970
  width: 12,
105335
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
104971
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105336
104972
  bold: true,
105337
104973
  children: "Story ID"
105338
104974
  }, undefined, false, undefined, this)
105339
104975
  }, undefined, false, undefined, this),
105340
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104976
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105341
104977
  width: 12,
105342
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
104978
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105343
104979
  bold: true,
105344
104980
  children: "Status"
105345
104981
  }, undefined, false, undefined, this)
105346
104982
  }, undefined, false, undefined, this),
105347
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104983
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105348
104984
  width: 12,
105349
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
104985
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105350
104986
  bold: true,
105351
104987
  children: "Cost"
105352
104988
  }, undefined, false, undefined, this)
105353
104989
  }, undefined, false, undefined, this)
105354
104990
  ]
105355
104991
  }, undefined, true, undefined, this),
105356
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104992
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105357
104993
  flexDirection: "column",
105358
104994
  paddingY: 1,
105359
- children: executedStories.length > 0 ? executedStories.map((story) => /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104995
+ children: executedStories.length > 0 ? executedStories.map((story) => /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105360
104996
  children: [
105361
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
104997
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105362
104998
  width: 12,
105363
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
104999
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105364
105000
  children: story.story.id
105365
105001
  }, undefined, false, undefined, this)
105366
105002
  }, undefined, false, undefined, this),
105367
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105003
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105368
105004
  width: 12,
105369
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105005
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105370
105006
  color: story.status === "passed" ? "green" : story.status === "failed" ? "red" : undefined,
105371
105007
  children: story.status
105372
105008
  }, undefined, false, undefined, this)
105373
105009
  }, undefined, false, undefined, this),
105374
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105010
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105375
105011
  width: 12,
105376
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105012
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105377
105013
  children: formatCost2(story.cost || 0)
105378
105014
  }, undefined, false, undefined, this)
105379
105015
  }, undefined, false, undefined, this)
105380
105016
  ]
105381
- }, story.story.id, true, undefined, this)) : /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105017
+ }, story.story.id, true, undefined, this)) : /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105382
105018
  dimColor: true,
105383
105019
  children: "No stories executed yet"
105384
105020
  }, undefined, false, undefined, this)
105385
105021
  }, undefined, false, undefined, this),
105386
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105022
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105387
105023
  paddingTop: 1,
105388
105024
  borderTop: true,
105389
105025
  borderColor: "gray",
105390
105026
  children: [
105391
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105027
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105392
105028
  width: 24,
105393
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105029
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105394
105030
  bold: true,
105395
105031
  children: "Total Cost:"
105396
105032
  }, undefined, false, undefined, this)
105397
105033
  }, undefined, false, undefined, this),
105398
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105034
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105399
105035
  width: 12,
105400
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105036
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105401
105037
  bold: true,
105402
105038
  color: "cyan",
105403
105039
  children: formatCost2(totalCost)
@@ -105405,16 +105041,16 @@ function CostOverlay({ visible = false, stories = [], totalCost = 0 }) {
105405
105041
  }, undefined, false, undefined, this)
105406
105042
  ]
105407
105043
  }, undefined, true, undefined, this),
105408
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105044
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
105409
105045
  justifyContent: "center",
105410
105046
  paddingTop: 1,
105411
105047
  borderTop: true,
105412
105048
  borderColor: "gray",
105413
- children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105049
+ children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105414
105050
  dimColor: true,
105415
105051
  children: [
105416
105052
  "Press ",
105417
- /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105053
+ /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
105418
105054
  color: "yellow",
105419
105055
  children: "Esc"
105420
105056
  }, undefined, false, undefined, this),
@@ -105427,118 +105063,118 @@ function CostOverlay({ visible = false, stories = [], totalCost = 0 }) {
105427
105063
  }
105428
105064
 
105429
105065
  // src/tui/components/HelpOverlay.tsx
105430
- var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
105066
+ var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
105431
105067
  function HelpOverlay({ visible = false }) {
105432
105068
  if (!visible) {
105433
105069
  return null;
105434
105070
  }
105435
- return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105071
+ return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105436
105072
  flexDirection: "column",
105437
105073
  borderStyle: "double",
105438
105074
  borderColor: "cyan",
105439
105075
  paddingX: 2,
105440
105076
  paddingY: 1,
105441
105077
  children: [
105442
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105078
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105443
105079
  paddingBottom: 1,
105444
- children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105080
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105445
105081
  bold: true,
105446
105082
  color: "cyan",
105447
105083
  children: "Keyboard Shortcuts"
105448
105084
  }, undefined, false, undefined, this)
105449
105085
  }, undefined, false, undefined, this),
105450
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105086
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105451
105087
  flexDirection: "column",
105452
105088
  paddingBottom: 1,
105453
105089
  children: [
105454
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105090
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105455
105091
  dimColor: true,
105456
105092
  children: "Stories Panel (default):"
105457
105093
  }, undefined, false, undefined, this),
105458
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105094
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105459
105095
  children: [
105460
105096
  " ",
105461
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105097
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105462
105098
  color: "yellow",
105463
105099
  children: "p"
105464
105100
  }, undefined, false, undefined, this),
105465
105101
  " \u2014 Pause after current story"
105466
105102
  ]
105467
105103
  }, undefined, true, undefined, this),
105468
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105104
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105469
105105
  children: [
105470
105106
  " ",
105471
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105107
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105472
105108
  color: "yellow",
105473
105109
  children: "a"
105474
105110
  }, undefined, false, undefined, this),
105475
105111
  " \u2014 Abort run"
105476
105112
  ]
105477
105113
  }, undefined, true, undefined, this),
105478
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105114
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105479
105115
  children: [
105480
105116
  " ",
105481
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105117
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105482
105118
  color: "yellow",
105483
105119
  children: "s"
105484
105120
  }, undefined, false, undefined, this),
105485
105121
  " \u2014 Skip current story"
105486
105122
  ]
105487
105123
  }, undefined, true, undefined, this),
105488
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105124
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105489
105125
  children: [
105490
105126
  " ",
105491
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105127
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105492
105128
  color: "yellow",
105493
105129
  children: "Tab"
105494
105130
  }, undefined, false, undefined, this),
105495
105131
  " \u2014 Toggle focus to Agent panel"
105496
105132
  ]
105497
105133
  }, undefined, true, undefined, this),
105498
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105134
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105499
105135
  children: [
105500
105136
  " ",
105501
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105137
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105502
105138
  color: "yellow",
105503
105139
  children: "q"
105504
105140
  }, undefined, false, undefined, this),
105505
105141
  " \u2014 Quit TUI"
105506
105142
  ]
105507
105143
  }, undefined, true, undefined, this),
105508
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105144
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105509
105145
  children: [
105510
105146
  " ",
105511
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105147
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105512
105148
  color: "yellow",
105513
105149
  children: "?"
105514
105150
  }, undefined, false, undefined, this),
105515
105151
  " \u2014 Show this help"
105516
105152
  ]
105517
105153
  }, undefined, true, undefined, this),
105518
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105154
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105519
105155
  children: [
105520
105156
  " ",
105521
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105157
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105522
105158
  color: "yellow",
105523
105159
  children: "c"
105524
105160
  }, undefined, false, undefined, this),
105525
105161
  " \u2014 Show cost breakdown"
105526
105162
  ]
105527
105163
  }, undefined, true, undefined, this),
105528
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105164
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105529
105165
  children: [
105530
105166
  " ",
105531
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105167
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105532
105168
  color: "yellow",
105533
105169
  children: "r"
105534
105170
  }, undefined, false, undefined, this),
105535
105171
  " \u2014 Retry last failed story"
105536
105172
  ]
105537
105173
  }, undefined, true, undefined, this),
105538
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105174
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105539
105175
  children: [
105540
105176
  " ",
105541
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105177
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105542
105178
  color: "yellow",
105543
105179
  children: "Esc"
105544
105180
  }, undefined, false, undefined, this),
@@ -105547,28 +105183,28 @@ function HelpOverlay({ visible = false }) {
105547
105183
  }, undefined, true, undefined, this)
105548
105184
  ]
105549
105185
  }, undefined, true, undefined, this),
105550
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105186
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105551
105187
  flexDirection: "column",
105552
105188
  paddingBottom: 1,
105553
105189
  children: [
105554
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105190
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105555
105191
  dimColor: true,
105556
105192
  children: "Agent Panel (when focused):"
105557
105193
  }, undefined, false, undefined, this),
105558
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105194
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105559
105195
  children: [
105560
105196
  " ",
105561
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105197
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105562
105198
  color: "yellow",
105563
105199
  children: "Ctrl+]"
105564
105200
  }, undefined, false, undefined, this),
105565
105201
  " \u2014 Escape back to Stories panel"
105566
105202
  ]
105567
105203
  }, undefined, true, undefined, this),
105568
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105204
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105569
105205
  children: [
105570
105206
  " ",
105571
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105207
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105572
105208
  dimColor: true,
105573
105209
  children: "All other keys"
105574
105210
  }, undefined, false, undefined, this),
@@ -105577,16 +105213,16 @@ function HelpOverlay({ visible = false }) {
105577
105213
  }, undefined, true, undefined, this)
105578
105214
  ]
105579
105215
  }, undefined, true, undefined, this),
105580
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105216
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
105581
105217
  justifyContent: "center",
105582
105218
  paddingTop: 1,
105583
105219
  borderTop: true,
105584
105220
  borderColor: "gray",
105585
- children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105221
+ children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105586
105222
  dimColor: true,
105587
105223
  children: [
105588
105224
  "Press ",
105589
- /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105225
+ /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
105590
105226
  color: "yellow",
105591
105227
  children: "Esc"
105592
105228
  }, undefined, false, undefined, this),
@@ -105598,46 +105234,289 @@ function HelpOverlay({ visible = false }) {
105598
105234
  }, undefined, true, undefined, this);
105599
105235
  }
105600
105236
 
105601
- // src/tui/components/StatusBar.tsx
105602
- var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
105603
- function StatusBar({ currentStory, currentStage, modelTier, testStrategy }) {
105604
- if (!currentStory) {
105605
- return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
105606
- paddingX: 1,
105607
- borderStyle: "single",
105608
- borderColor: "gray",
105609
- children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
105610
- dimColor: true,
105611
- children: "Idle"
105612
- }, undefined, false, undefined, this)
105613
- }, undefined, false, undefined, this);
105614
- }
105615
- const storyInfo = `Story ${currentStory.id}`;
105616
- const stageInfo = currentStage ? ` \xB7 ${currentStage}` : "";
105617
- const tierInfo = modelTier ? ` \xB7 ${modelTier}` : "";
105618
- const strategyInfo = testStrategy ? ` \xB7 ${testStrategy}` : "";
105619
- return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
105620
- paddingX: 1,
105237
+ // node_modules/ink-spinner/build/index.js
105238
+ var import_react29 = __toESM(require_react(), 1);
105239
+ var import_cli_spinners = __toESM(require_cli_spinners(), 1);
105240
+ function Spinner({ type = "dots" }) {
105241
+ const [frame, setFrame] = import_react29.useState(0);
105242
+ const spinner = import_cli_spinners.default[type];
105243
+ import_react29.useEffect(() => {
105244
+ const timer = setInterval(() => {
105245
+ setFrame((previousFrame) => {
105246
+ const isLastFrame = previousFrame === spinner.frames.length - 1;
105247
+ return isLastFrame ? 0 : previousFrame + 1;
105248
+ });
105249
+ }, spinner.interval);
105250
+ return () => {
105251
+ clearInterval(timer);
105252
+ };
105253
+ }, [spinner]);
105254
+ return import_react29.default.createElement(Text, null, spinner.frames[frame]);
105255
+ }
105256
+ var build_default = Spinner;
105257
+
105258
+ // src/tui/components/LiveActivityPanel.tsx
105259
+ var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
105260
+ var MAX_ESCALATION_DISPLAY = 5;
105261
+ function LiveActivityPanel({
105262
+ focused = false,
105263
+ activeCalls,
105264
+ runSummary,
105265
+ runErrored,
105266
+ escalationLog = []
105267
+ }) {
105268
+ const borderColor = focused ? "cyan" : "gray";
105269
+ const activeCallList = activeCalls ? Array.from(activeCalls.values()) : [];
105270
+ const hasActiveCalls = activeCallList.length > 0;
105271
+ const hasSummary = runSummary !== undefined;
105272
+ const hasError = runErrored !== undefined;
105273
+ const recentEscalations = escalationLog.slice(-MAX_ESCALATION_DISPLAY);
105274
+ const hasEscalations = recentEscalations.length > 0;
105275
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105276
+ flexDirection: "column",
105277
+ flexGrow: 1,
105621
105278
  borderStyle: "single",
105622
- borderColor: "gray",
105623
- children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
105279
+ borderColor,
105280
+ children: [
105281
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105282
+ paddingX: 1,
105283
+ borderStyle: "single",
105284
+ borderBottom: true,
105285
+ borderColor,
105286
+ children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105287
+ bold: true,
105288
+ color: focused ? "cyan" : undefined,
105289
+ children: [
105290
+ "Live Activity ",
105291
+ focused && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105292
+ dimColor: true,
105293
+ children: "(focused)"
105294
+ }, undefined, false, undefined, this)
105295
+ ]
105296
+ }, undefined, true, undefined, this)
105297
+ }, undefined, false, undefined, this),
105298
+ hasError && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105299
+ paddingX: 1,
105300
+ paddingY: 1,
105301
+ children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105302
+ color: "red",
105303
+ children: [
105304
+ "[FAIL] ",
105305
+ runErrored
105306
+ ]
105307
+ }, undefined, true, undefined, this)
105308
+ }, undefined, false, undefined, this),
105309
+ hasSummary && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105310
+ flexDirection: "column",
105311
+ paddingX: 1,
105312
+ paddingY: 1,
105313
+ children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(RunSummaryRow, {
105314
+ summary: runSummary
105315
+ }, undefined, false, undefined, this)
105316
+ }, undefined, false, undefined, this),
105317
+ hasActiveCalls && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105318
+ flexDirection: "column",
105319
+ paddingX: 1,
105320
+ paddingY: 1,
105321
+ children: activeCallList.map((call) => /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(ActiveCallRow, {
105322
+ call
105323
+ }, call.callId, false, undefined, this))
105324
+ }, undefined, false, undefined, this),
105325
+ hasEscalations && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105326
+ flexDirection: "column",
105327
+ paddingX: 1,
105328
+ children: [
105329
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105330
+ dimColor: true,
105331
+ children: "Escalations:"
105332
+ }, undefined, false, undefined, this),
105333
+ recentEscalations.map((entry) => /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(EscalationRow, {
105334
+ entry
105335
+ }, `${entry.storyId}-${entry.at}`, false, undefined, this))
105336
+ ]
105337
+ }, undefined, true, undefined, this),
105338
+ !hasActiveCalls && !hasSummary && !hasError && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105339
+ paddingX: 1,
105340
+ paddingY: 1,
105341
+ children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105342
+ dimColor: true,
105343
+ children: [
105344
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(build_default, {
105345
+ type: "dots"
105346
+ }, undefined, false, undefined, this),
105347
+ " Waiting for agent..."
105348
+ ]
105349
+ }, undefined, true, undefined, this)
105350
+ }, undefined, false, undefined, this)
105351
+ ]
105352
+ }, undefined, true, undefined, this);
105353
+ }
105354
+ function ActiveCallRow({ call }) {
105355
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105356
+ flexDirection: "column",
105357
+ marginBottom: 1,
105358
+ children: [
105359
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105360
+ flexDirection: "row",
105361
+ gap: 1,
105362
+ children: [
105363
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105364
+ color: "cyan",
105365
+ children: call.agentName
105366
+ }, undefined, false, undefined, this),
105367
+ call.storyId && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105368
+ children: call.storyId
105369
+ }, undefined, false, undefined, this),
105370
+ call.stage && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105371
+ dimColor: true,
105372
+ children: [
105373
+ "[",
105374
+ call.stage,
105375
+ "]"
105376
+ ]
105377
+ }, undefined, true, undefined, this)
105378
+ ]
105379
+ }, undefined, true, undefined, this),
105380
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105381
+ flexDirection: "row",
105382
+ gap: 1,
105383
+ children: [
105384
+ call.model && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105385
+ dimColor: true,
105386
+ children: [
105387
+ "model:",
105388
+ call.model
105389
+ ]
105390
+ }, undefined, true, undefined, this),
105391
+ call.lastToolName && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105392
+ dimColor: true,
105393
+ children: [
105394
+ "tool:",
105395
+ call.lastToolName
105396
+ ]
105397
+ }, undefined, true, undefined, this),
105398
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105399
+ dimColor: true,
105400
+ children: [
105401
+ "tools:",
105402
+ call.toolCallUpdates,
105403
+ " msg:",
105404
+ call.messageUpdates
105405
+ ]
105406
+ }, undefined, true, undefined, this)
105407
+ ]
105408
+ }, undefined, true, undefined, this)
105409
+ ]
105410
+ }, undefined, true, undefined, this);
105411
+ }
105412
+ function RunSummaryRow({ summary }) {
105413
+ const cost = summary.totalCost !== undefined ? `$${summary.totalCost.toFixed(4)}` : null;
105414
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105415
+ flexDirection: "column",
105416
+ children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105417
+ flexDirection: "row",
105418
+ gap: 1,
105624
105419
  children: [
105625
- /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
105420
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105626
105421
  bold: true,
105627
- children: storyInfo
105422
+ children: "Run complete"
105628
105423
  }, undefined, false, undefined, this),
105629
- /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
105424
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105425
+ color: "green",
105426
+ children: [
105427
+ summary.passedStories,
105428
+ " passed"
105429
+ ]
105430
+ }, undefined, true, undefined, this),
105431
+ summary.failedStories > 0 && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105432
+ color: "red",
105433
+ children: [
105434
+ summary.failedStories,
105435
+ " failed"
105436
+ ]
105437
+ }, undefined, true, undefined, this),
105438
+ summary.skippedStories > 0 && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105630
105439
  dimColor: true,
105631
105440
  children: [
105632
- stageInfo,
105633
- tierInfo,
105634
- strategyInfo
105441
+ summary.skippedStories,
105442
+ " skipped"
105635
105443
  ]
105636
- }, undefined, true, undefined, this)
105444
+ }, undefined, true, undefined, this),
105445
+ cost && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105446
+ dimColor: true,
105447
+ children: cost
105448
+ }, undefined, false, undefined, this)
105637
105449
  ]
105638
105450
  }, undefined, true, undefined, this)
105639
105451
  }, undefined, false, undefined, this);
105640
105452
  }
105453
+ function EscalationRow({ entry }) {
105454
+ return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
105455
+ flexDirection: "row",
105456
+ gap: 1,
105457
+ children: [
105458
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105459
+ dimColor: true,
105460
+ children: entry.storyId
105461
+ }, undefined, false, undefined, this),
105462
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105463
+ color: "yellow",
105464
+ children: entry.fromTier
105465
+ }, undefined, false, undefined, this),
105466
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105467
+ dimColor: true,
105468
+ children: "->"
105469
+ }, undefined, false, undefined, this),
105470
+ /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
105471
+ color: "cyan",
105472
+ children: entry.toTier
105473
+ }, undefined, false, undefined, this)
105474
+ ]
105475
+ }, undefined, true, undefined, this);
105476
+ }
105477
+
105478
+ // src/tui/components/StatusBar.tsx
105479
+ var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
105480
+ function StatusBar({
105481
+ currentStage,
105482
+ currentStoryId,
105483
+ modelTier,
105484
+ runPaused,
105485
+ runComplete,
105486
+ isParallel,
105487
+ activeCount = 0
105488
+ }) {
105489
+ const hints = runComplete ? "q quit c cost ? help" : "p pause a abort s skip c cost ? help";
105490
+ let context;
105491
+ if (runComplete) {
105492
+ context = "done";
105493
+ } else if (runPaused) {
105494
+ context = "run paused";
105495
+ } else if (isParallel && activeCount > 0) {
105496
+ context = `parallel \xB7 ${activeCount} active`;
105497
+ } else if (currentStoryId) {
105498
+ const parts = [currentStoryId, currentStage, modelTier].filter(Boolean);
105499
+ context = parts.join(" \xB7 ");
105500
+ } else {
105501
+ context = "idle";
105502
+ }
105503
+ return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
105504
+ paddingX: 1,
105505
+ borderStyle: "single",
105506
+ borderColor: "gray",
105507
+ justifyContent: "space-between",
105508
+ children: [
105509
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
105510
+ dimColor: true,
105511
+ children: hints
105512
+ }, undefined, false, undefined, this),
105513
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
105514
+ dimColor: true,
105515
+ children: context
105516
+ }, undefined, false, undefined, this)
105517
+ ]
105518
+ }, undefined, true, undefined, this);
105519
+ }
105641
105520
 
105642
105521
  // src/tui/components/StoriesPanel.tsx
105643
105522
  var import_react31 = __toESM(require_react(), 1);
@@ -105702,13 +105581,7 @@ function getStatusIcon(status) {
105702
105581
  return "\u23F8\uFE0F";
105703
105582
  }
105704
105583
  }
105705
- function formatElapsedTime(ms) {
105706
- const totalSeconds = Math.floor(ms / 1000);
105707
- const minutes = Math.floor(totalSeconds / 60);
105708
- const seconds = totalSeconds % 60;
105709
- return `${minutes}m ${seconds}s`;
105710
- }
105711
- function StoriesPanel({ stories, totalCost, elapsedMs, width, compact: compact2 = false, maxHeight }) {
105584
+ function StoriesPanel({ stories, width, compact: compact2 = false, maxHeight }) {
105712
105585
  const maxVisible = compact2 ? COMPACT_MAX_VISIBLE_STORIES : MAX_VISIBLE_STORIES;
105713
105586
  const needsScrolling = stories.length > maxVisible;
105714
105587
  const [scrollOffset, setScrollOffset] = import_react31.useState(0);
@@ -105781,20 +105654,37 @@ function StoriesPanel({ stories, totalCost, elapsedMs, width, compact: compact2
105781
105654
  }, undefined, true, undefined, this)
105782
105655
  }, s.story.id, false, undefined, this);
105783
105656
  }
105784
- const routing = s.routing ? ` ${s.routing.complexity.slice(0, 3)} ${s.routing.modelTier}` : "";
105657
+ const routing = s.routing ? ` ${s.routing.complexity.slice(0, 3)}` : "";
105658
+ const shortTier = s.modelTier?.slice(0, 3);
105659
+ const tierSuffix = s.status === "retrying" && shortTier ? `\u2192${shortTier}` : s.status === "running" && shortTier ? shortTier : "";
105660
+ const showFailureLine = (s.status === "failed" || s.status === "paused") && s.failureReason;
105785
105661
  return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
105786
- children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105787
- children: [
105788
- icon,
105789
- " ",
105790
- s.story.id,
105791
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105792
- dimColor: true,
105793
- children: routing
105794
- }, undefined, false, undefined, this)
105795
- ]
105796
- }, undefined, true, undefined, this)
105797
- }, s.story.id, false, undefined, this);
105662
+ flexDirection: "column",
105663
+ children: [
105664
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105665
+ children: [
105666
+ icon,
105667
+ " ",
105668
+ s.story.id,
105669
+ /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105670
+ dimColor: true,
105671
+ children: routing
105672
+ }, undefined, false, undefined, this),
105673
+ tierSuffix ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105674
+ dimColor: true,
105675
+ children: [
105676
+ " ",
105677
+ tierSuffix
105678
+ ]
105679
+ }, undefined, true, undefined, this) : null
105680
+ ]
105681
+ }, undefined, true, undefined, this),
105682
+ showFailureLine && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105683
+ dimColor: true,
105684
+ children: ` \u2514 ${s.failureReason.slice(0, 25)}`
105685
+ }, undefined, false, undefined, this)
105686
+ ]
105687
+ }, s.story.id, true, undefined, this);
105798
105688
  })
105799
105689
  }, undefined, false, undefined, this),
105800
105690
  needsScrolling && canScrollDown && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
@@ -105807,50 +105697,7 @@ function StoriesPanel({ stories, totalCost, elapsedMs, width, compact: compact2
105807
105697
  " more below"
105808
105698
  ]
105809
105699
  }, undefined, true, undefined, this)
105810
- }, undefined, false, undefined, this),
105811
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
105812
- flexDirection: "column",
105813
- paddingX: 1,
105814
- paddingY: 1,
105815
- borderStyle: "single",
105816
- borderTop: true,
105817
- borderColor: "gray",
105818
- children: [
105819
- !compact2 && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(jsx_dev_runtime5.Fragment, {
105820
- children: [
105821
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105822
- children: [
105823
- "Cost: ",
105824
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105825
- color: "green",
105826
- children: [
105827
- "$",
105828
- totalCost.toFixed(4)
105829
- ]
105830
- }, undefined, true, undefined, this)
105831
- ]
105832
- }, undefined, true, undefined, this),
105833
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105834
- children: [
105835
- "Time: ",
105836
- /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105837
- color: "cyan",
105838
- children: formatElapsedTime(elapsedMs)
105839
- }, undefined, false, undefined, this)
105840
- ]
105841
- }, undefined, true, undefined, this)
105842
- ]
105843
- }, undefined, true, undefined, this),
105844
- compact2 && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
105845
- children: [
105846
- "$",
105847
- totalCost.toFixed(2),
105848
- " \xB7 ",
105849
- formatElapsedTime(elapsedMs)
105850
- ]
105851
- }, undefined, true, undefined, this)
105852
- ]
105853
- }, undefined, true, undefined, this)
105700
+ }, undefined, false, undefined, this)
105854
105701
  ]
105855
105702
  }, undefined, true, undefined, this);
105856
105703
  }
@@ -105879,7 +105726,8 @@ function useAgentStreamEvents(bus) {
105879
105726
  thinkingUpdates: 0,
105880
105727
  usageUpdates: 0,
105881
105728
  toolCallUpdates: 0,
105882
- status: "active"
105729
+ status: "active",
105730
+ model: event.model
105883
105731
  });
105884
105732
  break;
105885
105733
  }
@@ -105922,7 +105770,8 @@ function useAgentStreamEvents(bus) {
105922
105770
  next.set(event.callId, {
105923
105771
  ...state,
105924
105772
  toolCallUpdates: state.toolCallUpdates + 1,
105925
- lastActivityAt: event.timestamp
105773
+ lastActivityAt: event.timestamp,
105774
+ lastToolName: event.toolName
105926
105775
  });
105927
105776
  }
105928
105777
  break;
@@ -105996,128 +105845,159 @@ function useKeyboard({ focus, currentStory, onAction, disabled = false }) {
105996
105845
  });
105997
105846
  }
105998
105847
 
105999
- // src/tui/hooks/usePipelineEvents.ts
105848
+ // src/tui/hooks/usePipelineBusEvents.ts
105849
+ init_pipeline();
106000
105850
  var import_react33 = __toESM(require_react(), 1);
106001
- function usePipelineEvents(events, initialStories) {
105851
+ function usePipelineBusEvents(initialStories) {
106002
105852
  const [state, setState] = import_react33.useState(() => ({
106003
- stories: initialStories.map((story) => ({
106004
- story,
106005
- status: story.passes ? "passed" : "pending",
106006
- routing: story.routing,
106007
- cost: 0
106008
- })),
105853
+ stories: initialStories,
106009
105854
  totalCost: 0,
106010
- elapsedMs: 0
105855
+ elapsedMs: 0,
105856
+ runPaused: false,
105857
+ runErrored: false,
105858
+ escalationLog: []
106011
105859
  }));
106012
105860
  const startTimeRef = import_react33.useRef(Date.now());
106013
105861
  import_react33.useEffect(() => {
106014
105862
  const startTime = startTimeRef.current;
106015
- let timer = null;
106016
- const startTimer = () => {
106017
- if (!timer) {
106018
- timer = setInterval(() => {
106019
- setState((prev) => ({
106020
- ...prev,
106021
- elapsedMs: Date.now() - startTime
106022
- }));
106023
- }, 1000);
106024
- }
106025
- };
106026
- const stopTimer = () => {
106027
- if (timer) {
106028
- clearInterval(timer);
106029
- timer = null;
106030
- }
106031
- };
106032
- const onStoryStart = (story) => {
106033
- startTimer();
105863
+ const timer = setInterval(() => {
106034
105864
  setState((prev) => ({
106035
105865
  ...prev,
106036
- currentStory: story,
106037
- stories: prev.stories.map((s) => s.story.id === story.id ? { ...s, status: "running" } : s)
105866
+ elapsedMs: Date.now() - startTime
106038
105867
  }));
106039
- };
106040
- const onStoryComplete = (story, result2) => {
106041
- stopTimer();
105868
+ }, 1000);
105869
+ const unsubStarted = pipelineEventBus.on("story:started", (event) => {
105870
+ setState((prev) => ({
105871
+ ...prev,
105872
+ stories: prev.stories.map((s) => s.story.id === event.storyId ? {
105873
+ ...s,
105874
+ status: "running",
105875
+ modelTier: event.modelTier,
105876
+ iteration: event.iteration
105877
+ } : s)
105878
+ }));
105879
+ });
105880
+ const unsubCompleted = pipelineEventBus.on("story:completed", (event) => {
106042
105881
  setState((prev) => {
106043
105882
  const newStories = prev.stories.map((s) => {
106044
- if (s.story.id === story.id) {
106045
- let status = "pending";
106046
- if (result2.action === "continue") {
106047
- status = "passed";
106048
- } else if (result2.action === "fail") {
106049
- status = "failed";
106050
- } else if (result2.action === "skip") {
106051
- status = "skipped";
106052
- } else if (result2.action === "pause") {
106053
- status = "paused";
106054
- }
106055
- const storyCost = (s.cost || 0) + (result2.cost || 0);
105883
+ if (s.story.id === event.storyId) {
105884
+ const status = event.passed ? "passed" : "failed";
105885
+ const storyCost = event.cost ?? s.cost;
106056
105886
  return { ...s, status, cost: storyCost };
106057
105887
  }
106058
105888
  return s;
106059
105889
  });
106060
- const totalCost = newStories.reduce((sum2, s) => sum2 + (s.cost || 0), 0);
106061
- return {
106062
- ...prev,
106063
- stories: newStories,
106064
- currentStory: undefined,
106065
- totalCost
106066
- };
105890
+ const totalCost = newStories.reduce((sum2, s) => sum2 + (s.cost ?? 0), 0);
105891
+ return { ...prev, stories: newStories, totalCost };
106067
105892
  });
106068
- };
106069
- const onStoryEscalate = (story) => {
105893
+ });
105894
+ const unsubFailed = pipelineEventBus.on("story:failed", (event) => {
106070
105895
  setState((prev) => ({
106071
105896
  ...prev,
106072
- stories: prev.stories.map((s) => s.story.id === story.id ? { ...s, status: "retrying" } : s)
105897
+ stories: prev.stories.map((s) => s.story.id === event.storyId ? {
105898
+ ...s,
105899
+ status: "failed",
105900
+ failureReason: event.reason
105901
+ } : s)
106073
105902
  }));
106074
- };
106075
- const onStageEnter = (stage) => {
105903
+ });
105904
+ const unsubSkipped = pipelineEventBus.on("story:skipped", (event) => {
106076
105905
  setState((prev) => ({
106077
105906
  ...prev,
106078
- currentStage: stage
105907
+ stories: prev.stories.map((s) => s.story.id === event.storyId ? { ...s, status: "skipped" } : s)
106079
105908
  }));
106080
- };
106081
- const onRunComplete = (summary) => {
105909
+ });
105910
+ const unsubEscalated = pipelineEventBus.on("story:escalated", (event) => {
105911
+ const entry = {
105912
+ storyId: event.storyId,
105913
+ fromTier: event.fromTier,
105914
+ toTier: event.toTier,
105915
+ at: Date.now()
105916
+ };
106082
105917
  setState((prev) => ({
106083
105918
  ...prev,
106084
- totalCost: summary.totalCost,
106085
- summary
105919
+ stories: prev.stories.map((s) => s.story.id === event.storyId ? { ...s, status: "retrying" } : s),
105920
+ escalationLog: [...prev.escalationLog, entry]
106086
105921
  }));
106087
- };
106088
- events.on("story:start", onStoryStart);
106089
- events.on("story:complete", onStoryComplete);
106090
- events.on("story:escalate", onStoryEscalate);
106091
- events.on("stage:enter", onStageEnter);
106092
- events.on("run:complete", onRunComplete);
105922
+ });
105923
+ const unsubStoryPaused = pipelineEventBus.on("story:paused", (event) => {
105924
+ setState((prev) => ({
105925
+ ...prev,
105926
+ stories: prev.stories.map((s) => s.story.id === event.storyId ? { ...s, status: "paused", failureReason: event.reason } : s)
105927
+ }));
105928
+ });
105929
+ const unsubPaused = pipelineEventBus.on("run:paused", (_event) => {
105930
+ setState((prev) => ({ ...prev, runPaused: true }));
105931
+ });
105932
+ const unsubResumed = pipelineEventBus.on("run:resumed", (_event) => {
105933
+ setState((prev) => ({ ...prev, runPaused: false }));
105934
+ });
105935
+ const unsubCompleted2 = pipelineEventBus.on("run:completed", (event) => {
105936
+ clearInterval(timer);
105937
+ const summary = {
105938
+ totalStories: event.totalStories,
105939
+ passedStories: event.passedStories,
105940
+ failedStories: event.failedStories,
105941
+ skippedStories: event.skippedStories,
105942
+ pausedStories: event.pausedStories,
105943
+ durationMs: event.durationMs,
105944
+ totalCost: event.totalCost
105945
+ };
105946
+ setState((prev) => ({
105947
+ ...prev,
105948
+ elapsedMs: event.durationMs,
105949
+ runSummary: summary,
105950
+ totalCost: event.totalCost ?? prev.totalCost
105951
+ }));
105952
+ });
105953
+ const unsubErrored = pipelineEventBus.on("run:errored", (_event) => {
105954
+ setState((prev) => ({ ...prev, runErrored: true }));
105955
+ });
106093
105956
  return () => {
106094
- stopTimer();
106095
- events.off("story:start", onStoryStart);
106096
- events.off("story:complete", onStoryComplete);
106097
- events.off("story:escalate", onStoryEscalate);
106098
- events.off("stage:enter", onStageEnter);
106099
- events.off("run:complete", onRunComplete);
105957
+ clearInterval(timer);
105958
+ unsubStarted();
105959
+ unsubCompleted();
105960
+ unsubFailed();
105961
+ unsubSkipped();
105962
+ unsubEscalated();
105963
+ unsubStoryPaused();
105964
+ unsubPaused();
105965
+ unsubResumed();
105966
+ unsubCompleted2();
105967
+ unsubErrored();
106100
105968
  };
106101
- }, [events]);
105969
+ }, []);
106102
105970
  return state;
106103
105971
  }
106104
105972
 
106105
- // src/tui/hooks/usePty.ts
105973
+ // src/tui/hooks/usePipelineEvents.ts
106106
105974
  var import_react34 = __toESM(require_react(), 1);
105975
+ function usePipelineEvents(events) {
105976
+ const [currentStage, setCurrentStage] = import_react34.useState(undefined);
105977
+ import_react34.useEffect(() => {
105978
+ const onStageEnter = (stage) => setCurrentStage(stage);
105979
+ events.on("stage:enter", onStageEnter);
105980
+ return () => events.off("stage:enter", onStageEnter);
105981
+ }, [events]);
105982
+ return { currentStage };
105983
+ }
105984
+
105985
+ // src/tui/hooks/usePty.ts
105986
+ var import_react35 = __toESM(require_react(), 1);
106107
105987
  var MAX_PTY_BUFFER_LINES = 500;
106108
105988
  var MAX_LINE_LENGTH = 1e4;
106109
105989
  var PTY_FLUSH_INTERVAL_MS = 100;
106110
105990
  function usePty(options) {
106111
- const [state, setState] = import_react34.useState(() => ({
105991
+ const [state, setState] = import_react35.useState(() => ({
106112
105992
  outputLines: [],
106113
105993
  isRunning: false
106114
105994
  }));
106115
- const [handle, setHandle] = import_react34.useState(null);
105995
+ const [handle, setHandle] = import_react35.useState(null);
106116
105996
  const command = options?.command;
106117
105997
  const argsJson = JSON.stringify(options?.args);
106118
105998
  const cwd2 = options?.cwd;
106119
105999
  const envJson = JSON.stringify(options?.env);
106120
- import_react34.useEffect(() => {
106000
+ import_react35.useEffect(() => {
106121
106001
  if (!command) {
106122
106002
  return;
106123
106003
  }
@@ -106186,8 +106066,8 @@ function usePty(options) {
106186
106066
  proc.kill();
106187
106067
  };
106188
106068
  }, [command, argsJson, cwd2, envJson]);
106189
- const handleResize = import_react34.useCallback((_cols, _rows) => {}, []);
106190
- import_react34.useEffect(() => {
106069
+ const handleResize = import_react35.useCallback((_cols, _rows) => {}, []);
106070
+ import_react35.useEffect(() => {
106191
106071
  const onResize = () => {
106192
106072
  const cols = process.stdout.columns ?? 80;
106193
106073
  const rows = process.stdout.rows ?? 24;
@@ -106203,6 +106083,14 @@ function usePty(options) {
106203
106083
 
106204
106084
  // src/tui/App.tsx
106205
106085
  var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
106086
+ function formatElapsed(ms) {
106087
+ const mins = Math.floor(ms / 60000);
106088
+ const secs = Math.floor(ms % 60000 / 1000);
106089
+ return `${mins}m ${secs}s`;
106090
+ }
106091
+ function formatCost3(cost) {
106092
+ return `$${cost.toFixed(4)}`;
106093
+ }
106206
106094
  function App2({
106207
106095
  feature,
106208
106096
  stories: initialStories,
@@ -106212,15 +106100,21 @@ function App2({
106212
106100
  agentStreamEvents
106213
106101
  }) {
106214
106102
  const layout = useLayout();
106215
- const state = usePipelineEvents(events, initialStories.map((s) => s.story));
106103
+ const busState = usePipelineBusEvents(initialStories);
106104
+ const { currentStage } = usePipelineEvents(events);
106216
106105
  const { exit } = use_app_default();
106217
- const [focus, setFocus] = import_react35.useState("stories" /* Stories */);
106218
- const [showHelp, setShowHelp] = import_react35.useState(false);
106219
- const [showCost, setShowCost] = import_react35.useState(false);
106220
- const [showQuitConfirm, setShowQuitConfirm] = import_react35.useState(false);
106221
- const [showAbortConfirm, setShowAbortConfirm] = import_react35.useState(false);
106222
- const { outputLines: agentOutputLines, handle: ptyHandle } = usePty(ptyOptions ?? null);
106106
+ const [focus, setFocus] = import_react36.useState("stories" /* Stories */);
106107
+ const [showHelp, setShowHelp] = import_react36.useState(false);
106108
+ const [showCost, setShowCost] = import_react36.useState(false);
106109
+ const [showQuitConfirm, setShowQuitConfirm] = import_react36.useState(false);
106110
+ const [showAbortConfirm, setShowAbortConfirm] = import_react36.useState(false);
106111
+ const { handle: ptyHandle } = usePty(ptyOptions ?? null);
106223
106112
  const { activeCalls } = useAgentStreamEvents(agentStreamEvents);
106113
+ const isRunComplete = !!busState.runSummary;
106114
+ const runningStories = busState.stories.filter((s) => s.status === "running");
106115
+ const isParallel = runningStories.length > 1;
106116
+ const currentRunningStory = runningStories[0];
106117
+ const runErroredForPanel = busState.runErrored ? "Run encountered an error" : undefined;
106224
106118
  const handleKeyboardAction = async (action) => {
106225
106119
  switch (action.type) {
106226
106120
  case "TOGGLE_FOCUS":
@@ -106242,7 +106136,7 @@ function App2({
106242
106136
  setShowAbortConfirm(false);
106243
106137
  break;
106244
106138
  case "QUIT":
106245
- if (state.currentStory) {
106139
+ if (currentRunningStory) {
106246
106140
  setShowQuitConfirm(true);
106247
106141
  } else {
106248
106142
  exit();
@@ -106254,7 +106148,7 @@ function App2({
106254
106148
  }
106255
106149
  break;
106256
106150
  case "ABORT":
106257
- if (state.currentStory) {
106151
+ if (currentRunningStory) {
106258
106152
  setShowAbortConfirm(true);
106259
106153
  } else if (queueFilePath) {
106260
106154
  await writeQueueCommand(queueFilePath, { type: "ABORT" });
@@ -106296,12 +106190,18 @@ function App2({
106296
106190
  });
106297
106191
  useKeyboard({
106298
106192
  focus,
106299
- currentStory: state.currentStory,
106193
+ currentStory: currentRunningStory?.story,
106300
106194
  onAction: handleKeyboardAction,
106301
106195
  disabled: showQuitConfirm || showAbortConfirm
106302
106196
  });
106303
- const currentRouting = state.currentStory?.routing;
106304
106197
  const isTooSmall = layout.width < MIN_TERMINAL_WIDTH;
106198
+ const activeCount = runningStories.length;
106199
+ const headerRight = [
106200
+ activeCount > 0 ? `${activeCount} running` : null,
106201
+ formatCost3(busState.totalCost),
106202
+ formatElapsed(busState.elapsedMs)
106203
+ ].filter(Boolean).join(" \xB7 ");
106204
+ const maxHeight = layout.mode === "single" ? COMPACT_MAX_VISIBLE_STORIES : MAX_VISIBLE_STORIES;
106305
106205
  return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
106306
106206
  flexDirection: "column",
106307
106207
  height: "100%",
@@ -106311,22 +106211,29 @@ function App2({
106311
106211
  borderStyle: "single",
106312
106212
  borderBottom: true,
106313
106213
  borderColor: "cyan",
106314
- children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
106315
- bold: true,
106316
- color: "cyan",
106317
- children: [
106318
- "nax run \u2014 ",
106319
- feature
106320
- ]
106321
- }, undefined, true, undefined, this)
106322
- }, undefined, false, undefined, this),
106214
+ justifyContent: "space-between",
106215
+ children: [
106216
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
106217
+ bold: true,
106218
+ color: "cyan",
106219
+ children: [
106220
+ "nax run \u2014 ",
106221
+ feature
106222
+ ]
106223
+ }, undefined, true, undefined, this),
106224
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
106225
+ dimColor: true,
106226
+ children: headerRight
106227
+ }, undefined, false, undefined, this)
106228
+ ]
106229
+ }, undefined, true, undefined, this),
106323
106230
  isTooSmall && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
106324
106231
  paddingX: 1,
106325
106232
  backgroundColor: "yellow",
106326
106233
  children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
106327
106234
  color: "black",
106328
106235
  children: [
106329
- "\u26A0\uFE0F Terminal too narrow (",
106236
+ "Terminal too narrow (",
106330
106237
  layout.width,
106331
106238
  " cols). Minimum ",
106332
106239
  MIN_TERMINAL_WIDTH,
@@ -106339,33 +106246,36 @@ function App2({
106339
106246
  flexGrow: 1,
106340
106247
  children: [
106341
106248
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(StoriesPanel, {
106342
- stories: state.stories,
106343
- totalCost: state.totalCost,
106344
- elapsedMs: state.elapsedMs,
106249
+ stories: busState.stories,
106345
106250
  width: layout.mode === "single" ? layout.width : layout.storiesPanelWidth,
106346
106251
  compact: layout.mode === "single",
106347
- maxHeight: layout.mode === "single" ? 10 : undefined
106252
+ maxHeight
106348
106253
  }, undefined, false, undefined, this),
106349
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(AgentPanel, {
106254
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(LiveActivityPanel, {
106350
106255
  focused: focus === "agent" /* Agent */,
106351
- outputLines: agentOutputLines,
106352
- activeCalls
106256
+ activeCalls,
106257
+ runSummary: busState.runSummary,
106258
+ runErrored: runErroredForPanel,
106259
+ escalationLog: busState.escalationLog
106353
106260
  }, undefined, false, undefined, this)
106354
106261
  ]
106355
106262
  }, undefined, true, undefined, this),
106356
106263
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(StatusBar, {
106357
- currentStory: state.currentStory,
106358
- currentStage: state.currentStage,
106359
- modelTier: currentRouting?.modelTier,
106360
- testStrategy: currentRouting?.testStrategy
106264
+ currentStage,
106265
+ currentStoryId: currentRunningStory?.story.id,
106266
+ modelTier: currentRunningStory?.modelTier,
106267
+ runPaused: busState.runPaused,
106268
+ runComplete: isRunComplete,
106269
+ isParallel,
106270
+ activeCount
106361
106271
  }, undefined, false, undefined, this),
106362
106272
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(HelpOverlay, {
106363
106273
  visible: showHelp
106364
106274
  }, undefined, false, undefined, this),
106365
106275
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(CostOverlay, {
106366
106276
  visible: showCost,
106367
- stories: state.stories,
106368
- totalCost: state.totalCost
106277
+ stories: busState.stories,
106278
+ totalCost: busState.totalCost
106369
106279
  }, undefined, false, undefined, this),
106370
106280
  showQuitConfirm && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
106371
106281
  position: "absolute",
@@ -106383,7 +106293,7 @@ function App2({
106383
106293
  children: [
106384
106294
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
106385
106295
  color: "yellow",
106386
- children: "\u26A0\uFE0F Story is running. Quit anyway?"
106296
+ children: "Story is running. Quit anyway?"
106387
106297
  }, undefined, false, undefined, this),
106388
106298
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
106389
106299
  paddingTop: 1,
@@ -106423,7 +106333,7 @@ function App2({
106423
106333
  children: [
106424
106334
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
106425
106335
  color: "red",
106426
- children: "\u26A0\uFE0F Story is running. Abort anyway?"
106336
+ children: "Story is running. Abort anyway?"
106427
106337
  }, undefined, false, undefined, this),
106428
106338
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
106429
106339
  paddingTop: 1,
@@ -106512,8 +106422,8 @@ Next: nax generate --package ${options.package}`));
106512
106422
  }
106513
106423
  return;
106514
106424
  }
106515
- const naxDir = join84(workdir, ".nax");
106516
- if (existsSync37(naxDir) && !options.force) {
106425
+ const naxDir = join83(workdir, ".nax");
106426
+ if (existsSync36(naxDir) && !options.force) {
106517
106427
  console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
106518
106428
  return;
106519
106429
  }
@@ -106541,11 +106451,11 @@ Next: nax generate --package ${options.package}`));
106541
106451
  }
106542
106452
  }
106543
106453
  }
106544
- mkdirSync7(join84(naxDir, "features"), { recursive: true });
106545
- mkdirSync7(join84(naxDir, "hooks"), { recursive: true });
106454
+ mkdirSync7(join83(naxDir, "features"), { recursive: true });
106455
+ mkdirSync7(join83(naxDir, "hooks"), { recursive: true });
106546
106456
  const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
106547
- await Bun.write(join84(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106548
- await Bun.write(join84(naxDir, "hooks.json"), JSON.stringify({
106457
+ await Bun.write(join83(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
106458
+ await Bun.write(join83(naxDir, "hooks.json"), JSON.stringify({
106549
106459
  hooks: {
106550
106460
  "on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
106551
106461
  "on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
@@ -106553,12 +106463,12 @@ Next: nax generate --package ${options.package}`));
106553
106463
  "on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
106554
106464
  }
106555
106465
  }, null, 2));
106556
- await Bun.write(join84(naxDir, ".gitignore"), `# nax temp files
106466
+ await Bun.write(join83(naxDir, ".gitignore"), `# nax temp files
106557
106467
  *.tmp
106558
106468
  .paused.json
106559
106469
  .nax-verifier-verdict.json
106560
106470
  `);
106561
- await Bun.write(join84(naxDir, "context.md"), `# Project Context
106471
+ await Bun.write(join83(naxDir, "context.md"), `# Project Context
106562
106472
 
106563
106473
  This document defines coding standards, architectural decisions, and forbidden patterns for this project.
106564
106474
  Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
@@ -106655,7 +106565,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106655
106565
  console.error(source_default.red("Error: --plan requires --from <spec-path>"));
106656
106566
  process.exit(1);
106657
106567
  }
106658
- if (options.from && !existsSync37(options.from)) {
106568
+ if (options.from && !existsSync36(options.from)) {
106659
106569
  console.error(source_default.red(`Error: File not found: ${options.from} (required with --plan)`));
106660
106570
  process.exit(1);
106661
106571
  }
@@ -106688,10 +106598,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106688
106598
  console.error(source_default.red("nax not initialized. Run: nax init"));
106689
106599
  process.exit(1);
106690
106600
  }
106691
- const featureDir = join84(naxDir, "features", options.feature);
106692
- const prdPath = join84(featureDir, "prd.json");
106601
+ const featureDir = join83(naxDir, "features", options.feature);
106602
+ const prdPath = join83(featureDir, "prd.json");
106693
106603
  if (options.plan && options.from) {
106694
- if (existsSync37(prdPath) && !options.force) {
106604
+ if (existsSync36(prdPath) && !options.force) {
106695
106605
  console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
106696
106606
  console.error(source_default.dim(" Use --force to overwrite, or run without --plan to use the existing PRD."));
106697
106607
  process.exit(1);
@@ -106711,10 +106621,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
106711
106621
  }
106712
106622
  }
106713
106623
  try {
106714
- const planLogDir = join84(featureDir, "plan");
106624
+ const planLogDir = join83(featureDir, "plan");
106715
106625
  mkdirSync7(planLogDir, { recursive: true });
106716
106626
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106717
- const planLogPath = join84(planLogDir, `${planLogId}.jsonl`);
106627
+ const planLogPath = join83(planLogDir, `${planLogId}.jsonl`);
106718
106628
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
106719
106629
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
106720
106630
  console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
@@ -106753,17 +106663,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
106753
106663
  process.exit(1);
106754
106664
  }
106755
106665
  }
106756
- if (!existsSync37(prdPath)) {
106666
+ if (!existsSync36(prdPath)) {
106757
106667
  console.error(source_default.red(`Feature "${options.feature}" not found or missing prd.json`));
106758
106668
  process.exit(1);
106759
106669
  }
106760
106670
  resetLogger();
106761
- const projectKey = config2.name?.trim() || basename17(workdir);
106671
+ const projectKey = config2.name?.trim() || basename16(workdir);
106762
106672
  const outputDir = projectOutputDir(projectKey, config2.outputDir);
106763
- const runsDir = join84(outputDir, "features", options.feature, "runs");
106673
+ const runsDir = join83(outputDir, "features", options.feature, "runs");
106764
106674
  mkdirSync7(runsDir, { recursive: true });
106765
106675
  const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
106766
- const logFilePath = join84(runsDir, `${runId}.jsonl`);
106676
+ const logFilePath = join83(runsDir, `${runId}.jsonl`);
106767
106677
  const isTTY = process.stdout.isTTY ?? false;
106768
106678
  const headlessFlag = options.headless ?? false;
106769
106679
  const headlessEnv = process.env.NAX_HEADLESS === "1";
@@ -106780,7 +106690,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
106780
106690
  config2.agent.default = options.agent;
106781
106691
  }
106782
106692
  config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
106783
- const globalNaxDir = join84(homedir3(), ".nax");
106693
+ const globalNaxDir = join83(homedir3(), ".nax");
106784
106694
  const hooks = await loadHooksConfig(naxDir, globalNaxDir);
106785
106695
  const eventEmitter = new PipelineEventEmitter;
106786
106696
  let tuiInstance;
@@ -106795,15 +106705,13 @@ program2.command("run").description("Run the orchestration loop for a feature").
106795
106705
  tuiInstance = renderTui({
106796
106706
  feature: options.feature,
106797
106707
  stories: initialStories,
106798
- totalCost: 0,
106799
- elapsedMs: 0,
106800
106708
  events: eventEmitter,
106801
106709
  ptyOptions: null
106802
106710
  });
106803
106711
  } else {
106804
106712
  console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
106805
106713
  }
106806
- const statusFilePath = join84(outputDir, "status.json");
106714
+ const statusFilePath = join83(outputDir, "status.json");
106807
106715
  let parallel;
106808
106716
  if (options.parallel !== undefined) {
106809
106717
  parallel = Number.parseInt(options.parallel, 10);
@@ -106829,9 +106737,9 @@ program2.command("run").description("Run the orchestration loop for a feature").
106829
106737
  headless: useHeadless,
106830
106738
  skipPrecheck: options.skipPrecheck ?? false
106831
106739
  });
106832
- const latestSymlink = join84(runsDir, "latest.jsonl");
106740
+ const latestSymlink = join83(runsDir, "latest.jsonl");
106833
106741
  try {
106834
- if (existsSync37(latestSymlink)) {
106742
+ if (existsSync36(latestSymlink)) {
106835
106743
  Bun.spawnSync(["rm", latestSymlink]);
106836
106744
  }
106837
106745
  Bun.spawnSync(["ln", "-s", `${runId}.jsonl`, latestSymlink], {
@@ -106890,9 +106798,9 @@ features.command("create <name>").description("Create a new feature").option("-d
106890
106798
  console.error(source_default.red("nax not initialized. Run: nax init"));
106891
106799
  process.exit(1);
106892
106800
  }
106893
- const featureDir = join84(naxDir, "features", name);
106801
+ const featureDir = join83(naxDir, "features", name);
106894
106802
  mkdirSync7(featureDir, { recursive: true });
106895
- await Bun.write(join84(featureDir, "spec.md"), `# Feature: ${name}
106803
+ await Bun.write(join83(featureDir, "spec.md"), `# Feature: ${name}
106896
106804
 
106897
106805
  ## Overview
106898
106806
 
@@ -106925,7 +106833,7 @@ features.command("create <name>").description("Create a new feature").option("-d
106925
106833
 
106926
106834
  <!-- What this feature explicitly does NOT cover. -->
106927
106835
  `);
106928
- await Bun.write(join84(featureDir, "progress.txt"), `# Progress: ${name}
106836
+ await Bun.write(join83(featureDir, "progress.txt"), `# Progress: ${name}
106929
106837
 
106930
106838
  Created: ${new Date().toISOString()}
106931
106839
 
@@ -106951,13 +106859,13 @@ features.command("list").description("List all features").option("-d, --dir <pat
106951
106859
  console.error(source_default.red("nax not initialized."));
106952
106860
  process.exit(1);
106953
106861
  }
106954
- const featuresDir = join84(naxDir, "features");
106955
- if (!existsSync37(featuresDir)) {
106862
+ const featuresDir = join83(naxDir, "features");
106863
+ if (!existsSync36(featuresDir)) {
106956
106864
  console.log(source_default.dim("No features yet."));
106957
106865
  return;
106958
106866
  }
106959
- const { readdirSync: readdirSync10 } = await import("fs");
106960
- const entries = readdirSync10(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
106867
+ const { readdirSync: readdirSync9 } = await import("fs");
106868
+ const entries = readdirSync9(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
106961
106869
  if (entries.length === 0) {
106962
106870
  console.log(source_default.dim("No features yet."));
106963
106871
  return;
@@ -106966,8 +106874,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
106966
106874
  Features:
106967
106875
  `));
106968
106876
  for (const name of entries) {
106969
- const prdPath = join84(featuresDir, name, "prd.json");
106970
- if (existsSync37(prdPath)) {
106877
+ const prdPath = join83(featuresDir, name, "prd.json");
106878
+ if (existsSync36(prdPath)) {
106971
106879
  const prd = await loadPRD(prdPath);
106972
106880
  const c = countStories(prd);
106973
106881
  console.log(` ${name} \u2014 ${c.passed}/${c.total} stories done`);
@@ -107001,10 +106909,10 @@ Use: nax plan -f <feature> --from <spec>`));
107001
106909
  cliOverrides.profile = options.profile;
107002
106910
  }
107003
106911
  const config2 = await loadConfig(workdir, cliOverrides);
107004
- const featureLogDir = join84(naxDir, "features", options.feature, "plan");
106912
+ const featureLogDir = join83(naxDir, "features", options.feature, "plan");
107005
106913
  mkdirSync7(featureLogDir, { recursive: true });
107006
106914
  const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
107007
- const planLogPath = join84(featureLogDir, `${planLogId}.jsonl`);
106915
+ const planLogPath = join83(featureLogDir, `${planLogId}.jsonl`);
107008
106916
  initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
107009
106917
  console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
107010
106918
  try {
@@ -107169,19 +107077,6 @@ program2.command("logs").description("Display run logs with filtering and follow
107169
107077
  process.exit(1);
107170
107078
  }
107171
107079
  });
107172
- program2.command("diagnose").description("Diagnose run failures and generate recommendations").option("-f, --feature <name>", "Feature name (defaults to current feature)").option("-d, --dir <path>", "Working directory", process.cwd()).option("--json", "Output machine-readable JSON", false).option("--verbose", "Verbose output with story breakdown", false).action(async (options) => {
107173
- try {
107174
- await diagnose({
107175
- feature: options.feature,
107176
- workdir: options.dir,
107177
- json: options.json,
107178
- verbose: options.verbose
107179
- });
107180
- } catch (err) {
107181
- console.error(source_default.red(`Error: ${err.message}`));
107182
- process.exit(1);
107183
- }
107184
- });
107185
107080
  program2.command("precheck").description("Validate feature readiness before execution").option("-f, --feature <name>", "Feature name").option("-d, --dir <path>", "Project directory", process.cwd()).option("--json", "Output machine-readable JSON", false).option("--light", "Environment-only check \u2014 skips PRD validation (use before nax plan)", false).action(async (options) => {
107186
107081
  try {
107187
107082
  await precheckCommand({