@nathapp/nax 0.69.4 → 0.69.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +160 -37
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -34389,17 +34389,18 @@ function acSentinelToFinding(sentinel, _output) {
34389
34389
  }
34390
34390
 
34391
34391
  // src/findings/adapters/typecheck.ts
34392
- function tscDiagnosticToFinding(d, workdir) {
34392
+ function genericTypecheckDiagnosticToFinding(d, workdir, tool) {
34393
34393
  return {
34394
34394
  source: "typecheck",
34395
- tool: "tsc",
34395
+ tool,
34396
34396
  severity: "error",
34397
34397
  category: "type-error",
34398
34398
  rule: d.code ? `TS${d.code}` : undefined,
34399
34399
  file: rebaseToWorkdir(d.file, workdir, workdir),
34400
34400
  line: d.line,
34401
34401
  column: d.column,
34402
- message: d.message
34402
+ message: d.message,
34403
+ fixTarget: "source"
34403
34404
  };
34404
34405
  }
34405
34406
  var init_typecheck = __esm(() => {
@@ -38911,7 +38912,7 @@ function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
38911
38912
  const claimsAdversarial = opts.includeAdversarialReview === true;
38912
38913
  return {
38913
38914
  name: "autofix-implementer",
38914
- 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",
38915
38916
  fixOp: implementerRectifyOp,
38916
38917
  buildInput: (findings, _prior, _cycleCtx) => ({
38917
38918
  failedChecks: findingsToFailedChecks(findings),
@@ -39392,7 +39393,15 @@ var init_lint_check = __esm(() => {
39392
39393
  return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39393
39394
  }
39394
39395
  const parsed = deps.parseLintOutput(result.output, "auto", { workdir: input.workdir });
39395
- 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 };
39396
39405
  }
39397
39406
  };
39398
39407
  });
@@ -39583,6 +39592,11 @@ function strategiesFor(format) {
39583
39592
  return [];
39584
39593
  return [tscStrategy, typecheckTextBlockStrategy];
39585
39594
  }
39595
+ function toolForFormat(format) {
39596
+ if (format === "tsc")
39597
+ return "tsc";
39598
+ return;
39599
+ }
39586
39600
  function parseTypecheckOutput(output, format = "auto", opts) {
39587
39601
  if (!output.trim())
39588
39602
  return null;
@@ -39590,7 +39604,8 @@ function parseTypecheckOutput(output, format = "auto", opts) {
39590
39604
  const parsed = strategy.parse(output);
39591
39605
  if (parsed && parsed.diagnostics.length > 0) {
39592
39606
  if (opts) {
39593
- 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));
39594
39609
  return { ...parsed, findings };
39595
39610
  }
39596
39611
  return parsed;
@@ -39648,7 +39663,16 @@ var init_typecheck_check = __esm(() => {
39648
39663
  return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
39649
39664
  }
39650
39665
  const parsed = deps.parseTypecheckOutput(result.output, "auto", { workdir: input.workdir });
39651
- 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 };
39652
39676
  }
39653
39677
  };
39654
39678
  });
@@ -39949,26 +39973,23 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39949
39973
  costUsd: totalCostUsd
39950
39974
  };
39951
39975
  }
39952
- for (const strategy of active) {
39953
- const attempts = countStrategyAttempts(cycle.iterations, strategy.name);
39954
- if (attempts >= strategy.maxAttempts) {
39955
- logger?.info("findings.cycle", "cycle exited \u2014 strategy attempt cap reached", {
39956
- storyId,
39957
- packageDir,
39958
- cycleName,
39959
- reason: "max-attempts-per-strategy",
39960
- exhaustedStrategy: strategy.name,
39961
- attempts,
39962
- maxAttempts: strategy.maxAttempts
39963
- });
39964
- return {
39965
- iterations: cycle.iterations,
39966
- finalFindings: cycle.findings,
39967
- exitReason: "max-attempts-per-strategy",
39968
- exhaustedStrategy: strategy.name,
39969
- costUsd: totalCostUsd
39970
- };
39971
- }
39976
+ const uncappedActive = active.filter((s) => countStrategyAttempts(cycle.iterations, s.name) < s.maxAttempts);
39977
+ if (uncappedActive.length === 0) {
39978
+ const exhaustedStrategy = active.find((s) => countStrategyAttempts(cycle.iterations, s.name) >= s.maxAttempts);
39979
+ logger?.info("findings.cycle", "cycle exited \u2014 all active strategies exhausted", {
39980
+ storyId,
39981
+ packageDir,
39982
+ cycleName,
39983
+ reason: "max-attempts-per-strategy",
39984
+ exhaustedStrategy: exhaustedStrategy?.name
39985
+ });
39986
+ return {
39987
+ iterations: cycle.iterations,
39988
+ finalFindings: cycle.findings,
39989
+ exitReason: "max-attempts-per-strategy",
39990
+ exhaustedStrategy: exhaustedStrategy?.name,
39991
+ costUsd: totalCostUsd
39992
+ };
39972
39993
  }
39973
39994
  const totalAttempts = countTotalAttempts(cycle.iterations);
39974
39995
  if (totalAttempts >= cycle.config.maxAttemptsTotal) {
@@ -39987,7 +40008,7 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39987
40008
  costUsd: totalCostUsd
39988
40009
  };
39989
40010
  }
39990
- for (const strategy of active) {
40011
+ for (const strategy of uncappedActive) {
39991
40012
  const bailReason = strategy.bailWhen?.(cycle.iterations) ?? null;
39992
40013
  if (bailReason !== null) {
39993
40014
  logger?.info("findings.cycle", "cycle exited \u2014 bail predicate fired", {
@@ -40007,7 +40028,7 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
40007
40028
  };
40008
40029
  }
40009
40030
  }
40010
- const group = selectExecutionGroup(active);
40031
+ const group = selectExecutionGroup(uncappedActive);
40011
40032
  const startedAt = now();
40012
40033
  const findingsBefore = [...cycle.findings];
40013
40034
  const fixesApplied = [];
@@ -40118,6 +40139,19 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
40118
40139
  };
40119
40140
  }
40120
40141
  if (liteShortCircuited) {
40142
+ const companions = uncappedActive.filter((s) => !group.includes(s));
40143
+ if (companions.length > 0) {
40144
+ const iterCostUsd = fixesApplied.reduce((sum, fa) => sum + (fa.costUsd ?? 0), 0);
40145
+ totalCostUsd += iterCostUsd;
40146
+ logger?.info("findings.cycle", "exclusive strategy exhausted \u2014 continuing to companion strategies", {
40147
+ storyId,
40148
+ packageDir,
40149
+ cycleName,
40150
+ exhaustedStrategies: group.map((s) => s.name),
40151
+ remainingStrategies: companions.map((s) => s.name)
40152
+ });
40153
+ continue;
40154
+ }
40121
40155
  logger?.info("findings.cycle", "cycle exited \u2014 validate short-circuited", {
40122
40156
  storyId,
40123
40157
  packageDir,
@@ -40467,6 +40501,67 @@ var init_text_block2 = __esm(() => {
40467
40501
  };
40468
40502
  });
40469
40503
 
40504
+ // src/review/lint-parsing/strategies/ruff-annotated.ts
40505
+ function parseRuffAnnotated(output) {
40506
+ if (!output.trim())
40507
+ return null;
40508
+ const lines = output.split(/\r?\n/);
40509
+ let hasArrow = false;
40510
+ for (const line of lines) {
40511
+ if (ARROW_RE.test(line)) {
40512
+ hasArrow = true;
40513
+ break;
40514
+ }
40515
+ }
40516
+ if (!hasArrow)
40517
+ return null;
40518
+ const diagnostics = [];
40519
+ let i = 0;
40520
+ while (i < lines.length) {
40521
+ const arrowMatch = ARROW_RE.exec(lines[i]);
40522
+ if (!arrowMatch || !SOURCE_EXT_RE2.test(arrowMatch[1])) {
40523
+ i++;
40524
+ continue;
40525
+ }
40526
+ const file3 = arrowMatch[1];
40527
+ const line = Number.parseInt(arrowMatch[2], 10);
40528
+ const col = arrowMatch[3] ? Number.parseInt(arrowMatch[3], 10) : undefined;
40529
+ const messageLines = [];
40530
+ let j = i - 1;
40531
+ while (j >= 0) {
40532
+ const l = lines[j]?.trim() ?? "";
40533
+ if (!l || ARROW_RE.test(lines[j] ?? "") || CONTEXT_LINE_RE.test(lines[j] ?? ""))
40534
+ break;
40535
+ messageLines.unshift(lines[j] ?? "");
40536
+ j--;
40537
+ }
40538
+ const contextLines = [lines[i]];
40539
+ let k = i + 1;
40540
+ while (k < lines.length && CONTEXT_LINE_RE.test(lines[k])) {
40541
+ contextLines.push(lines[k]);
40542
+ k++;
40543
+ }
40544
+ const raw = [...messageLines, ...contextLines].join(`
40545
+ `);
40546
+ const message = (messageLines[messageLines.length - 1] ?? file3).trim();
40547
+ diagnostics.push({ file: file3, line, column: col, message, raw });
40548
+ i = k;
40549
+ }
40550
+ if (diagnostics.length === 0)
40551
+ return null;
40552
+ return { diagnostics, format: "ruff-annotated" };
40553
+ }
40554
+ var ARROW_RE, CONTEXT_LINE_RE, ruffAnnotatedStrategy;
40555
+ var init_ruff_annotated = __esm(() => {
40556
+ init_text_block2();
40557
+ ARROW_RE = /^\s+-->\s+(.+?):(\d+)(?::(\d+))?$/;
40558
+ CONTEXT_LINE_RE = /^\s*\d*\s*\|/;
40559
+ ruffAnnotatedStrategy = {
40560
+ name: "ruff-annotated",
40561
+ parse: parseRuffAnnotated
40562
+ };
40563
+ });
40564
+
40470
40565
  // src/review/lint-parsing/parse.ts
40471
40566
  function strategiesFor2(format) {
40472
40567
  if (format === "eslint-json")
@@ -40474,12 +40569,12 @@ function strategiesFor2(format) {
40474
40569
  if (format === "biome-json")
40475
40570
  return [biomeJsonStrategy];
40476
40571
  if (format === "text")
40477
- return [textBlockStrategy];
40572
+ return [ruffAnnotatedStrategy, textBlockStrategy];
40478
40573
  if (format === "none")
40479
40574
  return [];
40480
- return [eslintJsonStrategy, biomeJsonStrategy, textBlockStrategy];
40575
+ return [eslintJsonStrategy, biomeJsonStrategy, ruffAnnotatedStrategy, textBlockStrategy];
40481
40576
  }
40482
- function toolForFormat(format) {
40577
+ function toolForFormat2(format) {
40483
40578
  if (format === "biome-json")
40484
40579
  return "biome";
40485
40580
  if (format === "eslint-json")
@@ -40493,7 +40588,7 @@ function parseLintOutput(output, format = "auto", opts) {
40493
40588
  const parsed = strategy.parse(output);
40494
40589
  if (parsed && parsed.diagnostics.length > 0) {
40495
40590
  if (opts) {
40496
- const tool = toolForFormat(parsed.format);
40591
+ const tool = toolForFormat2(parsed.format);
40497
40592
  const findings = parsed.diagnostics.map((d) => lintDiagnosticToFinding(d, opts.workdir, tool));
40498
40593
  return { ...parsed, findings };
40499
40594
  }
@@ -40513,6 +40608,7 @@ var init_parse4 = __esm(() => {
40513
40608
  init_findings();
40514
40609
  init_biome_json();
40515
40610
  init_eslint_json();
40611
+ init_ruff_annotated();
40516
40612
  init_text_block2();
40517
40613
  });
40518
40614
 
@@ -54717,6 +54813,33 @@ class ExecutionPlan {
54717
54813
  }
54718
54814
  }
54719
54815
  }
54816
+ if (this.state.rectification && rectResult.rectificationExhausted) {
54817
+ const mechanicalOnly = !!rectResult.unfixedFindings?.length && rectResult.unfixedFindings.every((f) => f.source === "lint" || f.source === "typecheck");
54818
+ if (mechanicalOnly) {
54819
+ for (const phase of collectOrderedPhases(this.state)) {
54820
+ const name = phase.slot.op.name;
54821
+ if (name in phaseOutputs)
54822
+ continue;
54823
+ try {
54824
+ await runPhase(this.ctx, phase.slot, phaseCosts, phaseOutputs, this.isThreeSession);
54825
+ } catch (error48) {
54826
+ logger?.error("story-orchestrator", "Phase threw unexpected error (mechanical-only resume)", {
54827
+ storyId: this.ctx.storyId,
54828
+ phase: name,
54829
+ error: errorMessage(error48)
54830
+ });
54831
+ throw error48;
54832
+ }
54833
+ if (!phasePassed(name, phaseOutputs[name], this.ctx.storyId)) {
54834
+ logger?.warn("story-orchestrator", "Phase failed in mechanical-only resume", {
54835
+ storyId: this.ctx.storyId,
54836
+ phase: name
54837
+ });
54838
+ break;
54839
+ }
54840
+ }
54841
+ }
54842
+ }
54720
54843
  const advCfg = this.state.adversarialReview ? this.state.nonBlockingFix : undefined;
54721
54844
  const advisoryOut = phaseOutputs["adversarial-review"];
54722
54845
  const advisoryFindings = advisoryOut?.advisoryFindings ?? [];
@@ -55577,7 +55700,7 @@ async function decideStageAction(ctx, planResult, inspection, opts) {
55577
55700
  const sources = new Set(planResult.unfixedFindings.map((f) => f.source));
55578
55701
  const allMechanical = [...sources].every((s) => s === "lint" || s === "typecheck");
55579
55702
  if (allMechanical) {
55580
- logger.warn("execution", "Mechanical-only failure unfixable \u2014 proceeding (LLM review passed)", {
55703
+ logger.warn("execution", "Mechanical-only failure unfixable \u2014 proceeding (style-only errors remain)", {
55581
55704
  storyId: ctx.story.id
55582
55705
  });
55583
55706
  return { action: "continue" };
@@ -59572,7 +59695,7 @@ var package_default;
59572
59695
  var init_package = __esm(() => {
59573
59696
  package_default = {
59574
59697
  name: "@nathapp/nax",
59575
- version: "0.69.4",
59698
+ version: "0.69.6",
59576
59699
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
59577
59700
  type: "module",
59578
59701
  bin: {
@@ -59667,8 +59790,8 @@ var init_version = __esm(() => {
59667
59790
  NAX_VERSION = package_default.version;
59668
59791
  NAX_COMMIT = (() => {
59669
59792
  try {
59670
- if (/^[0-9a-f]{6,10}$/.test("fb1560dd"))
59671
- return "fb1560dd";
59793
+ if (/^[0-9a-f]{6,10}$/.test("97a1e367"))
59794
+ return "97a1e367";
59672
59795
  } catch {}
59673
59796
  try {
59674
59797
  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.4",
3
+ "version": "0.69.6",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {