@nathapp/nax 0.69.3 → 0.69.5

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 +126 -19
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -25203,8 +25203,22 @@ async function selectScopedTests(input) {
25203
25203
  if (!smartCfg.enabled || !input.storyGitRef || isMonorepoOrchestrator) {
25204
25204
  return { effectiveCommand: input.testCommand, isFullSuite: true, thresholdFallback: false, isMonorepoOrchestrator };
25205
25205
  }
25206
- const nonTestFiles = await _scopedSelectionDeps.getChangedNonTestFiles(input.workdir, input.storyGitRef, undefined, globsToTestRegex(smartCfg.testFilePatterns), input.naxIgnoreIndex);
25207
- const pass1Files = await _scopedSelectionDeps.mapSourceToTests(nonTestFiles, input.workdir);
25206
+ const repoRoot = input.repoRoot ?? input.workdir;
25207
+ const classifyRegex = input.resolvedTestPatterns?.regex ? [...input.resolvedTestPatterns.regex] : globsToTestRegex(smartCfg.testFilePatterns);
25208
+ const mappingGlobs = input.resolvedTestPatterns?.globs ? [...input.resolvedTestPatterns.globs] : smartCfg.testFilePatterns;
25209
+ const changedTestFiles = await _scopedSelectionDeps.getChangedTestFiles(input.workdir, repoRoot, input.storyGitRef, input.packagePrefix, classifyRegex, input.naxIgnoreIndex);
25210
+ if (changedTestFiles.length > threshold) {
25211
+ logger.warn("verify[scoped]", `Changed test file count ${changedTestFiles.length} exceeds threshold ${threshold} \u2014 falling back to full suite`, { storyId: input.storyId });
25212
+ return fullSuite({ scopeTestFallback: true, thresholdFallback: true });
25213
+ }
25214
+ if (changedTestFiles.length > 0) {
25215
+ logger.info("verify[scoped]", `Pass 0: ${changedTestFiles.length} changed test file(s) detected directly`, {
25216
+ storyId: input.storyId
25217
+ });
25218
+ return scoped(changedTestFiles);
25219
+ }
25220
+ const nonTestFiles = await _scopedSelectionDeps.getChangedNonTestFiles(input.workdir, input.storyGitRef, input.packagePrefix, classifyRegex, input.naxIgnoreIndex, repoRoot);
25221
+ const pass1Files = await _scopedSelectionDeps.mapSourceToTests(nonTestFiles, repoRoot, input.packagePrefix, mappingGlobs);
25208
25222
  if (pass1Files.length > threshold) {
25209
25223
  logger.warn("verify[scoped]", `Scoped test file count ${pass1Files.length} exceeds threshold ${threshold} \u2014 falling back to full suite`, { storyId: input.storyId });
25210
25224
  return fullSuite({ scopeTestFallback: true, thresholdFallback: true });
@@ -25218,7 +25232,7 @@ async function selectScopedTests(input) {
25218
25232
  if (smartCfg.fallback !== "import-grep") {
25219
25233
  return fullSuite();
25220
25234
  }
25221
- const pass2Files = await _scopedSelectionDeps.importGrepFallback(nonTestFiles, input.workdir, smartCfg.testFilePatterns);
25235
+ const pass2Files = await _scopedSelectionDeps.importGrepFallback(nonTestFiles, input.workdir, mappingGlobs);
25222
25236
  if (pass2Files.length > threshold) {
25223
25237
  logger.warn("verify[scoped]", `Scoped test file count ${pass2Files.length} exceeds threshold ${threshold} \u2014 falling back to full suite`, { storyId: input.storyId });
25224
25238
  return fullSuite({ scopeTestFallback: true, thresholdFallback: true });
@@ -25243,6 +25257,7 @@ var init_scoped_selection = __esm(() => {
25243
25257
  };
25244
25258
  _scopedSelectionDeps = {
25245
25259
  getChangedNonTestFiles: _smartRunnerDeps.getChangedNonTestFiles,
25260
+ getChangedTestFiles: _smartRunnerDeps.getChangedTestFiles,
25246
25261
  mapSourceToTests: _smartRunnerDeps.mapSourceToTests,
25247
25262
  importGrepFallback: _smartRunnerDeps.importGrepFallback,
25248
25263
  buildSmartTestCommand: _smartRunnerDeps.buildSmartTestCommand
@@ -34374,17 +34389,18 @@ function acSentinelToFinding(sentinel, _output) {
34374
34389
  }
34375
34390
 
34376
34391
  // src/findings/adapters/typecheck.ts
34377
- function tscDiagnosticToFinding(d, workdir) {
34392
+ function genericTypecheckDiagnosticToFinding(d, workdir, tool) {
34378
34393
  return {
34379
34394
  source: "typecheck",
34380
- tool: "tsc",
34395
+ tool,
34381
34396
  severity: "error",
34382
34397
  category: "type-error",
34383
34398
  rule: d.code ? `TS${d.code}` : undefined,
34384
34399
  file: rebaseToWorkdir(d.file, workdir, workdir),
34385
34400
  line: d.line,
34386
34401
  column: d.column,
34387
- message: d.message
34402
+ message: d.message,
34403
+ fixTarget: "source"
34388
34404
  };
34389
34405
  }
34390
34406
  var init_typecheck = __esm(() => {
@@ -38896,7 +38912,7 @@ function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
38896
38912
  const claimsAdversarial = opts.includeAdversarialReview === true;
38897
38913
  return {
38898
38914
  name: "autofix-implementer",
38899
- appliesTo: (f) => f.fixTarget === "source" && IMPLEMENTER_SOURCES.has(f.source) || claimsAdversarial && f.source === "adversarial-review",
38915
+ appliesTo: (f) => (f.fixTarget === "source" || f.fixTarget == null) && IMPLEMENTER_SOURCES.has(f.source) || claimsAdversarial && f.source === "adversarial-review",
38900
38916
  fixOp: implementerRectifyOp,
38901
38917
  buildInput: (findings, _prior, _cycleCtx) => ({
38902
38918
  failedChecks: findingsToFailedChecks(findings),
@@ -39377,7 +39393,15 @@ var init_lint_check = __esm(() => {
39377
39393
  return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39378
39394
  }
39379
39395
  const parsed = deps.parseLintOutput(result.output, "auto", { workdir: input.workdir });
39380
- return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
39396
+ const parsedFindings = parsed?.findings ?? [];
39397
+ const sentinel = {
39398
+ source: "lint",
39399
+ severity: "error",
39400
+ category: "lint-failure",
39401
+ message: `lint failed (no structured findings parsed), please run the lint check command: ${command}`
39402
+ };
39403
+ const findings = parsedFindings.length > 0 ? parsedFindings : [sentinel];
39404
+ return { success: false, findings, durationMs: Date.now() - start };
39381
39405
  }
39382
39406
  };
39383
39407
  });
@@ -39568,6 +39592,11 @@ function strategiesFor(format) {
39568
39592
  return [];
39569
39593
  return [tscStrategy, typecheckTextBlockStrategy];
39570
39594
  }
39595
+ function toolForFormat(format) {
39596
+ if (format === "tsc")
39597
+ return "tsc";
39598
+ return;
39599
+ }
39571
39600
  function parseTypecheckOutput(output, format = "auto", opts) {
39572
39601
  if (!output.trim())
39573
39602
  return null;
@@ -39575,7 +39604,8 @@ function parseTypecheckOutput(output, format = "auto", opts) {
39575
39604
  const parsed = strategy.parse(output);
39576
39605
  if (parsed && parsed.diagnostics.length > 0) {
39577
39606
  if (opts) {
39578
- const findings = parsed.diagnostics.map((d) => tscDiagnosticToFinding(d, opts.workdir));
39607
+ const tool = toolForFormat(parsed.format);
39608
+ const findings = parsed.diagnostics.map((d) => genericTypecheckDiagnosticToFinding(d, opts.workdir, tool));
39579
39609
  return { ...parsed, findings };
39580
39610
  }
39581
39611
  return parsed;
@@ -39633,7 +39663,16 @@ var init_typecheck_check = __esm(() => {
39633
39663
  return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39634
39664
  }
39635
39665
  const parsed = deps.parseTypecheckOutput(result.output, "auto", { workdir: input.workdir });
39636
- return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
39666
+ const parsedFindings = parsed?.findings ?? [];
39667
+ const sentinel = {
39668
+ source: "typecheck",
39669
+ severity: "error",
39670
+ category: "typecheck-failure",
39671
+ fixTarget: "source",
39672
+ message: `typecheck failed (no structured findings parsed), please run the typecheck command: ${command}`
39673
+ };
39674
+ const findings = parsedFindings.length > 0 ? parsedFindings : [sentinel];
39675
+ return { success: false, findings, durationMs: Date.now() - start };
39637
39676
  }
39638
39677
  };
39639
39678
  });
@@ -39685,7 +39724,10 @@ var init_verify_scoped = __esm(() => {
39685
39724
  smartRunnerConfig: quality.execution?.smartTestRunner,
39686
39725
  scopeTestThreshold: quality.quality?.scopeTestThreshold,
39687
39726
  fallbackFullSuiteCommand: quality.quality?.commands?.test,
39688
- naxIgnoreIndex: input.naxIgnoreIndex
39727
+ naxIgnoreIndex: input.naxIgnoreIndex,
39728
+ repoRoot: input.repoRoot,
39729
+ packagePrefix: input.packagePrefix,
39730
+ resolvedTestPatterns: input.resolvedTestPatterns
39689
39731
  });
39690
39732
  if (selection.isFullSuite && regressionMode === "deferred" && !selection.isMonorepoOrchestrator && !selection.thresholdFallback) {
39691
39733
  logger.info("verify[scoped]", "No mapped tests \u2014 deferring to run-end (mode: deferred)", {
@@ -40449,6 +40491,67 @@ var init_text_block2 = __esm(() => {
40449
40491
  };
40450
40492
  });
40451
40493
 
40494
+ // src/review/lint-parsing/strategies/ruff-annotated.ts
40495
+ function parseRuffAnnotated(output) {
40496
+ if (!output.trim())
40497
+ return null;
40498
+ const lines = output.split(/\r?\n/);
40499
+ let hasArrow = false;
40500
+ for (const line of lines) {
40501
+ if (ARROW_RE.test(line)) {
40502
+ hasArrow = true;
40503
+ break;
40504
+ }
40505
+ }
40506
+ if (!hasArrow)
40507
+ return null;
40508
+ const diagnostics = [];
40509
+ let i = 0;
40510
+ while (i < lines.length) {
40511
+ const arrowMatch = ARROW_RE.exec(lines[i]);
40512
+ if (!arrowMatch || !SOURCE_EXT_RE2.test(arrowMatch[1])) {
40513
+ i++;
40514
+ continue;
40515
+ }
40516
+ const file3 = arrowMatch[1];
40517
+ const line = Number.parseInt(arrowMatch[2], 10);
40518
+ const col = arrowMatch[3] ? Number.parseInt(arrowMatch[3], 10) : undefined;
40519
+ const messageLines = [];
40520
+ let j = i - 1;
40521
+ while (j >= 0) {
40522
+ const l = lines[j]?.trim() ?? "";
40523
+ if (!l || ARROW_RE.test(lines[j] ?? "") || CONTEXT_LINE_RE.test(lines[j] ?? ""))
40524
+ break;
40525
+ messageLines.unshift(lines[j] ?? "");
40526
+ j--;
40527
+ }
40528
+ const contextLines = [lines[i]];
40529
+ let k = i + 1;
40530
+ while (k < lines.length && CONTEXT_LINE_RE.test(lines[k])) {
40531
+ contextLines.push(lines[k]);
40532
+ k++;
40533
+ }
40534
+ const raw = [...messageLines, ...contextLines].join(`
40535
+ `);
40536
+ const message = (messageLines[messageLines.length - 1] ?? file3).trim();
40537
+ diagnostics.push({ file: file3, line, column: col, message, raw });
40538
+ i = k;
40539
+ }
40540
+ if (diagnostics.length === 0)
40541
+ return null;
40542
+ return { diagnostics, format: "ruff-annotated" };
40543
+ }
40544
+ var ARROW_RE, CONTEXT_LINE_RE, ruffAnnotatedStrategy;
40545
+ var init_ruff_annotated = __esm(() => {
40546
+ init_text_block2();
40547
+ ARROW_RE = /^\s+-->\s+(.+?):(\d+)(?::(\d+))?$/;
40548
+ CONTEXT_LINE_RE = /^\s*\d*\s*\|/;
40549
+ ruffAnnotatedStrategy = {
40550
+ name: "ruff-annotated",
40551
+ parse: parseRuffAnnotated
40552
+ };
40553
+ });
40554
+
40452
40555
  // src/review/lint-parsing/parse.ts
40453
40556
  function strategiesFor2(format) {
40454
40557
  if (format === "eslint-json")
@@ -40456,12 +40559,12 @@ function strategiesFor2(format) {
40456
40559
  if (format === "biome-json")
40457
40560
  return [biomeJsonStrategy];
40458
40561
  if (format === "text")
40459
- return [textBlockStrategy];
40562
+ return [ruffAnnotatedStrategy, textBlockStrategy];
40460
40563
  if (format === "none")
40461
40564
  return [];
40462
- return [eslintJsonStrategy, biomeJsonStrategy, textBlockStrategy];
40565
+ return [eslintJsonStrategy, biomeJsonStrategy, ruffAnnotatedStrategy, textBlockStrategy];
40463
40566
  }
40464
- function toolForFormat(format) {
40567
+ function toolForFormat2(format) {
40465
40568
  if (format === "biome-json")
40466
40569
  return "biome";
40467
40570
  if (format === "eslint-json")
@@ -40475,7 +40578,7 @@ function parseLintOutput(output, format = "auto", opts) {
40475
40578
  const parsed = strategy.parse(output);
40476
40579
  if (parsed && parsed.diagnostics.length > 0) {
40477
40580
  if (opts) {
40478
- const tool = toolForFormat(parsed.format);
40581
+ const tool = toolForFormat2(parsed.format);
40479
40582
  const findings = parsed.diagnostics.map((d) => lintDiagnosticToFinding(d, opts.workdir, tool));
40480
40583
  return { ...parsed, findings };
40481
40584
  }
@@ -40495,6 +40598,7 @@ var init_parse4 = __esm(() => {
40495
40598
  init_findings();
40496
40599
  init_biome_json();
40497
40600
  init_eslint_json();
40601
+ init_ruff_annotated();
40498
40602
  init_text_block2();
40499
40603
  });
40500
40604
 
@@ -55133,7 +55237,10 @@ async function assemblePlanInputsFromCtx(ctx) {
55133
55237
  storyId: story.id,
55134
55238
  storyGitRef: ctx.storyGitRef,
55135
55239
  naxIgnoreIndex: ctx.naxIgnoreIndex,
55136
- regressionMode: toVerifyScopedMode(ctx.config.execution?.regressionGate?.mode)
55240
+ regressionMode: toVerifyScopedMode(ctx.config.execution?.regressionGate?.mode),
55241
+ repoRoot: ctx.projectDir,
55242
+ packagePrefix: packageDirRelative,
55243
+ resolvedTestPatterns
55137
55244
  } : undefined;
55138
55245
  const lintCheckInput = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("lint") && ctx.config.quality.commands.lint ? { workdir: ctx.workdir, storyId: story.id } : undefined;
55139
55246
  const typecheckCheckInput = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("typecheck") && ctx.config.quality.commands.typecheck ? { workdir: ctx.workdir, storyId: story.id } : undefined;
@@ -59551,7 +59658,7 @@ var package_default;
59551
59658
  var init_package = __esm(() => {
59552
59659
  package_default = {
59553
59660
  name: "@nathapp/nax",
59554
- version: "0.69.3",
59661
+ version: "0.69.5",
59555
59662
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
59556
59663
  type: "module",
59557
59664
  bin: {
@@ -59646,8 +59753,8 @@ var init_version = __esm(() => {
59646
59753
  NAX_VERSION = package_default.version;
59647
59754
  NAX_COMMIT = (() => {
59648
59755
  try {
59649
- if (/^[0-9a-f]{6,10}$/.test("7917c240"))
59650
- return "7917c240";
59756
+ if (/^[0-9a-f]{6,10}$/.test("f09a75e2"))
59757
+ return "f09a75e2";
59651
59758
  } catch {}
59652
59759
  try {
59653
59760
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.69.3",
3
+ "version": "0.69.5",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {