@nathapp/nax 0.73.2 → 0.73.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +398 -94
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -24524,16 +24524,24 @@ function buildTestFrameworkHint(testCommand) {
24524
24524
  return "Use Jest (describe/test/expect)";
24525
24525
  return "Use your project's test framework";
24526
24526
  }
24527
+ function stripAnsi(output) {
24528
+ return output.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, "");
24529
+ }
24527
24530
  function detectFramework(output) {
24528
- if (/^\s*Test Files\s+\d+/m.test(output))
24531
+ const clean = stripAnsi(output);
24532
+ if (/^\s*Test Files\s+\d+/m.test(clean))
24529
24533
  return "vitest";
24530
- if (/^\s*Tests:\s+\d+/m.test(output))
24534
+ if (/^\s*Tests:\s+\d+/m.test(clean))
24531
24535
  return "jest";
24532
- if (/={3,}\s+\d+\s+(?:failed|passed).*in\s+[\d.]+s\s*={3,}/m.test(output))
24536
+ if (/={3,}\s+\d+\s+(?:failed|passed).*in\s+[\d.]+s\s*={3,}/m.test(clean))
24533
24537
  return "pytest";
24534
- if (/^--- (?:FAIL|PASS):/m.test(output) || /^(?:ok|FAIL)\s+\t/m.test(output))
24538
+ if (/^--- (?:FAIL|PASS):/m.test(clean) || /^(?:ok|FAIL)\s+\t/m.test(clean))
24535
24539
  return "go";
24536
- if (/^\(fail\)\s/m.test(output) || /^bun test/m.test(output) || /[\u2713\u2714\u2717\u2718]/m.test(output))
24540
+ if (/^test result:\s+(?:ok|FAILED)\./m.test(clean) || /panicked at /.test(clean))
24541
+ return "rust";
24542
+ if (/^\s*\d+\s+passing\b/m.test(clean))
24543
+ return "mocha";
24544
+ if (/^\(fail\)\s/m.test(clean) || /^bun test/m.test(clean) || /[\u2713\u2714\u2717\u2718]/m.test(clean))
24537
24545
  return "bun";
24538
24546
  return "unknown";
24539
24547
  }
@@ -24670,21 +24678,125 @@ var init_resolver = __esm(() => {
24670
24678
  };
24671
24679
  });
24672
24680
 
24681
+ // src/test-runners/parse-mocha.ts
24682
+ function parseMochaOutput(output) {
24683
+ return {
24684
+ passed: matchCount(output, /(\d+)\s+passing\b/),
24685
+ failed: matchCount(output, /(\d+)\s+failing\b/),
24686
+ failures: extractMochaFailures(output)
24687
+ };
24688
+ }
24689
+ function matchCount(output, re) {
24690
+ const m = output.match(re);
24691
+ return m ? Number.parseInt(m[1], 10) : 0;
24692
+ }
24693
+ function extractMochaFailures(output) {
24694
+ const failingIdx = output.search(/^\s*\d+\s+failing\b/m);
24695
+ if (failingIdx === -1)
24696
+ return [];
24697
+ const lines = output.slice(failingIdx).split(`
24698
+ `);
24699
+ const failures = [];
24700
+ for (let i = 1;i < lines.length; i++) {
24701
+ const header = lines[i].match(/^\s*(\d+)\)\s+(.+)$/);
24702
+ if (!header)
24703
+ continue;
24704
+ const testName = header[2].replace(/:\s*$/, "").trim();
24705
+ let file3 = "unknown";
24706
+ let error48 = "";
24707
+ const stackTrace = [];
24708
+ for (let j = i + 1;j < lines.length; j++) {
24709
+ const line = lines[j];
24710
+ if (/^\s*\d+\)\s+/.test(line))
24711
+ break;
24712
+ const at = line.match(/at\s+.*\(([^)]+):(\d+):(\d+)\)/);
24713
+ if (at) {
24714
+ if (file3 === "unknown")
24715
+ file3 = at[1];
24716
+ if (stackTrace.length < MAX_STACK_LINES)
24717
+ stackTrace.push(`${at[1]}:${at[2]}`);
24718
+ continue;
24719
+ }
24720
+ if (!error48 && line.trim())
24721
+ error48 = line.trim();
24722
+ }
24723
+ failures.push({ file: file3, testName, error: error48 || "Unknown error", stackTrace });
24724
+ }
24725
+ return failures;
24726
+ }
24727
+ var MAX_STACK_LINES = 5;
24728
+
24729
+ // src/test-runners/parse-rust.ts
24730
+ function parseRustTestOutput(output) {
24731
+ let passed = 0;
24732
+ let failed = 0;
24733
+ for (const m of output.matchAll(RESULT_LINE_RE)) {
24734
+ passed += Number.parseInt(m[1], 10);
24735
+ failed += Number.parseInt(m[2], 10);
24736
+ }
24737
+ return { passed, failed, failures: extractRustFailures(output) };
24738
+ }
24739
+ function extractRustFailures(output) {
24740
+ const lines = output.split(`
24741
+ `);
24742
+ const failures = [];
24743
+ for (let i = 0;i < lines.length; i++) {
24744
+ const header = lines[i].match(/^---- (\S+) stdout ----$/);
24745
+ if (!header)
24746
+ continue;
24747
+ const testName = header[1];
24748
+ let file3 = "unknown";
24749
+ let error48 = "";
24750
+ const stackTrace = [];
24751
+ for (let j = i + 1;j < lines.length; j++) {
24752
+ const line = lines[j];
24753
+ if (/^---- \S+ stdout ----$/.test(line) || /^test result:/.test(line))
24754
+ break;
24755
+ const panic = line.match(/panicked at ([^:\n]+):(\d+):(?:\d+):/);
24756
+ if (panic) {
24757
+ file3 = panic[1];
24758
+ if (stackTrace.length < MAX_STACK_LINES2)
24759
+ stackTrace.push(`${panic[1]}:${panic[2]}`);
24760
+ continue;
24761
+ }
24762
+ if (!error48 && line.trim() && !line.startsWith("note:"))
24763
+ error48 = line.trim();
24764
+ }
24765
+ failures.push({ file: file3, testName, error: error48 || "Unknown error", stackTrace });
24766
+ }
24767
+ if (failures.length > 0)
24768
+ return failures;
24769
+ for (const m of output.matchAll(FAILED_TEST_LINE_RE)) {
24770
+ failures.push({ file: "unknown", testName: m[1], error: "Unknown error", stackTrace: [] });
24771
+ }
24772
+ return failures;
24773
+ }
24774
+ var RESULT_LINE_RE, FAILED_TEST_LINE_RE, MAX_STACK_LINES2 = 5;
24775
+ var init_parse_rust = __esm(() => {
24776
+ RESULT_LINE_RE = /^test result:\s+(?:ok|FAILED)\.\s+(\d+)\s+passed;\s+(\d+)\s+failed;/gm;
24777
+ FAILED_TEST_LINE_RE = /^test (\S+) \.\.\. FAILED$/gm;
24778
+ });
24779
+
24673
24780
  // src/test-runners/parser.ts
24674
24781
  function parseTestOutput(output) {
24675
- const framework = detectFramework(output);
24782
+ const clean = stripAnsi(output);
24783
+ const framework = detectFramework(clean);
24676
24784
  switch (framework) {
24677
24785
  case "bun":
24678
- return parseBunOutput(output);
24786
+ return parseBunOutput(clean);
24679
24787
  case "jest":
24680
24788
  case "vitest":
24681
- return parseJestOutput(output);
24789
+ return parseJestOutput(clean);
24682
24790
  case "pytest":
24683
- return parsePytestOutput(output);
24791
+ return parsePytestOutput(clean);
24684
24792
  case "go":
24685
- return parseGoTestOutput(output);
24793
+ return parseGoTestOutput(clean);
24794
+ case "rust":
24795
+ return parseRustTestOutput(clean);
24796
+ case "mocha":
24797
+ return parseMochaOutput(clean);
24686
24798
  default:
24687
- return parseCommonOutput(output);
24799
+ return parseCommonOutput(clean);
24688
24800
  }
24689
24801
  }
24690
24802
  function parseBunTestOutput(output) {
@@ -25014,11 +25126,12 @@ function analyzeTestExitCode(output, exitCode) {
25014
25126
  }
25015
25127
  var init_parser = __esm(() => {
25016
25128
  init_detector2();
25129
+ init_parse_rust();
25017
25130
  });
25018
25131
 
25019
25132
  // src/test-runners/ac-parser.ts
25020
25133
  function parseTestFailures(output) {
25021
- const clean = output.replace(ANSI_ESCAPE_PATTERN, "");
25134
+ const clean = stripAnsi(output);
25022
25135
  const framework = detectFramework(clean);
25023
25136
  const failedACs = [];
25024
25137
  const lines = clean.split(`
@@ -25072,10 +25185,8 @@ function parseTestFailures(output) {
25072
25185
  }
25073
25186
  return failedACs;
25074
25187
  }
25075
- var ANSI_ESCAPE_PATTERN;
25076
25188
  var init_ac_parser = __esm(() => {
25077
25189
  init_detector2();
25078
- ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[A-Za-z]`, "g");
25079
25190
  });
25080
25191
 
25081
25192
  // src/utils/git.ts
@@ -25756,6 +25867,8 @@ __export(exports_test_runners, {
25756
25867
  resolveReviewExcludePatterns: () => resolveReviewExcludePatterns,
25757
25868
  parseTestOutput: () => parseTestOutput,
25758
25869
  parseTestFailures: () => parseTestFailures,
25870
+ parseRustTestOutput: () => parseRustTestOutput,
25871
+ parseMochaOutput: () => parseMochaOutput,
25759
25872
  parseBunTestOutput: () => parseBunTestOutput,
25760
25873
  isTestFileByPatterns: () => isTestFileByPatterns,
25761
25874
  isTestFile: () => isTestFile,
@@ -25787,6 +25900,7 @@ var init_test_runners = __esm(() => {
25787
25900
  init_detector2();
25788
25901
  init_resolver();
25789
25902
  init_parser();
25903
+ init_parse_rust();
25790
25904
  init_workspace();
25791
25905
  init_ac_parser();
25792
25906
  init_scoped_selection();
@@ -35651,7 +35765,7 @@ var init_semantic_review = __esm(() => {
35651
35765
  const substantiated = await substantiateSemanticEvidence(sanitized, input.mode, input.workdir, input.story.id, threshold, input.repoRoot);
35652
35766
  const { accepted, dropped } = filterByAcGroundingMinimal(substantiated, input.story.acceptanceCriteria);
35653
35767
  const blocking = accepted.filter((f) => isBlockingSeverity(f.severity, threshold));
35654
- const passed = parsed.passed && blocking.length === 0;
35768
+ const passed = blocking.length === 0 && (parsed.passed || accepted.length > 0);
35655
35769
  return {
35656
35770
  ...parsed,
35657
35771
  passed,
@@ -40454,27 +40568,37 @@ function flipWithPairs(pairs, snippet) {
40454
40568
  }
40455
40569
  return results;
40456
40570
  }
40457
- function applyBooleanFlip(snippet) {
40458
- const seen = new Set;
40459
- const results = [];
40460
- if (/\btrue\b/.test(snippet)) {
40461
- const produced = snippet.replace(/\btrue\b/g, "false");
40462
- seen.add(produced);
40463
- results.push(produced);
40464
- }
40465
- if (/\bfalse\b/.test(snippet)) {
40466
- const produced = snippet.replace(/\bfalse\b/g, "true");
40467
- if (!seen.has(produced))
40571
+ function makeBooleanFlip(trueLit, falseLit) {
40572
+ return (snippet) => {
40573
+ const seen = new Set;
40574
+ const results = [];
40575
+ if (new RegExp(`\\b${trueLit}\\b`).test(snippet)) {
40576
+ const produced = snippet.replace(new RegExp(`\\b${trueLit}\\b`, "g"), falseLit);
40577
+ seen.add(produced);
40468
40578
  results.push(produced);
40469
- }
40470
- return results;
40579
+ }
40580
+ if (new RegExp(`\\b${falseLit}\\b`).test(snippet)) {
40581
+ const produced = snippet.replace(new RegExp(`\\b${falseLit}\\b`, "g"), trueLit);
40582
+ if (!seen.has(produced))
40583
+ results.push(produced);
40584
+ }
40585
+ return results;
40586
+ };
40587
+ }
40588
+ function makeOperators(prefix, trueLit, falseLit) {
40589
+ return [
40590
+ { id: `${prefix}:cmp-flip`, apply: (snippet) => flipWithPairs(UNIVERSAL_COMPARISON_PAIRS, snippet) },
40591
+ { id: `${prefix}:cmp-bracket-flip`, apply: applyComparisonBracketFlip },
40592
+ { id: `${prefix}:bool-flip`, apply: makeBooleanFlip(trueLit, falseLit) },
40593
+ { id: `${prefix}:arith-flip`, apply: (snippet) => flipWithPairs(ARITHMETIC_PAIRS, snippet) }
40594
+ ];
40471
40595
  }
40472
40596
  function getOperatorsForLanguage(language) {
40473
40597
  if (!language)
40474
40598
  return [];
40475
40599
  return SUPPORTED_LANGUAGES.get(language) ?? [];
40476
40600
  }
40477
- var TS_COMPARISON_PAIRS, COMPARISON_GT, COMPARISON_LT, TS_COMPARISON_BRACKET_FLIP_ID = "ts:cmp-bracket-flip", TS_COMPARISON_FLIP_ID = "ts:cmp-flip", TS_BOOLEAN_FLIP_ID = "ts:bool-flip", TS_ARITHMETIC_PAIRS, TS_ARITHMETIC_FLIP_ID = "ts:arith-flip", TYPESCRIPT_OPERATORS, PYTHON_OPERATORS, GO_OPERATORS, SUPPORTED_LANGUAGES;
40601
+ var TS_COMPARISON_PAIRS, UNIVERSAL_COMPARISON_PAIRS, COMPARISON_GT, COMPARISON_LT, ARITHMETIC_PAIRS, TYPESCRIPT_OPERATORS, PYTHON_OPERATORS, GO_OPERATORS, RUST_OPERATORS, SUPPORTED_LANGUAGES;
40478
40602
  var init_operators = __esm(() => {
40479
40603
  TS_COMPARISON_PAIRS = [
40480
40604
  [/==/g, "!="],
@@ -40484,27 +40608,35 @@ var init_operators = __esm(() => {
40484
40608
  [/>=/g, "<="],
40485
40609
  [/<=/g, ">="]
40486
40610
  ];
40611
+ UNIVERSAL_COMPARISON_PAIRS = [
40612
+ [/==/g, "!="],
40613
+ [/!=/g, "=="],
40614
+ [/>=/g, "<="],
40615
+ [/<=/g, ">="]
40616
+ ];
40487
40617
  COMPARISON_GT = /(?<=\s)>(?!=)(?=\s|$)/g;
40488
40618
  COMPARISON_LT = /(?<=\s)<(?!=)(?=\s)/g;
40489
- TS_ARITHMETIC_PAIRS = [
40619
+ ARITHMETIC_PAIRS = [
40490
40620
  [/\+/g, "-"],
40491
40621
  [/-/g, "+"],
40492
40622
  [/\*/g, "/"],
40493
40623
  [/\//g, "*"]
40494
40624
  ];
40495
40625
  TYPESCRIPT_OPERATORS = [
40496
- { id: TS_COMPARISON_FLIP_ID, apply: (snippet) => flipWithPairs(TS_COMPARISON_PAIRS, snippet) },
40497
- { id: TS_COMPARISON_BRACKET_FLIP_ID, apply: applyComparisonBracketFlip },
40498
- { id: TS_BOOLEAN_FLIP_ID, apply: applyBooleanFlip },
40499
- { id: TS_ARITHMETIC_FLIP_ID, apply: (snippet) => flipWithPairs(TS_ARITHMETIC_PAIRS, snippet) }
40626
+ { id: "ts:cmp-flip", apply: (snippet) => flipWithPairs(TS_COMPARISON_PAIRS, snippet) },
40627
+ { id: "ts:cmp-bracket-flip", apply: applyComparisonBracketFlip },
40628
+ { id: "ts:bool-flip", apply: makeBooleanFlip("true", "false") },
40629
+ { id: "ts:arith-flip", apply: (snippet) => flipWithPairs(ARITHMETIC_PAIRS, snippet) }
40500
40630
  ];
40501
- PYTHON_OPERATORS = [];
40502
- GO_OPERATORS = [];
40631
+ PYTHON_OPERATORS = makeOperators("py", "True", "False");
40632
+ GO_OPERATORS = makeOperators("go", "true", "false");
40633
+ RUST_OPERATORS = makeOperators("rust", "true", "false");
40503
40634
  SUPPORTED_LANGUAGES = new Map([
40504
40635
  ["typescript", TYPESCRIPT_OPERATORS],
40505
40636
  ["javascript", TYPESCRIPT_OPERATORS],
40506
40637
  ["python", PYTHON_OPERATORS],
40507
- ["go", GO_OPERATORS]
40638
+ ["go", GO_OPERATORS],
40639
+ ["rust", RUST_OPERATORS]
40508
40640
  ]);
40509
40641
  });
40510
40642
 
@@ -40526,7 +40658,8 @@ function generateMutants(input) {
40526
40658
  const line = lines[i];
40527
40659
  const lineNumber = i + 1;
40528
40660
  const trimmed = line.trim();
40529
- if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*"))
40661
+ const commentPrefixes = language === "python" ? ["#"] : ["//", "/*", "*"];
40662
+ if (commentPrefixes.some((prefix) => trimmed.startsWith(prefix)))
40530
40663
  continue;
40531
40664
  for (const operator of operators) {
40532
40665
  for (const replacement of applyOperator(operator, line)) {
@@ -41632,7 +41765,7 @@ var package_default;
41632
41765
  var init_package = __esm(() => {
41633
41766
  package_default = {
41634
41767
  name: "@nathapp/nax",
41635
- version: "0.73.2",
41768
+ version: "0.73.3",
41636
41769
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
41637
41770
  type: "module",
41638
41771
  bin: {
@@ -41733,8 +41866,8 @@ var init_version = __esm(() => {
41733
41866
  NAX_VERSION = package_default.version;
41734
41867
  NAX_COMMIT = (() => {
41735
41868
  try {
41736
- if (/^[0-9a-f]{6,10}$/.test("18a1906c"))
41737
- return "18a1906c";
41869
+ if (/^[0-9a-f]{6,10}$/.test("a98a211e"))
41870
+ return "a98a211e";
41738
41871
  } catch {}
41739
41872
  try {
41740
41873
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -64582,44 +64715,210 @@ var init_crash_recovery = __esm(() => {
64582
64715
  init_crash_heartbeat();
64583
64716
  });
64584
64717
 
64585
- // src/acceptance/fix-diagnosis.ts
64586
- function parseImportStatements(content) {
64587
- const importRegex = /import\s+(?:{[^}]+}|[^;]+)\s+from\s+["']([^"']+)["']/g;
64588
- const imports = [];
64589
- const regexMatch = content.matchAll(importRegex);
64590
- for (const match of regexMatch) {
64591
- imports.push(match[1]);
64718
+ // src/acceptance/import-resolution.ts
64719
+ import { resolve as resolve16, sep as sep5 } from "path";
64720
+ function languageFromExtension(testFilePath) {
64721
+ if (!testFilePath)
64722
+ return;
64723
+ const dot = testFilePath.lastIndexOf(".");
64724
+ if (dot < 0)
64725
+ return;
64726
+ return EXT_LANGUAGE.get(testFilePath.slice(dot).toLowerCase());
64727
+ }
64728
+ function isResolvedLanguage(lang) {
64729
+ return lang !== undefined && SUPPORTED.has(lang);
64730
+ }
64731
+ async function resolveLanguage(opts) {
64732
+ const explicit = opts.language ?? languageFromExtension(opts.testFilePath);
64733
+ if (isResolvedLanguage(explicit))
64734
+ return explicit;
64735
+ const detected = await detectLanguage(opts.packageDir);
64736
+ if (isResolvedLanguage(detected))
64737
+ return detected;
64738
+ return "typescript";
64739
+ }
64740
+ async function readCapped(relPath, packageDir) {
64741
+ const resolvedPackageDir = resolve16(packageDir);
64742
+ const fullPath = resolve16(resolvedPackageDir, relPath);
64743
+ if (fullPath !== resolvedPackageDir && !fullPath.startsWith(resolvedPackageDir + sep5)) {
64744
+ return null;
64745
+ }
64746
+ try {
64747
+ const text = await Bun.file(fullPath).text();
64748
+ const content = text.split(`
64749
+ `).slice(0, MAX_FILE_LINES2).join(`
64750
+ `);
64751
+ return { path: relPath, content };
64752
+ } catch {
64753
+ return null;
64592
64754
  }
64593
- return imports;
64594
64755
  }
64595
- function resolveImportPaths(imports, _workdir) {
64596
- const resolved = [];
64597
- for (const imp of imports) {
64598
- if (imp.startsWith(".")) {
64599
- resolved.push(imp);
64600
- }
64756
+ function parseTsImports(content) {
64757
+ const specs = [];
64758
+ for (const m of content.matchAll(TS_IMPORT_RE)) {
64759
+ if (m[1].startsWith("."))
64760
+ specs.push(m[1]);
64761
+ }
64762
+ return specs;
64763
+ }
64764
+ function tsCandidates(spec) {
64765
+ if (TS_EXTS.some((e) => spec.endsWith(e)))
64766
+ return [spec];
64767
+ const out = [];
64768
+ for (const e of TS_EXTS)
64769
+ out.push(`${spec}${e}`);
64770
+ for (const e of TS_EXTS)
64771
+ out.push(`${spec}/index${e}`);
64772
+ return out;
64773
+ }
64774
+ function parsePythonImports(content) {
64775
+ const modules = [];
64776
+ for (const m of content.matchAll(PY_IMPORT_LINE_RE)) {
64777
+ const fromModule = m[1];
64778
+ const importList = m[2];
64779
+ if (fromModule !== undefined) {
64780
+ if (fromModule)
64781
+ modules.push(fromModule);
64782
+ } else if (importList) {
64783
+ for (const part of importList.split(",")) {
64784
+ const mod = part.trim().split(/\s+as\s+/)[0].trim();
64785
+ if (mod)
64786
+ modules.push(mod);
64787
+ }
64788
+ }
64789
+ }
64790
+ return modules;
64791
+ }
64792
+ function pythonCandidates(module) {
64793
+ const base = module.replace(/\./g, "/");
64794
+ return [`${base}.py`, `${base}/__init__.py`];
64795
+ }
64796
+ function parseRustUses(content) {
64797
+ const paths = [];
64798
+ for (const m of content.matchAll(RUST_USE_RE))
64799
+ paths.push(m[1]);
64800
+ return paths;
64801
+ }
64802
+ function rustCandidates(usePath) {
64803
+ const rest = usePath.split("::").slice(1);
64804
+ if (rest.length === 0)
64805
+ return [];
64806
+ const base = `src/${rest.join("/")}`;
64807
+ const candidates = [`${base}.rs`, `${base}/mod.rs`];
64808
+ if (rest.length > 1) {
64809
+ const parent = `src/${rest.slice(0, -1).join("/")}`;
64810
+ candidates.push(`${parent}.rs`, `${parent}/mod.rs`);
64811
+ } else {
64812
+ candidates.push("src/lib.rs");
64601
64813
  }
64602
- return resolved.slice(0, MAX_SOURCE_FILES);
64814
+ return candidates;
64603
64815
  }
64604
- async function loadSourceFilesForDiagnosis(testFileContent, workdir) {
64605
- const imports = parseImportStatements(testFileContent);
64606
- const relativeImports = resolveImportPaths(imports, workdir);
64607
- const results = await Promise.all(relativeImports.map((imp) => readSourceFileContent(imp, workdir)));
64608
- return results.filter((f) => f !== null);
64816
+ function parseGoImports(content) {
64817
+ const paths = [];
64818
+ for (const m of content.matchAll(GO_SINGLE_IMPORT_RE))
64819
+ paths.push(m[1]);
64820
+ for (const block of content.matchAll(GO_IMPORT_BLOCK_RE)) {
64821
+ for (const line of block[1].matchAll(GO_BLOCK_LINE_RE))
64822
+ paths.push(line[1]);
64823
+ }
64824
+ return paths;
64609
64825
  }
64610
- async function readSourceFileContent(filePath, workdir) {
64826
+ async function readGoModulePrefix(packageDir) {
64611
64827
  try {
64612
- const fullPath = `${workdir}/${filePath}`;
64613
- const file3 = await Bun.file(fullPath).text();
64614
- const lines = file3.split(`
64615
- `).slice(0, MAX_FILE_LINES2);
64616
- return { path: filePath, content: lines.join(`
64617
- `) };
64828
+ const text = await Bun.file(`${packageDir}/go.mod`).text();
64829
+ const m = text.match(/^\s*module\s+(\S+)/m);
64830
+ return m ? m[1] : null;
64618
64831
  } catch {
64619
64832
  return null;
64620
64833
  }
64621
64834
  }
64622
- var MAX_SOURCE_FILES = 5, MAX_FILE_LINES2 = 500;
64835
+ async function resolveGoCandidates(content, packageDir) {
64836
+ const prefix = await readGoModulePrefix(packageDir);
64837
+ if (!prefix)
64838
+ return [];
64839
+ const candidates = [];
64840
+ for (const imp of parseGoImports(content)) {
64841
+ if (imp !== prefix && !imp.startsWith(`${prefix}/`))
64842
+ continue;
64843
+ const relDir = imp === prefix ? "." : imp.slice(prefix.length + 1);
64844
+ try {
64845
+ for (const file3 of new Bun.Glob("*.go").scanSync({ cwd: `${packageDir}/${relDir}`, absolute: false })) {
64846
+ if (file3.endsWith("_test.go"))
64847
+ continue;
64848
+ candidates.push(relDir === "." ? file3 : `${relDir}/${file3}`);
64849
+ }
64850
+ } catch {}
64851
+ }
64852
+ return candidates;
64853
+ }
64854
+ async function collectCandidates(lang, opts) {
64855
+ switch (lang) {
64856
+ case "typescript":
64857
+ case "javascript":
64858
+ return parseTsImports(opts.testFileContent).flatMap(tsCandidates);
64859
+ case "python":
64860
+ return parsePythonImports(opts.testFileContent).flatMap(pythonCandidates);
64861
+ case "rust":
64862
+ return parseRustUses(opts.testFileContent).flatMap(rustCandidates);
64863
+ case "go":
64864
+ return resolveGoCandidates(opts.testFileContent, opts.packageDir);
64865
+ default:
64866
+ return [];
64867
+ }
64868
+ }
64869
+ async function readCandidates(candidates, packageDir) {
64870
+ const seen = new Set;
64871
+ const results = [];
64872
+ for (const rel of candidates) {
64873
+ if (results.length >= MAX_SOURCE_FILES)
64874
+ break;
64875
+ if (seen.has(rel))
64876
+ continue;
64877
+ seen.add(rel);
64878
+ const file3 = await readCapped(rel, packageDir);
64879
+ if (file3)
64880
+ results.push(file3);
64881
+ }
64882
+ return results;
64883
+ }
64884
+ async function resolveSourceFiles(opts) {
64885
+ const lang = await resolveLanguage(opts);
64886
+ const candidates = await collectCandidates(lang, opts);
64887
+ return readCandidates(candidates, opts.packageDir);
64888
+ }
64889
+ var MAX_SOURCE_FILES = 5, MAX_FILE_LINES2 = 500, SUPPORTED, EXT_LANGUAGE, TS_IMPORT_RE, TS_EXTS, PY_IMPORT_LINE_RE, RUST_USE_RE, GO_SINGLE_IMPORT_RE, GO_IMPORT_BLOCK_RE, GO_BLOCK_LINE_RE;
64890
+ var init_import_resolution = __esm(() => {
64891
+ init_project();
64892
+ SUPPORTED = new Set(["typescript", "javascript", "python", "go", "rust"]);
64893
+ EXT_LANGUAGE = new Map([
64894
+ [".ts", "typescript"],
64895
+ [".tsx", "typescript"],
64896
+ [".mts", "typescript"],
64897
+ [".cts", "typescript"],
64898
+ [".js", "javascript"],
64899
+ [".jsx", "javascript"],
64900
+ [".mjs", "javascript"],
64901
+ [".cjs", "javascript"],
64902
+ [".py", "python"],
64903
+ [".go", "go"],
64904
+ [".rs", "rust"]
64905
+ ]);
64906
+ TS_IMPORT_RE = /import\s+(?:{[^}]+}|[^;'"]+)\s+from\s+["']([^"']+)["']/g;
64907
+ TS_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"];
64908
+ PY_IMPORT_LINE_RE = /^\s*(?:from\s+\.*([\w.]*)\s+import\s+|import\s+([\w.]+(?:\s*,\s*[\w.]+)*))/gm;
64909
+ RUST_USE_RE = /^\s*use\s+(crate(?:::\w+)*|(?:super|self)(?:::\w+)?)/gm;
64910
+ GO_SINGLE_IMPORT_RE = /^\s*import\s+"([^"]+)"/gm;
64911
+ GO_IMPORT_BLOCK_RE = /import\s*\(([\s\S]*?)\)/g;
64912
+ GO_BLOCK_LINE_RE = /"([^"]+)"/g;
64913
+ });
64914
+
64915
+ // src/acceptance/fix-diagnosis.ts
64916
+ async function loadSourceFilesForDiagnosis(opts) {
64917
+ return resolveSourceFiles(opts);
64918
+ }
64919
+ var init_fix_diagnosis = __esm(() => {
64920
+ init_import_resolution();
64921
+ });
64623
64922
 
64624
64923
  // src/execution/lifecycle/acceptance-helpers.ts
64625
64924
  import path23 from "path";
@@ -64800,7 +65099,11 @@ async function resolveAcceptanceDiagnosis(opts) {
64800
65099
  confidence: 0.9
64801
65100
  };
64802
65101
  }
64803
- const sourceFiles = await loadSourceFilesForDiagnosis(diagnosisOpts.testFileContent, diagnosisOpts.workdir);
65102
+ const sourceFiles = await loadSourceFilesForDiagnosis({
65103
+ testFileContent: diagnosisOpts.testFileContent,
65104
+ packageDir: diagnosisOpts.workdir,
65105
+ testFilePath: diagnosisOpts.acceptanceTestPath
65106
+ });
64804
65107
  return await _diagnosisDeps.callOp(fixCallCtx(ctx), acceptanceDiagnoseOp, {
64805
65108
  testOutput: diagnosisOpts.testOutput,
64806
65109
  testFileContent: diagnosisOpts.testFileContent,
@@ -64811,6 +65114,7 @@ async function resolveAcceptanceDiagnosis(opts) {
64811
65114
  }
64812
65115
  var _diagnosisDeps;
64813
65116
  var init_acceptance_fix2 = __esm(() => {
65117
+ init_fix_diagnosis();
64814
65118
  init_errors();
64815
65119
  init_logger2();
64816
65120
  init_operations();
@@ -69317,7 +69621,7 @@ var init_runner_execution = __esm(() => {
69317
69621
 
69318
69622
  // src/execution/status-file.ts
69319
69623
  import { rename as rename2, unlink as unlink3 } from "fs/promises";
69320
- import { resolve as resolve16 } from "path";
69624
+ import { resolve as resolve17 } from "path";
69321
69625
  function countProgress(prd) {
69322
69626
  const stories = prd.userStories;
69323
69627
  const passed = stories.filter((s) => s.status === "passed").length;
@@ -69362,7 +69666,7 @@ function buildStatusSnapshot(state) {
69362
69666
  return snapshot;
69363
69667
  }
69364
69668
  async function writeStatusFile(filePath, status) {
69365
- const resolvedPath = resolve16(filePath);
69669
+ const resolvedPath = resolve17(filePath);
69366
69670
  if (filePath.includes("../") || filePath.includes("..\\")) {
69367
69671
  throw new Error("Invalid status file path: path traversal detected");
69368
69672
  }
@@ -71300,14 +71604,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71300
71604
  prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
71301
71605
  actScopeDepth = prevActScopeDepth;
71302
71606
  }
71303
- function recursivelyFlushAsyncActWork(returnValue, resolve17, reject) {
71607
+ function recursivelyFlushAsyncActWork(returnValue, resolve18, reject) {
71304
71608
  var queue = ReactSharedInternals.actQueue;
71305
71609
  if (queue !== null)
71306
71610
  if (queue.length !== 0)
71307
71611
  try {
71308
71612
  flushActQueue(queue);
71309
71613
  enqueueTask(function() {
71310
- return recursivelyFlushAsyncActWork(returnValue, resolve17, reject);
71614
+ return recursivelyFlushAsyncActWork(returnValue, resolve18, reject);
71311
71615
  });
71312
71616
  return;
71313
71617
  } catch (error48) {
@@ -71315,7 +71619,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71315
71619
  }
71316
71620
  else
71317
71621
  ReactSharedInternals.actQueue = null;
71318
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve17(returnValue);
71622
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve18(returnValue);
71319
71623
  }
71320
71624
  function flushActQueue(queue) {
71321
71625
  if (!isFlushing) {
@@ -71491,14 +71795,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71491
71795
  didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = true, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
71492
71796
  });
71493
71797
  return {
71494
- then: function(resolve17, reject) {
71798
+ then: function(resolve18, reject) {
71495
71799
  didAwaitActCall = true;
71496
71800
  thenable.then(function(returnValue) {
71497
71801
  popActScope(prevActQueue, prevActScopeDepth);
71498
71802
  if (prevActScopeDepth === 0) {
71499
71803
  try {
71500
71804
  flushActQueue(queue), enqueueTask(function() {
71501
- return recursivelyFlushAsyncActWork(returnValue, resolve17, reject);
71805
+ return recursivelyFlushAsyncActWork(returnValue, resolve18, reject);
71502
71806
  });
71503
71807
  } catch (error$0) {
71504
71808
  ReactSharedInternals.thrownErrors.push(error$0);
@@ -71509,7 +71813,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71509
71813
  reject(_thrownError);
71510
71814
  }
71511
71815
  } else
71512
- resolve17(returnValue);
71816
+ resolve18(returnValue);
71513
71817
  }, function(error48) {
71514
71818
  popActScope(prevActQueue, prevActScopeDepth);
71515
71819
  0 < ReactSharedInternals.thrownErrors.length ? (error48 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(error48)) : reject(error48);
@@ -71525,11 +71829,11 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71525
71829
  if (0 < ReactSharedInternals.thrownErrors.length)
71526
71830
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
71527
71831
  return {
71528
- then: function(resolve17, reject) {
71832
+ then: function(resolve18, reject) {
71529
71833
  didAwaitActCall = true;
71530
71834
  prevActScopeDepth === 0 ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
71531
- return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve17, reject);
71532
- })) : resolve17(returnValue$jscomp$0);
71835
+ return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve18, reject);
71836
+ })) : resolve18(returnValue$jscomp$0);
71533
71837
  }
71534
71838
  };
71535
71839
  };
@@ -74371,8 +74675,8 @@ It can also happen if the client has a browser extension installed which messes
74371
74675
  currentEntangledActionThenable = {
74372
74676
  status: "pending",
74373
74677
  value: undefined,
74374
- then: function(resolve17) {
74375
- entangledListeners.push(resolve17);
74678
+ then: function(resolve18) {
74679
+ entangledListeners.push(resolve18);
74376
74680
  }
74377
74681
  };
74378
74682
  }
@@ -74396,8 +74700,8 @@ It can also happen if the client has a browser extension installed which messes
74396
74700
  status: "pending",
74397
74701
  value: null,
74398
74702
  reason: null,
74399
- then: function(resolve17) {
74400
- listeners.push(resolve17);
74703
+ then: function(resolve18) {
74704
+ listeners.push(resolve18);
74401
74705
  }
74402
74706
  };
74403
74707
  thenable.then(function() {
@@ -106460,7 +106764,7 @@ function ansiRegex({ onlyFirst = false } = {}) {
106460
106764
 
106461
106765
  // node_modules/strip-ansi/index.js
106462
106766
  var regex = ansiRegex();
106463
- function stripAnsi(string4) {
106767
+ function stripAnsi2(string4) {
106464
106768
  if (typeof string4 !== "string") {
106465
106769
  throw new TypeError(`Expected a \`string\`, got \`${typeof string4}\``);
106466
106770
  }
@@ -106509,7 +106813,7 @@ function stringWidth(string4, options = {}) {
106509
106813
  countAnsiEscapeCodes = false
106510
106814
  } = options;
106511
106815
  if (!countAnsiEscapeCodes) {
106512
- string4 = stripAnsi(string4);
106816
+ string4 = stripAnsi2(string4);
106513
106817
  }
106514
106818
  if (string4.length === 0) {
106515
106819
  return 0;
@@ -106740,7 +107044,7 @@ var wrapWord = (rows, word, columns) => {
106740
107044
  const characters = [...word];
106741
107045
  let isInsideEscape = false;
106742
107046
  let isInsideLinkEscape = false;
106743
- let visible = stringWidth(stripAnsi(rows.at(-1)));
107047
+ let visible = stringWidth(stripAnsi2(rows.at(-1)));
106744
107048
  for (const [index, character] of characters.entries()) {
106745
107049
  const characterLength = stringWidth(character);
106746
107050
  if (visible + characterLength <= columns) {
@@ -107026,7 +107330,7 @@ function stringWidth2(input, options = {}) {
107026
107330
  } = options;
107027
107331
  let string4 = input;
107028
107332
  if (!countAnsiEscapeCodes) {
107029
- string4 = stripAnsi(string4);
107333
+ string4 = stripAnsi2(string4);
107030
107334
  }
107031
107335
  if (string4.length === 0) {
107032
107336
  return 0;
@@ -109906,8 +110210,8 @@ class Ink {
109906
110210
  }
109907
110211
  }
109908
110212
  async waitUntilExit() {
109909
- this.exitPromise ||= new Promise((resolve17, reject2) => {
109910
- this.resolveExitPromise = resolve17;
110213
+ this.exitPromise ||= new Promise((resolve18, reject2) => {
110214
+ this.resolveExitPromise = resolve18;
109911
110215
  this.rejectExitPromise = reject2;
109912
110216
  });
109913
110217
  if (!this.beforeExitHandler) {
@@ -112161,7 +112465,7 @@ async function promptForConfirmation(question) {
112161
112465
  if (!process.stdin.isTTY) {
112162
112466
  return true;
112163
112467
  }
112164
- return new Promise((resolve17) => {
112468
+ return new Promise((resolve18) => {
112165
112469
  process.stdout.write(source_default.bold(`${question} [Y/n] `));
112166
112470
  process.stdin.setRawMode(true);
112167
112471
  process.stdin.resume();
@@ -112174,9 +112478,9 @@ async function promptForConfirmation(question) {
112174
112478
  process.stdout.write(`
112175
112479
  `);
112176
112480
  if (answer === "n") {
112177
- resolve17(false);
112481
+ resolve18(false);
112178
112482
  } else {
112179
- resolve17(true);
112483
+ resolve18(true);
112180
112484
  }
112181
112485
  };
112182
112486
  process.stdin.on("data", handler);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.73.2",
3
+ "version": "0.73.3",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {