@nathapp/nax 0.73.2 → 0.73.4

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 +749 -108
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -17283,6 +17283,53 @@ var init_schemas_infra = __esm(() => {
17283
17283
  });
17284
17284
  });
17285
17285
 
17286
+ // src/config/schemas-reporters.ts
17287
+ var HeadersSchema, ReporterEventSchema, WebhookReporterConfigSchema, OtelReporterConfigSchema, ReportersConfigSchema;
17288
+ var init_schemas_reporters = __esm(() => {
17289
+ init_zod();
17290
+ HeadersSchema = exports_external.record(exports_external.string(), exports_external.string()).default({});
17291
+ ReporterEventSchema = exports_external.enum(["onRunStart", "onStoryComplete", "onRunEnd"]);
17292
+ WebhookReporterConfigSchema = exports_external.object({
17293
+ enabled: exports_external.boolean().default(false),
17294
+ url: exports_external.string().url().optional(),
17295
+ headers: HeadersSchema,
17296
+ events: exports_external.array(ReporterEventSchema).optional(),
17297
+ timeoutMs: exports_external.number().int().positive().default(5000)
17298
+ }).default({
17299
+ enabled: false,
17300
+ headers: {},
17301
+ timeoutMs: 5000
17302
+ });
17303
+ OtelReporterConfigSchema = exports_external.object({
17304
+ enabled: exports_external.boolean().default(false),
17305
+ endpoint: exports_external.string().url().optional(),
17306
+ headers: HeadersSchema,
17307
+ serviceName: exports_external.string().default("nax"),
17308
+ timeoutMs: exports_external.number().int().positive().default(5000)
17309
+ }).default({
17310
+ enabled: false,
17311
+ headers: {},
17312
+ serviceName: "nax",
17313
+ timeoutMs: 5000
17314
+ });
17315
+ ReportersConfigSchema = exports_external.object({
17316
+ webhook: WebhookReporterConfigSchema,
17317
+ otel: OtelReporterConfigSchema
17318
+ }).default({
17319
+ webhook: {
17320
+ enabled: false,
17321
+ headers: {},
17322
+ timeoutMs: 5000
17323
+ },
17324
+ otel: {
17325
+ enabled: false,
17326
+ headers: {},
17327
+ serviceName: "nax",
17328
+ timeoutMs: 5000
17329
+ }
17330
+ });
17331
+ });
17332
+
17286
17333
  // src/config/schemas-review.ts
17287
17334
  var SemanticReviewConfigSchema, AdversarialReviewConfigSchema, ReviewConfigSchema;
17288
17335
  var init_schemas_review = __esm(() => {
@@ -17369,6 +17416,7 @@ var init_schemas3 = __esm(() => {
17369
17416
  init_schemas_execution();
17370
17417
  init_schemas_infra();
17371
17418
  init_schemas_model();
17419
+ init_schemas_reporters();
17372
17420
  init_schemas_review();
17373
17421
  init_schemas_infra();
17374
17422
  init_schemas_review();
@@ -17739,6 +17787,7 @@ var init_schemas3 = __esm(() => {
17739
17787
  enabled: exports_external.boolean().default(false),
17740
17788
  draft: exports_external.boolean().default(true)
17741
17789
  }).optional().default({ enabled: false, draft: true }),
17790
+ reporters: ReportersConfigSchema,
17742
17791
  profile: exports_external.string().default("default"),
17743
17792
  profileChain: exports_external.array(exports_external.string()).default([])
17744
17793
  }).refine((data) => data.version === 1, {
@@ -24524,16 +24573,24 @@ function buildTestFrameworkHint(testCommand) {
24524
24573
  return "Use Jest (describe/test/expect)";
24525
24574
  return "Use your project's test framework";
24526
24575
  }
24576
+ function stripAnsi(output) {
24577
+ return output.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, "");
24578
+ }
24527
24579
  function detectFramework(output) {
24528
- if (/^\s*Test Files\s+\d+/m.test(output))
24580
+ const clean = stripAnsi(output);
24581
+ if (/^\s*Test Files\s+\d+/m.test(clean))
24529
24582
  return "vitest";
24530
- if (/^\s*Tests:\s+\d+/m.test(output))
24583
+ if (/^\s*Tests:\s+\d+/m.test(clean))
24531
24584
  return "jest";
24532
- if (/={3,}\s+\d+\s+(?:failed|passed).*in\s+[\d.]+s\s*={3,}/m.test(output))
24585
+ if (/={3,}\s+\d+\s+(?:failed|passed).*in\s+[\d.]+s\s*={3,}/m.test(clean))
24533
24586
  return "pytest";
24534
- if (/^--- (?:FAIL|PASS):/m.test(output) || /^(?:ok|FAIL)\s+\t/m.test(output))
24587
+ if (/^--- (?:FAIL|PASS):/m.test(clean) || /^(?:ok|FAIL)\s+\t/m.test(clean))
24535
24588
  return "go";
24536
- if (/^\(fail\)\s/m.test(output) || /^bun test/m.test(output) || /[\u2713\u2714\u2717\u2718]/m.test(output))
24589
+ if (/^test result:\s+(?:ok|FAILED)\./m.test(clean) || /panicked at /.test(clean))
24590
+ return "rust";
24591
+ if (/^\s*\d+\s+passing\b/m.test(clean))
24592
+ return "mocha";
24593
+ if (/^\(fail\)\s/m.test(clean) || /^bun test/m.test(clean) || /[\u2713\u2714\u2717\u2718]/m.test(clean))
24537
24594
  return "bun";
24538
24595
  return "unknown";
24539
24596
  }
@@ -24670,21 +24727,125 @@ var init_resolver = __esm(() => {
24670
24727
  };
24671
24728
  });
24672
24729
 
24730
+ // src/test-runners/parse-mocha.ts
24731
+ function parseMochaOutput(output) {
24732
+ return {
24733
+ passed: matchCount(output, /(\d+)\s+passing\b/),
24734
+ failed: matchCount(output, /(\d+)\s+failing\b/),
24735
+ failures: extractMochaFailures(output)
24736
+ };
24737
+ }
24738
+ function matchCount(output, re) {
24739
+ const m = output.match(re);
24740
+ return m ? Number.parseInt(m[1], 10) : 0;
24741
+ }
24742
+ function extractMochaFailures(output) {
24743
+ const failingIdx = output.search(/^\s*\d+\s+failing\b/m);
24744
+ if (failingIdx === -1)
24745
+ return [];
24746
+ const lines = output.slice(failingIdx).split(`
24747
+ `);
24748
+ const failures = [];
24749
+ for (let i = 1;i < lines.length; i++) {
24750
+ const header = lines[i].match(/^\s*(\d+)\)\s+(.+)$/);
24751
+ if (!header)
24752
+ continue;
24753
+ const testName = header[2].replace(/:\s*$/, "").trim();
24754
+ let file3 = "unknown";
24755
+ let error48 = "";
24756
+ const stackTrace = [];
24757
+ for (let j = i + 1;j < lines.length; j++) {
24758
+ const line = lines[j];
24759
+ if (/^\s*\d+\)\s+/.test(line))
24760
+ break;
24761
+ const at = line.match(/at\s+.*\(([^)]+):(\d+):(\d+)\)/);
24762
+ if (at) {
24763
+ if (file3 === "unknown")
24764
+ file3 = at[1];
24765
+ if (stackTrace.length < MAX_STACK_LINES)
24766
+ stackTrace.push(`${at[1]}:${at[2]}`);
24767
+ continue;
24768
+ }
24769
+ if (!error48 && line.trim())
24770
+ error48 = line.trim();
24771
+ }
24772
+ failures.push({ file: file3, testName, error: error48 || "Unknown error", stackTrace });
24773
+ }
24774
+ return failures;
24775
+ }
24776
+ var MAX_STACK_LINES = 5;
24777
+
24778
+ // src/test-runners/parse-rust.ts
24779
+ function parseRustTestOutput(output) {
24780
+ let passed = 0;
24781
+ let failed = 0;
24782
+ for (const m of output.matchAll(RESULT_LINE_RE)) {
24783
+ passed += Number.parseInt(m[1], 10);
24784
+ failed += Number.parseInt(m[2], 10);
24785
+ }
24786
+ return { passed, failed, failures: extractRustFailures(output) };
24787
+ }
24788
+ function extractRustFailures(output) {
24789
+ const lines = output.split(`
24790
+ `);
24791
+ const failures = [];
24792
+ for (let i = 0;i < lines.length; i++) {
24793
+ const header = lines[i].match(/^---- (\S+) stdout ----$/);
24794
+ if (!header)
24795
+ continue;
24796
+ const testName = header[1];
24797
+ let file3 = "unknown";
24798
+ let error48 = "";
24799
+ const stackTrace = [];
24800
+ for (let j = i + 1;j < lines.length; j++) {
24801
+ const line = lines[j];
24802
+ if (/^---- \S+ stdout ----$/.test(line) || /^test result:/.test(line))
24803
+ break;
24804
+ const panic = line.match(/panicked at ([^:\n]+):(\d+):(?:\d+):/);
24805
+ if (panic) {
24806
+ file3 = panic[1];
24807
+ if (stackTrace.length < MAX_STACK_LINES2)
24808
+ stackTrace.push(`${panic[1]}:${panic[2]}`);
24809
+ continue;
24810
+ }
24811
+ if (!error48 && line.trim() && !line.startsWith("note:"))
24812
+ error48 = line.trim();
24813
+ }
24814
+ failures.push({ file: file3, testName, error: error48 || "Unknown error", stackTrace });
24815
+ }
24816
+ if (failures.length > 0)
24817
+ return failures;
24818
+ for (const m of output.matchAll(FAILED_TEST_LINE_RE)) {
24819
+ failures.push({ file: "unknown", testName: m[1], error: "Unknown error", stackTrace: [] });
24820
+ }
24821
+ return failures;
24822
+ }
24823
+ var RESULT_LINE_RE, FAILED_TEST_LINE_RE, MAX_STACK_LINES2 = 5;
24824
+ var init_parse_rust = __esm(() => {
24825
+ RESULT_LINE_RE = /^test result:\s+(?:ok|FAILED)\.\s+(\d+)\s+passed;\s+(\d+)\s+failed;/gm;
24826
+ FAILED_TEST_LINE_RE = /^test (\S+) \.\.\. FAILED$/gm;
24827
+ });
24828
+
24673
24829
  // src/test-runners/parser.ts
24674
24830
  function parseTestOutput(output) {
24675
- const framework = detectFramework(output);
24831
+ const clean = stripAnsi(output);
24832
+ const framework = detectFramework(clean);
24676
24833
  switch (framework) {
24677
24834
  case "bun":
24678
- return parseBunOutput(output);
24835
+ return parseBunOutput(clean);
24679
24836
  case "jest":
24680
24837
  case "vitest":
24681
- return parseJestOutput(output);
24838
+ return parseJestOutput(clean);
24682
24839
  case "pytest":
24683
- return parsePytestOutput(output);
24840
+ return parsePytestOutput(clean);
24684
24841
  case "go":
24685
- return parseGoTestOutput(output);
24842
+ return parseGoTestOutput(clean);
24843
+ case "rust":
24844
+ return parseRustTestOutput(clean);
24845
+ case "mocha":
24846
+ return parseMochaOutput(clean);
24686
24847
  default:
24687
- return parseCommonOutput(output);
24848
+ return parseCommonOutput(clean);
24688
24849
  }
24689
24850
  }
24690
24851
  function parseBunTestOutput(output) {
@@ -25014,11 +25175,12 @@ function analyzeTestExitCode(output, exitCode) {
25014
25175
  }
25015
25176
  var init_parser = __esm(() => {
25016
25177
  init_detector2();
25178
+ init_parse_rust();
25017
25179
  });
25018
25180
 
25019
25181
  // src/test-runners/ac-parser.ts
25020
25182
  function parseTestFailures(output) {
25021
- const clean = output.replace(ANSI_ESCAPE_PATTERN, "");
25183
+ const clean = stripAnsi(output);
25022
25184
  const framework = detectFramework(clean);
25023
25185
  const failedACs = [];
25024
25186
  const lines = clean.split(`
@@ -25072,10 +25234,8 @@ function parseTestFailures(output) {
25072
25234
  }
25073
25235
  return failedACs;
25074
25236
  }
25075
- var ANSI_ESCAPE_PATTERN;
25076
25237
  var init_ac_parser = __esm(() => {
25077
25238
  init_detector2();
25078
- ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[A-Za-z]`, "g");
25079
25239
  });
25080
25240
 
25081
25241
  // src/utils/git.ts
@@ -25756,6 +25916,8 @@ __export(exports_test_runners, {
25756
25916
  resolveReviewExcludePatterns: () => resolveReviewExcludePatterns,
25757
25917
  parseTestOutput: () => parseTestOutput,
25758
25918
  parseTestFailures: () => parseTestFailures,
25919
+ parseRustTestOutput: () => parseRustTestOutput,
25920
+ parseMochaOutput: () => parseMochaOutput,
25759
25921
  parseBunTestOutput: () => parseBunTestOutput,
25760
25922
  isTestFileByPatterns: () => isTestFileByPatterns,
25761
25923
  isTestFile: () => isTestFile,
@@ -25787,6 +25949,7 @@ var init_test_runners = __esm(() => {
25787
25949
  init_detector2();
25788
25950
  init_resolver();
25789
25951
  init_parser();
25952
+ init_parse_rust();
25790
25953
  init_workspace();
25791
25954
  init_ac_parser();
25792
25955
  init_scoped_selection();
@@ -35651,7 +35814,7 @@ var init_semantic_review = __esm(() => {
35651
35814
  const substantiated = await substantiateSemanticEvidence(sanitized, input.mode, input.workdir, input.story.id, threshold, input.repoRoot);
35652
35815
  const { accepted, dropped } = filterByAcGroundingMinimal(substantiated, input.story.acceptanceCriteria);
35653
35816
  const blocking = accepted.filter((f) => isBlockingSeverity(f.severity, threshold));
35654
- const passed = parsed.passed && blocking.length === 0;
35817
+ const passed = blocking.length === 0 && (parsed.passed || accepted.length > 0);
35655
35818
  return {
35656
35819
  ...parsed,
35657
35820
  passed,
@@ -38863,9 +39026,10 @@ var init_full_suite_rectify_op = __esm(() => {
38863
39026
  });
38864
39027
 
38865
39028
  // src/operations/full-suite-rectify.ts
38866
- function makeFullSuiteRectifyStrategy(story, config2, sink) {
38867
- const appliesTo = (finding) => finding.source === "test-runner" && (finding.category === "failed-test" || finding.category === "execution-failed");
39029
+ function makeFullSuiteRectifyStrategy(story, config2, sink, hasTestWriterDrainer) {
39030
+ const claimsFailingTest = (finding) => finding.source === "test-runner" && (finding.category === "failed-test" || finding.category === "execution-failed");
38868
39031
  if (sink) {
39032
+ const appliesTo = (finding) => claimsFailingTest(finding) && !(hasTestWriterDrainer === true && sink.mockHandoffs.length > 0);
38869
39033
  return {
38870
39034
  name: "full-suite-rectify",
38871
39035
  appliesTo,
@@ -38893,7 +39057,7 @@ function makeFullSuiteRectifyStrategy(story, config2, sink) {
38893
39057
  }
38894
39058
  return {
38895
39059
  name: "full-suite-rectify",
38896
- appliesTo,
39060
+ appliesTo: claimsFailingTest,
38897
39061
  fixOp: implementerOp,
38898
39062
  buildInput: (findings) => ({
38899
39063
  story,
@@ -40454,27 +40618,37 @@ function flipWithPairs(pairs, snippet) {
40454
40618
  }
40455
40619
  return results;
40456
40620
  }
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))
40621
+ function makeBooleanFlip(trueLit, falseLit) {
40622
+ return (snippet) => {
40623
+ const seen = new Set;
40624
+ const results = [];
40625
+ if (new RegExp(`\\b${trueLit}\\b`).test(snippet)) {
40626
+ const produced = snippet.replace(new RegExp(`\\b${trueLit}\\b`, "g"), falseLit);
40627
+ seen.add(produced);
40468
40628
  results.push(produced);
40469
- }
40470
- return results;
40629
+ }
40630
+ if (new RegExp(`\\b${falseLit}\\b`).test(snippet)) {
40631
+ const produced = snippet.replace(new RegExp(`\\b${falseLit}\\b`, "g"), trueLit);
40632
+ if (!seen.has(produced))
40633
+ results.push(produced);
40634
+ }
40635
+ return results;
40636
+ };
40637
+ }
40638
+ function makeOperators(prefix, trueLit, falseLit) {
40639
+ return [
40640
+ { id: `${prefix}:cmp-flip`, apply: (snippet) => flipWithPairs(UNIVERSAL_COMPARISON_PAIRS, snippet) },
40641
+ { id: `${prefix}:cmp-bracket-flip`, apply: applyComparisonBracketFlip },
40642
+ { id: `${prefix}:bool-flip`, apply: makeBooleanFlip(trueLit, falseLit) },
40643
+ { id: `${prefix}:arith-flip`, apply: (snippet) => flipWithPairs(ARITHMETIC_PAIRS, snippet) }
40644
+ ];
40471
40645
  }
40472
40646
  function getOperatorsForLanguage(language) {
40473
40647
  if (!language)
40474
40648
  return [];
40475
40649
  return SUPPORTED_LANGUAGES.get(language) ?? [];
40476
40650
  }
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;
40651
+ var TS_COMPARISON_PAIRS, UNIVERSAL_COMPARISON_PAIRS, COMPARISON_GT, COMPARISON_LT, ARITHMETIC_PAIRS, TYPESCRIPT_OPERATORS, PYTHON_OPERATORS, GO_OPERATORS, RUST_OPERATORS, SUPPORTED_LANGUAGES;
40478
40652
  var init_operators = __esm(() => {
40479
40653
  TS_COMPARISON_PAIRS = [
40480
40654
  [/==/g, "!="],
@@ -40484,27 +40658,35 @@ var init_operators = __esm(() => {
40484
40658
  [/>=/g, "<="],
40485
40659
  [/<=/g, ">="]
40486
40660
  ];
40661
+ UNIVERSAL_COMPARISON_PAIRS = [
40662
+ [/==/g, "!="],
40663
+ [/!=/g, "=="],
40664
+ [/>=/g, "<="],
40665
+ [/<=/g, ">="]
40666
+ ];
40487
40667
  COMPARISON_GT = /(?<=\s)>(?!=)(?=\s|$)/g;
40488
40668
  COMPARISON_LT = /(?<=\s)<(?!=)(?=\s)/g;
40489
- TS_ARITHMETIC_PAIRS = [
40669
+ ARITHMETIC_PAIRS = [
40490
40670
  [/\+/g, "-"],
40491
40671
  [/-/g, "+"],
40492
40672
  [/\*/g, "/"],
40493
40673
  [/\//g, "*"]
40494
40674
  ];
40495
40675
  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) }
40676
+ { id: "ts:cmp-flip", apply: (snippet) => flipWithPairs(TS_COMPARISON_PAIRS, snippet) },
40677
+ { id: "ts:cmp-bracket-flip", apply: applyComparisonBracketFlip },
40678
+ { id: "ts:bool-flip", apply: makeBooleanFlip("true", "false") },
40679
+ { id: "ts:arith-flip", apply: (snippet) => flipWithPairs(ARITHMETIC_PAIRS, snippet) }
40500
40680
  ];
40501
- PYTHON_OPERATORS = [];
40502
- GO_OPERATORS = [];
40681
+ PYTHON_OPERATORS = makeOperators("py", "True", "False");
40682
+ GO_OPERATORS = makeOperators("go", "true", "false");
40683
+ RUST_OPERATORS = makeOperators("rust", "true", "false");
40503
40684
  SUPPORTED_LANGUAGES = new Map([
40504
40685
  ["typescript", TYPESCRIPT_OPERATORS],
40505
40686
  ["javascript", TYPESCRIPT_OPERATORS],
40506
40687
  ["python", PYTHON_OPERATORS],
40507
- ["go", GO_OPERATORS]
40688
+ ["go", GO_OPERATORS],
40689
+ ["rust", RUST_OPERATORS]
40508
40690
  ]);
40509
40691
  });
40510
40692
 
@@ -40526,7 +40708,8 @@ function generateMutants(input) {
40526
40708
  const line = lines[i];
40527
40709
  const lineNumber = i + 1;
40528
40710
  const trimmed = line.trim();
40529
- if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*"))
40711
+ const commentPrefixes = language === "python" ? ["#"] : ["//", "/*", "*"];
40712
+ if (commentPrefixes.some((prefix) => trimmed.startsWith(prefix)))
40530
40713
  continue;
40531
40714
  for (const operator of operators) {
40532
40715
  for (const replacement of applyOperator(operator, line)) {
@@ -41632,7 +41815,7 @@ var package_default;
41632
41815
  var init_package = __esm(() => {
41633
41816
  package_default = {
41634
41817
  name: "@nathapp/nax",
41635
- version: "0.73.2",
41818
+ version: "0.73.4",
41636
41819
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
41637
41820
  type: "module",
41638
41821
  bin: {
@@ -41733,8 +41916,8 @@ var init_version = __esm(() => {
41733
41916
  NAX_VERSION = package_default.version;
41734
41917
  NAX_COMMIT = (() => {
41735
41918
  try {
41736
- if (/^[0-9a-f]{6,10}$/.test("18a1906c"))
41737
- return "18a1906c";
41919
+ if (/^[0-9a-f]{6,10}$/.test("ec04b570"))
41920
+ return "ec04b570";
41738
41921
  } catch {}
41739
41922
  try {
41740
41923
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -58811,7 +58994,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
58811
58994
  const fullSuiteGatePhasePresent = Boolean(inputs.fullSuiteGate) && (isThreeSession || regressionMode === "per-story");
58812
58995
  const verifyScopedPhasePresent = !isThreeSession && Boolean(inputs.verifyScoped);
58813
58996
  if (fullSuiteGatePhasePresent || verifyScopedPhasePresent) {
58814
- strategies.push(makeFullSuiteRectifyStrategy(story, config2, sink));
58997
+ strategies.push(makeFullSuiteRectifyStrategy(story, config2, sink, isThreeSession));
58815
58998
  }
58816
58999
  if (config2.quality.autofix?.enabled !== false) {
58817
59000
  strategies.push(makeAutofixImplementerStrategy(story, config2, sink, {
@@ -58881,7 +59064,8 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
58881
59064
  promptSeverityFloor: "info"
58882
59065
  }));
58883
59066
  }
58884
- nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2, nbSink));
59067
+ const nbHasTestWriterDrainer = isThreeSession && nbf.scope !== "source";
59068
+ nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2, nbSink, nbHasTestWriterDrainer));
58885
59069
  const nbPostValidate = async (findings, validateCtx) => {
58886
59070
  if (nbSink.testEdits.length === 0 && nbSink.mockHandoffs.length === 0)
58887
59071
  return findings;
@@ -63289,6 +63473,264 @@ var init_curator = __esm(() => {
63289
63473
  };
63290
63474
  });
63291
63475
 
63476
+ // src/plugins/builtin/reporter-shared/interpolate.ts
63477
+ function interpolateHeaders(headers, env2 = process.env) {
63478
+ const resolved = {};
63479
+ const missing = new Set;
63480
+ for (const [key, value] of Object.entries(headers)) {
63481
+ resolved[key] = value.replace(ENV_PLACEHOLDER, (_match, name) => {
63482
+ const v = env2[name];
63483
+ if (v === undefined) {
63484
+ missing.add(name);
63485
+ return "";
63486
+ }
63487
+ return v;
63488
+ });
63489
+ }
63490
+ return { resolved, missing: [...missing] };
63491
+ }
63492
+ var ENV_PLACEHOLDER;
63493
+ var init_interpolate = __esm(() => {
63494
+ ENV_PLACEHOLDER = /\$\{([A-Z0-9_]+)\}/g;
63495
+ });
63496
+
63497
+ // src/plugins/builtin/reporter-shared/post-json.ts
63498
+ async function postJson(url2, body, opts) {
63499
+ const deps = opts.deps ?? _postJsonDeps;
63500
+ const logger = getSafeLogger();
63501
+ try {
63502
+ const res = await deps.fetch(url2, {
63503
+ method: "POST",
63504
+ headers: { "Content-Type": "application/json", ...opts.headers },
63505
+ body: JSON.stringify(body),
63506
+ signal: AbortSignal.timeout(opts.timeoutMs)
63507
+ });
63508
+ if (!res.ok) {
63509
+ logger?.warn(opts.stage, "Telemetry POST returned non-2xx", { url: url2, status: res.status });
63510
+ return false;
63511
+ }
63512
+ return true;
63513
+ } catch (err) {
63514
+ logger?.warn(opts.stage, "Telemetry POST failed", { url: url2, error: errorMessage(err) });
63515
+ return false;
63516
+ }
63517
+ }
63518
+ var _postJsonDeps;
63519
+ var init_post_json = __esm(() => {
63520
+ init_logger2();
63521
+ _postJsonDeps = { fetch: globalThis.fetch };
63522
+ });
63523
+
63524
+ // src/plugins/builtin/reporter-shared/index.ts
63525
+ var init_reporter_shared = __esm(() => {
63526
+ init_interpolate();
63527
+ init_post_json();
63528
+ });
63529
+
63530
+ // src/plugins/builtin/otel-reporter/ids.ts
63531
+ function randomHex(bytes) {
63532
+ const arr = new Uint8Array(bytes);
63533
+ crypto.getRandomValues(arr);
63534
+ let out = "";
63535
+ for (const b of arr)
63536
+ out += b.toString(16).padStart(2, "0");
63537
+ return out;
63538
+ }
63539
+ var newTraceId = () => randomHex(16), newSpanId = () => randomHex(8);
63540
+
63541
+ // src/plugins/builtin/otel-reporter/otlp.ts
63542
+ function attr(key, value) {
63543
+ return typeof value === "number" ? { key, value: { doubleValue: value } } : { key, value: { stringValue: value } };
63544
+ }
63545
+ function msToUnixNano(ms) {
63546
+ return (BigInt(Math.round(ms)) * 1000000n).toString();
63547
+ }
63548
+ function buildTracesPayload(p) {
63549
+ const span = {
63550
+ traceId: p.traceId,
63551
+ spanId: p.spanId,
63552
+ name: "nax.run",
63553
+ kind: 1,
63554
+ startTimeUnixNano: p.startUnixNano,
63555
+ endTimeUnixNano: p.endUnixNano,
63556
+ attributes: [
63557
+ attr("feature", p.feature),
63558
+ attr("runId", p.runId),
63559
+ attr("stories.completed", p.storySummary.completed),
63560
+ attr("stories.failed", p.storySummary.failed),
63561
+ attr("stories.skipped", p.storySummary.skipped),
63562
+ attr("stories.paused", p.storySummary.paused),
63563
+ attr("cost.total", p.totalCost)
63564
+ ],
63565
+ events: p.events,
63566
+ status: { code: p.storySummary.failed > 0 ? 2 : 1 }
63567
+ };
63568
+ return {
63569
+ resourceSpans: [
63570
+ {
63571
+ resource: { attributes: [attr("service.name", p.serviceName)] },
63572
+ scopeSpans: [{ scope: { name: "nax" }, spans: [span] }]
63573
+ }
63574
+ ]
63575
+ };
63576
+ }
63577
+ function buildMetricsPayload(p) {
63578
+ const statusEntries = Object.entries(p.storySummary).filter(([, n]) => n > 0);
63579
+ const storiesSum = {
63580
+ name: "nax.stories.total",
63581
+ sum: {
63582
+ aggregationTemporality: 2,
63583
+ isMonotonic: true,
63584
+ dataPoints: statusEntries.map(([status, count]) => ({
63585
+ asInt: String(count),
63586
+ timeUnixNano: p.timeUnixNano,
63587
+ attributes: [attr("status", status)]
63588
+ }))
63589
+ }
63590
+ };
63591
+ const gauge = (name, value) => ({
63592
+ name,
63593
+ gauge: { dataPoints: [{ asDouble: value, timeUnixNano: p.timeUnixNano }] }
63594
+ });
63595
+ return {
63596
+ resourceMetrics: [
63597
+ {
63598
+ resource: { attributes: [attr("service.name", p.serviceName)] },
63599
+ scopeMetrics: [
63600
+ {
63601
+ scope: { name: "nax" },
63602
+ metrics: [storiesSum, gauge("nax.run.cost", p.totalCost), gauge("nax.run.duration_ms", p.totalDurationMs)]
63603
+ }
63604
+ ]
63605
+ }
63606
+ ]
63607
+ };
63608
+ }
63609
+
63610
+ // src/plugins/builtin/otel-reporter/index.ts
63611
+ function createOtelReporterPlugin(cfg, deps) {
63612
+ const states = new Map;
63613
+ const base = cfg.endpoint?.replace(/\/$/, "");
63614
+ const flush = async (st, endMs, e) => {
63615
+ if (!base)
63616
+ return;
63617
+ const { resolved, missing } = interpolateHeaders(cfg.headers);
63618
+ if (missing.length > 0) {
63619
+ getSafeLogger()?.warn(STAGE, "Skipping OTLP export \u2014 unresolved env vars", { missing });
63620
+ return;
63621
+ }
63622
+ const startUnixNano = msToUnixNano(st.startMs);
63623
+ const endUnixNano = msToUnixNano(endMs);
63624
+ const traces = buildTracesPayload({
63625
+ serviceName: cfg.serviceName,
63626
+ traceId: st.traceId,
63627
+ spanId: st.spanId,
63628
+ startUnixNano,
63629
+ endUnixNano,
63630
+ feature: st.feature,
63631
+ runId: e.runId,
63632
+ storySummary: e.storySummary,
63633
+ totalCost: e.totalCost,
63634
+ events: st.events
63635
+ });
63636
+ const metrics = buildMetricsPayload({
63637
+ serviceName: cfg.serviceName,
63638
+ runId: e.runId,
63639
+ timeUnixNano: endUnixNano,
63640
+ storySummary: e.storySummary,
63641
+ totalCost: e.totalCost,
63642
+ totalDurationMs: e.totalDurationMs
63643
+ });
63644
+ const opts = { headers: resolved, timeoutMs: cfg.timeoutMs, stage: STAGE, deps };
63645
+ await postJson(`${base}/v1/traces`, traces, opts);
63646
+ await postJson(`${base}/v1/metrics`, metrics, opts);
63647
+ };
63648
+ const reporter = {
63649
+ name: STAGE,
63650
+ async onRunStart(event) {
63651
+ states.set(event.runId, {
63652
+ traceId: newTraceId(),
63653
+ spanId: newSpanId(),
63654
+ startMs: Date.parse(event.startTime),
63655
+ feature: event.feature,
63656
+ events: []
63657
+ });
63658
+ },
63659
+ async onStoryComplete(event) {
63660
+ const st = states.get(event.runId);
63661
+ if (!st)
63662
+ return;
63663
+ st.events.push({
63664
+ timeUnixNano: msToUnixNano(st.startMs + event.runElapsedMs),
63665
+ name: "story.complete",
63666
+ attributes: [
63667
+ attr("storyId", event.storyId),
63668
+ attr("status", event.status),
63669
+ attr("cost", event.cost),
63670
+ attr("tier", event.tier),
63671
+ attr("testStrategy", event.testStrategy)
63672
+ ]
63673
+ });
63674
+ },
63675
+ async onRunEnd(event) {
63676
+ const existing = states.get(event.runId);
63677
+ const startMs = existing?.startMs ?? Date.now() - event.totalDurationMs;
63678
+ const st = existing ?? {
63679
+ traceId: newTraceId(),
63680
+ spanId: newSpanId(),
63681
+ startMs,
63682
+ feature: "",
63683
+ events: []
63684
+ };
63685
+ states.delete(event.runId);
63686
+ await flush(st, startMs + event.totalDurationMs, event);
63687
+ }
63688
+ };
63689
+ return {
63690
+ name: STAGE,
63691
+ version: "1.0.0",
63692
+ provides: ["reporter"],
63693
+ extensions: { reporter }
63694
+ };
63695
+ }
63696
+ var STAGE = "otel-reporter";
63697
+ var init_otel_reporter = __esm(() => {
63698
+ init_logger2();
63699
+ init_reporter_shared();
63700
+ });
63701
+
63702
+ // src/plugins/builtin/webhook-reporter/index.ts
63703
+ function createWebhookReporterPlugin(cfg, deps) {
63704
+ const enabledEvent = (event) => cfg.events === undefined || cfg.events.includes(event);
63705
+ const emit = async (type, data) => {
63706
+ if (!cfg.url || !enabledEvent(type))
63707
+ return;
63708
+ const { resolved, missing } = interpolateHeaders(cfg.headers);
63709
+ if (missing.length > 0) {
63710
+ getSafeLogger()?.warn(STAGE2, "Skipping webhook \u2014 unresolved env vars", { missing });
63711
+ return;
63712
+ }
63713
+ await postJson(cfg.url, { type, emittedAt: new Date().toISOString(), data }, { headers: resolved, timeoutMs: cfg.timeoutMs, stage: STAGE2, deps });
63714
+ };
63715
+ const reporter = {
63716
+ name: STAGE2,
63717
+ onRunStart: (event) => emit("onRunStart", event),
63718
+ onStoryComplete: (event) => emit("onStoryComplete", event),
63719
+ onRunEnd: (event) => emit("onRunEnd", event)
63720
+ };
63721
+ return {
63722
+ name: STAGE2,
63723
+ version: "1.0.0",
63724
+ provides: ["reporter"],
63725
+ extensions: { reporter }
63726
+ };
63727
+ }
63728
+ var STAGE2 = "webhook-reporter";
63729
+ var init_webhook_reporter = __esm(() => {
63730
+ init_logger2();
63731
+ init_reporter_shared();
63732
+ });
63733
+
63292
63734
  // src/plugins/plugin-logger.ts
63293
63735
  function createPluginLogger(pluginName) {
63294
63736
  const stage = `plugin:${pluginName}`;
@@ -63644,7 +64086,7 @@ function extractPluginName(pluginPath) {
63644
64086
  }
63645
64087
  return basename12.replace(/\.(ts|js|mjs)$/, "");
63646
64088
  }
63647
- async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, disabledPlugins, isTestFileFn) {
64089
+ async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, disabledPlugins, isTestFileFn, reporters) {
63648
64090
  const loadedPlugins = [];
63649
64091
  const builtinPostRunActions = [];
63650
64092
  const effectiveProjectRoot = projectRoot || projectDir;
@@ -63688,6 +64130,32 @@ async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, di
63688
64130
  } else {
63689
64131
  logger?.info("plugins", `Skipping disabled plugin: '${autoRoutePlugin.name}' (built-in)`);
63690
64132
  }
64133
+ const reporterFactories = reporters ? [
64134
+ {
64135
+ name: "webhook-reporter",
64136
+ enabled: reporters.webhook.enabled,
64137
+ make: () => createWebhookReporterPlugin(reporters.webhook)
64138
+ },
64139
+ {
64140
+ name: "otel-reporter",
64141
+ enabled: reporters.otel.enabled,
64142
+ make: () => createOtelReporterPlugin(reporters.otel)
64143
+ }
64144
+ ] : [];
64145
+ for (const { name, enabled: reporterEnabled, make } of reporterFactories) {
64146
+ if (!reporterEnabled)
64147
+ continue;
64148
+ if (disabledSet.has(name)) {
64149
+ logger?.info("plugins", `Skipping disabled plugin: '${name}' (built-in)`);
64150
+ continue;
64151
+ }
64152
+ const plugin = make();
64153
+ if (plugin.setup) {
64154
+ await plugin.setup({}, createPluginLogger(plugin.name));
64155
+ }
64156
+ loadedPlugins.push({ plugin, source: { type: "builtin", path: plugin.name } });
64157
+ pluginNames.add(plugin.name);
64158
+ }
63691
64159
  const globalPlugins = await discoverPlugins(globalDir, isTestFileFn);
63692
64160
  for (const plugin of globalPlugins) {
63693
64161
  const pluginName = extractPluginName(plugin.path);
@@ -63852,6 +64320,8 @@ var init_loader4 = __esm(() => {
63852
64320
  init_auto_pr();
63853
64321
  init_auto_route();
63854
64322
  init_curator();
64323
+ init_otel_reporter();
64324
+ init_webhook_reporter();
63855
64325
  init_plugin_logger();
63856
64326
  init_registry5();
63857
64327
  init_validator();
@@ -64582,44 +65052,210 @@ var init_crash_recovery = __esm(() => {
64582
65052
  init_crash_heartbeat();
64583
65053
  });
64584
65054
 
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]);
65055
+ // src/acceptance/import-resolution.ts
65056
+ import { resolve as resolve16, sep as sep5 } from "path";
65057
+ function languageFromExtension(testFilePath) {
65058
+ if (!testFilePath)
65059
+ return;
65060
+ const dot = testFilePath.lastIndexOf(".");
65061
+ if (dot < 0)
65062
+ return;
65063
+ return EXT_LANGUAGE.get(testFilePath.slice(dot).toLowerCase());
65064
+ }
65065
+ function isResolvedLanguage(lang) {
65066
+ return lang !== undefined && SUPPORTED.has(lang);
65067
+ }
65068
+ async function resolveLanguage(opts) {
65069
+ const explicit = opts.language ?? languageFromExtension(opts.testFilePath);
65070
+ if (isResolvedLanguage(explicit))
65071
+ return explicit;
65072
+ const detected = await detectLanguage(opts.packageDir);
65073
+ if (isResolvedLanguage(detected))
65074
+ return detected;
65075
+ return "typescript";
65076
+ }
65077
+ async function readCapped(relPath, packageDir) {
65078
+ const resolvedPackageDir = resolve16(packageDir);
65079
+ const fullPath = resolve16(resolvedPackageDir, relPath);
65080
+ if (fullPath !== resolvedPackageDir && !fullPath.startsWith(resolvedPackageDir + sep5)) {
65081
+ return null;
65082
+ }
65083
+ try {
65084
+ const text = await Bun.file(fullPath).text();
65085
+ const content = text.split(`
65086
+ `).slice(0, MAX_FILE_LINES2).join(`
65087
+ `);
65088
+ return { path: relPath, content };
65089
+ } catch {
65090
+ return null;
64592
65091
  }
64593
- return imports;
64594
65092
  }
64595
- function resolveImportPaths(imports, _workdir) {
64596
- const resolved = [];
64597
- for (const imp of imports) {
64598
- if (imp.startsWith(".")) {
64599
- resolved.push(imp);
64600
- }
65093
+ function parseTsImports(content) {
65094
+ const specs = [];
65095
+ for (const m of content.matchAll(TS_IMPORT_RE)) {
65096
+ if (m[1].startsWith("."))
65097
+ specs.push(m[1]);
64601
65098
  }
64602
- return resolved.slice(0, MAX_SOURCE_FILES);
65099
+ return specs;
64603
65100
  }
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);
65101
+ function tsCandidates(spec) {
65102
+ if (TS_EXTS.some((e) => spec.endsWith(e)))
65103
+ return [spec];
65104
+ const out = [];
65105
+ for (const e of TS_EXTS)
65106
+ out.push(`${spec}${e}`);
65107
+ for (const e of TS_EXTS)
65108
+ out.push(`${spec}/index${e}`);
65109
+ return out;
64609
65110
  }
64610
- async function readSourceFileContent(filePath, workdir) {
65111
+ function parsePythonImports(content) {
65112
+ const modules = [];
65113
+ for (const m of content.matchAll(PY_IMPORT_LINE_RE)) {
65114
+ const fromModule = m[1];
65115
+ const importList = m[2];
65116
+ if (fromModule !== undefined) {
65117
+ if (fromModule)
65118
+ modules.push(fromModule);
65119
+ } else if (importList) {
65120
+ for (const part of importList.split(",")) {
65121
+ const mod = part.trim().split(/\s+as\s+/)[0].trim();
65122
+ if (mod)
65123
+ modules.push(mod);
65124
+ }
65125
+ }
65126
+ }
65127
+ return modules;
65128
+ }
65129
+ function pythonCandidates(module) {
65130
+ const base = module.replace(/\./g, "/");
65131
+ return [`${base}.py`, `${base}/__init__.py`];
65132
+ }
65133
+ function parseRustUses(content) {
65134
+ const paths = [];
65135
+ for (const m of content.matchAll(RUST_USE_RE))
65136
+ paths.push(m[1]);
65137
+ return paths;
65138
+ }
65139
+ function rustCandidates(usePath) {
65140
+ const rest = usePath.split("::").slice(1);
65141
+ if (rest.length === 0)
65142
+ return [];
65143
+ const base = `src/${rest.join("/")}`;
65144
+ const candidates = [`${base}.rs`, `${base}/mod.rs`];
65145
+ if (rest.length > 1) {
65146
+ const parent = `src/${rest.slice(0, -1).join("/")}`;
65147
+ candidates.push(`${parent}.rs`, `${parent}/mod.rs`);
65148
+ } else {
65149
+ candidates.push("src/lib.rs");
65150
+ }
65151
+ return candidates;
65152
+ }
65153
+ function parseGoImports(content) {
65154
+ const paths = [];
65155
+ for (const m of content.matchAll(GO_SINGLE_IMPORT_RE))
65156
+ paths.push(m[1]);
65157
+ for (const block of content.matchAll(GO_IMPORT_BLOCK_RE)) {
65158
+ for (const line of block[1].matchAll(GO_BLOCK_LINE_RE))
65159
+ paths.push(line[1]);
65160
+ }
65161
+ return paths;
65162
+ }
65163
+ async function readGoModulePrefix(packageDir) {
64611
65164
  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
- `) };
65165
+ const text = await Bun.file(`${packageDir}/go.mod`).text();
65166
+ const m = text.match(/^\s*module\s+(\S+)/m);
65167
+ return m ? m[1] : null;
64618
65168
  } catch {
64619
65169
  return null;
64620
65170
  }
64621
65171
  }
64622
- var MAX_SOURCE_FILES = 5, MAX_FILE_LINES2 = 500;
65172
+ async function resolveGoCandidates(content, packageDir) {
65173
+ const prefix = await readGoModulePrefix(packageDir);
65174
+ if (!prefix)
65175
+ return [];
65176
+ const candidates = [];
65177
+ for (const imp of parseGoImports(content)) {
65178
+ if (imp !== prefix && !imp.startsWith(`${prefix}/`))
65179
+ continue;
65180
+ const relDir = imp === prefix ? "." : imp.slice(prefix.length + 1);
65181
+ try {
65182
+ for (const file3 of new Bun.Glob("*.go").scanSync({ cwd: `${packageDir}/${relDir}`, absolute: false })) {
65183
+ if (file3.endsWith("_test.go"))
65184
+ continue;
65185
+ candidates.push(relDir === "." ? file3 : `${relDir}/${file3}`);
65186
+ }
65187
+ } catch {}
65188
+ }
65189
+ return candidates;
65190
+ }
65191
+ async function collectCandidates(lang, opts) {
65192
+ switch (lang) {
65193
+ case "typescript":
65194
+ case "javascript":
65195
+ return parseTsImports(opts.testFileContent).flatMap(tsCandidates);
65196
+ case "python":
65197
+ return parsePythonImports(opts.testFileContent).flatMap(pythonCandidates);
65198
+ case "rust":
65199
+ return parseRustUses(opts.testFileContent).flatMap(rustCandidates);
65200
+ case "go":
65201
+ return resolveGoCandidates(opts.testFileContent, opts.packageDir);
65202
+ default:
65203
+ return [];
65204
+ }
65205
+ }
65206
+ async function readCandidates(candidates, packageDir) {
65207
+ const seen = new Set;
65208
+ const results = [];
65209
+ for (const rel of candidates) {
65210
+ if (results.length >= MAX_SOURCE_FILES)
65211
+ break;
65212
+ if (seen.has(rel))
65213
+ continue;
65214
+ seen.add(rel);
65215
+ const file3 = await readCapped(rel, packageDir);
65216
+ if (file3)
65217
+ results.push(file3);
65218
+ }
65219
+ return results;
65220
+ }
65221
+ async function resolveSourceFiles(opts) {
65222
+ const lang = await resolveLanguage(opts);
65223
+ const candidates = await collectCandidates(lang, opts);
65224
+ return readCandidates(candidates, opts.packageDir);
65225
+ }
65226
+ 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;
65227
+ var init_import_resolution = __esm(() => {
65228
+ init_project();
65229
+ SUPPORTED = new Set(["typescript", "javascript", "python", "go", "rust"]);
65230
+ EXT_LANGUAGE = new Map([
65231
+ [".ts", "typescript"],
65232
+ [".tsx", "typescript"],
65233
+ [".mts", "typescript"],
65234
+ [".cts", "typescript"],
65235
+ [".js", "javascript"],
65236
+ [".jsx", "javascript"],
65237
+ [".mjs", "javascript"],
65238
+ [".cjs", "javascript"],
65239
+ [".py", "python"],
65240
+ [".go", "go"],
65241
+ [".rs", "rust"]
65242
+ ]);
65243
+ TS_IMPORT_RE = /import\s+(?:{[^}]+}|[^;'"]+)\s+from\s+["']([^"']+)["']/g;
65244
+ TS_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"];
65245
+ PY_IMPORT_LINE_RE = /^\s*(?:from\s+\.*([\w.]*)\s+import\s+|import\s+([\w.]+(?:\s*,\s*[\w.]+)*))/gm;
65246
+ RUST_USE_RE = /^\s*use\s+(crate(?:::\w+)*|(?:super|self)(?:::\w+)?)/gm;
65247
+ GO_SINGLE_IMPORT_RE = /^\s*import\s+"([^"]+)"/gm;
65248
+ GO_IMPORT_BLOCK_RE = /import\s*\(([\s\S]*?)\)/g;
65249
+ GO_BLOCK_LINE_RE = /"([^"]+)"/g;
65250
+ });
65251
+
65252
+ // src/acceptance/fix-diagnosis.ts
65253
+ async function loadSourceFilesForDiagnosis(opts) {
65254
+ return resolveSourceFiles(opts);
65255
+ }
65256
+ var init_fix_diagnosis = __esm(() => {
65257
+ init_import_resolution();
65258
+ });
64623
65259
 
64624
65260
  // src/execution/lifecycle/acceptance-helpers.ts
64625
65261
  import path23 from "path";
@@ -64800,7 +65436,11 @@ async function resolveAcceptanceDiagnosis(opts) {
64800
65436
  confidence: 0.9
64801
65437
  };
64802
65438
  }
64803
- const sourceFiles = await loadSourceFilesForDiagnosis(diagnosisOpts.testFileContent, diagnosisOpts.workdir);
65439
+ const sourceFiles = await loadSourceFilesForDiagnosis({
65440
+ testFileContent: diagnosisOpts.testFileContent,
65441
+ packageDir: diagnosisOpts.workdir,
65442
+ testFilePath: diagnosisOpts.acceptanceTestPath
65443
+ });
64804
65444
  return await _diagnosisDeps.callOp(fixCallCtx(ctx), acceptanceDiagnoseOp, {
64805
65445
  testOutput: diagnosisOpts.testOutput,
64806
65446
  testFileContent: diagnosisOpts.testFileContent,
@@ -64811,6 +65451,7 @@ async function resolveAcceptanceDiagnosis(opts) {
64811
65451
  }
64812
65452
  var _diagnosisDeps;
64813
65453
  var init_acceptance_fix2 = __esm(() => {
65454
+ init_fix_diagnosis();
64814
65455
  init_errors();
64815
65456
  init_logger2();
64816
65457
  init_operations();
@@ -69317,7 +69958,7 @@ var init_runner_execution = __esm(() => {
69317
69958
 
69318
69959
  // src/execution/status-file.ts
69319
69960
  import { rename as rename2, unlink as unlink3 } from "fs/promises";
69320
- import { resolve as resolve16 } from "path";
69961
+ import { resolve as resolve17 } from "path";
69321
69962
  function countProgress(prd) {
69322
69963
  const stories = prd.userStories;
69323
69964
  const passed = stories.filter((s) => s.status === "passed").length;
@@ -69362,7 +70003,7 @@ function buildStatusSnapshot(state) {
69362
70003
  return snapshot;
69363
70004
  }
69364
70005
  async function writeStatusFile(filePath, status) {
69365
- const resolvedPath = resolve16(filePath);
70006
+ const resolvedPath = resolve17(filePath);
69366
70007
  if (filePath.includes("../") || filePath.includes("..\\")) {
69367
70008
  throw new Error("Invalid status file path: path traversal detected");
69368
70009
  }
@@ -70370,7 +71011,7 @@ async function setupRun(options) {
70370
71011
  const configPlugins = config2.plugins || [];
70371
71012
  const resolvedPatterns = await resolveTestFilePatterns(config2, workdir);
70372
71013
  const isTestFileFn = (filename) => resolvedPatterns.regex.some((re) => re.test(filename));
70373
- const pluginRegistry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, workdir, config2.disabledPlugins, isTestFileFn);
71014
+ const pluginRegistry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, workdir, config2.disabledPlugins, isTestFileFn, config2.reporters);
70374
71015
  clearCache();
70375
71016
  logger?.info("plugins", `Loaded ${pluginRegistry.plugins.length} plugins`, {
70376
71017
  plugins: pluginRegistry.plugins.map((p) => ({ name: p.name, version: p.version, provides: p.provides }))
@@ -71300,14 +71941,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71300
71941
  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
71942
  actScopeDepth = prevActScopeDepth;
71302
71943
  }
71303
- function recursivelyFlushAsyncActWork(returnValue, resolve17, reject) {
71944
+ function recursivelyFlushAsyncActWork(returnValue, resolve18, reject) {
71304
71945
  var queue = ReactSharedInternals.actQueue;
71305
71946
  if (queue !== null)
71306
71947
  if (queue.length !== 0)
71307
71948
  try {
71308
71949
  flushActQueue(queue);
71309
71950
  enqueueTask(function() {
71310
- return recursivelyFlushAsyncActWork(returnValue, resolve17, reject);
71951
+ return recursivelyFlushAsyncActWork(returnValue, resolve18, reject);
71311
71952
  });
71312
71953
  return;
71313
71954
  } catch (error48) {
@@ -71315,7 +71956,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71315
71956
  }
71316
71957
  else
71317
71958
  ReactSharedInternals.actQueue = null;
71318
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve17(returnValue);
71959
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve18(returnValue);
71319
71960
  }
71320
71961
  function flushActQueue(queue) {
71321
71962
  if (!isFlushing) {
@@ -71491,14 +72132,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71491
72132
  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
72133
  });
71493
72134
  return {
71494
- then: function(resolve17, reject) {
72135
+ then: function(resolve18, reject) {
71495
72136
  didAwaitActCall = true;
71496
72137
  thenable.then(function(returnValue) {
71497
72138
  popActScope(prevActQueue, prevActScopeDepth);
71498
72139
  if (prevActScopeDepth === 0) {
71499
72140
  try {
71500
72141
  flushActQueue(queue), enqueueTask(function() {
71501
- return recursivelyFlushAsyncActWork(returnValue, resolve17, reject);
72142
+ return recursivelyFlushAsyncActWork(returnValue, resolve18, reject);
71502
72143
  });
71503
72144
  } catch (error$0) {
71504
72145
  ReactSharedInternals.thrownErrors.push(error$0);
@@ -71509,7 +72150,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71509
72150
  reject(_thrownError);
71510
72151
  }
71511
72152
  } else
71512
- resolve17(returnValue);
72153
+ resolve18(returnValue);
71513
72154
  }, function(error48) {
71514
72155
  popActScope(prevActQueue, prevActScopeDepth);
71515
72156
  0 < ReactSharedInternals.thrownErrors.length ? (error48 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(error48)) : reject(error48);
@@ -71525,11 +72166,11 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
71525
72166
  if (0 < ReactSharedInternals.thrownErrors.length)
71526
72167
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
71527
72168
  return {
71528
- then: function(resolve17, reject) {
72169
+ then: function(resolve18, reject) {
71529
72170
  didAwaitActCall = true;
71530
72171
  prevActScopeDepth === 0 ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
71531
- return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve17, reject);
71532
- })) : resolve17(returnValue$jscomp$0);
72172
+ return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve18, reject);
72173
+ })) : resolve18(returnValue$jscomp$0);
71533
72174
  }
71534
72175
  };
71535
72176
  };
@@ -74371,8 +75012,8 @@ It can also happen if the client has a browser extension installed which messes
74371
75012
  currentEntangledActionThenable = {
74372
75013
  status: "pending",
74373
75014
  value: undefined,
74374
- then: function(resolve17) {
74375
- entangledListeners.push(resolve17);
75015
+ then: function(resolve18) {
75016
+ entangledListeners.push(resolve18);
74376
75017
  }
74377
75018
  };
74378
75019
  }
@@ -74396,8 +75037,8 @@ It can also happen if the client has a browser extension installed which messes
74396
75037
  status: "pending",
74397
75038
  value: null,
74398
75039
  reason: null,
74399
- then: function(resolve17) {
74400
- listeners.push(resolve17);
75040
+ then: function(resolve18) {
75041
+ listeners.push(resolve18);
74401
75042
  }
74402
75043
  };
74403
75044
  thenable.then(function() {
@@ -86358,13 +86999,13 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
86358
86999
  return false;
86359
87000
  }
86360
87001
  function utils_getInObject(object2, path31) {
86361
- return path31.reduce(function(reduced, attr) {
87002
+ return path31.reduce(function(reduced, attr2) {
86362
87003
  if (reduced) {
86363
- if (utils_hasOwnProperty.call(reduced, attr)) {
86364
- return reduced[attr];
87004
+ if (utils_hasOwnProperty.call(reduced, attr2)) {
87005
+ return reduced[attr2];
86365
87006
  }
86366
87007
  if (typeof reduced[Symbol.iterator] === "function") {
86367
- return Array.from(reduced)[attr];
87008
+ return Array.from(reduced)[attr2];
86368
87009
  }
86369
87010
  }
86370
87011
  return null;
@@ -97359,9 +98000,9 @@ The error thrown in the component is:
97359
98000
  getEnvironmentNames
97360
98001
  }, internalMcpFunctions);
97361
98002
  }
97362
- function decorate(object2, attr, fn) {
97363
- var old = object2[attr];
97364
- object2[attr] = function(instance2) {
98003
+ function decorate(object2, attr2, fn) {
98004
+ var old = object2[attr2];
98005
+ object2[attr2] = function(instance2) {
97365
98006
  return fn.call(this, old, arguments);
97366
98007
  };
97367
98008
  return old;
@@ -106460,7 +107101,7 @@ function ansiRegex({ onlyFirst = false } = {}) {
106460
107101
 
106461
107102
  // node_modules/strip-ansi/index.js
106462
107103
  var regex = ansiRegex();
106463
- function stripAnsi(string4) {
107104
+ function stripAnsi2(string4) {
106464
107105
  if (typeof string4 !== "string") {
106465
107106
  throw new TypeError(`Expected a \`string\`, got \`${typeof string4}\``);
106466
107107
  }
@@ -106509,7 +107150,7 @@ function stringWidth(string4, options = {}) {
106509
107150
  countAnsiEscapeCodes = false
106510
107151
  } = options;
106511
107152
  if (!countAnsiEscapeCodes) {
106512
- string4 = stripAnsi(string4);
107153
+ string4 = stripAnsi2(string4);
106513
107154
  }
106514
107155
  if (string4.length === 0) {
106515
107156
  return 0;
@@ -106740,7 +107381,7 @@ var wrapWord = (rows, word, columns) => {
106740
107381
  const characters = [...word];
106741
107382
  let isInsideEscape = false;
106742
107383
  let isInsideLinkEscape = false;
106743
- let visible = stringWidth(stripAnsi(rows.at(-1)));
107384
+ let visible = stringWidth(stripAnsi2(rows.at(-1)));
106744
107385
  for (const [index, character] of characters.entries()) {
106745
107386
  const characterLength = stringWidth(character);
106746
107387
  if (visible + characterLength <= columns) {
@@ -107026,7 +107667,7 @@ function stringWidth2(input, options = {}) {
107026
107667
  } = options;
107027
107668
  let string4 = input;
107028
107669
  if (!countAnsiEscapeCodes) {
107029
- string4 = stripAnsi(string4);
107670
+ string4 = stripAnsi2(string4);
107030
107671
  }
107031
107672
  if (string4.length === 0) {
107032
107673
  return 0;
@@ -109906,8 +110547,8 @@ class Ink {
109906
110547
  }
109907
110548
  }
109908
110549
  async waitUntilExit() {
109909
- this.exitPromise ||= new Promise((resolve17, reject2) => {
109910
- this.resolveExitPromise = resolve17;
110550
+ this.exitPromise ||= new Promise((resolve18, reject2) => {
110551
+ this.resolveExitPromise = resolve18;
109911
110552
  this.rejectExitPromise = reject2;
109912
110553
  });
109913
110554
  if (!this.beforeExitHandler) {
@@ -112161,7 +112802,7 @@ async function promptForConfirmation(question) {
112161
112802
  if (!process.stdin.isTTY) {
112162
112803
  return true;
112163
112804
  }
112164
- return new Promise((resolve17) => {
112805
+ return new Promise((resolve18) => {
112165
112806
  process.stdout.write(source_default.bold(`${question} [Y/n] `));
112166
112807
  process.stdin.setRawMode(true);
112167
112808
  process.stdin.resume();
@@ -112174,9 +112815,9 @@ async function promptForConfirmation(question) {
112174
112815
  process.stdout.write(`
112175
112816
  `);
112176
112817
  if (answer === "n") {
112177
- resolve17(false);
112818
+ resolve18(false);
112178
112819
  } else {
112179
- resolve17(true);
112820
+ resolve18(true);
112180
112821
  }
112181
112822
  };
112182
112823
  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.4",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {