@nathapp/nax 0.65.0 → 0.65.1

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 +238 -32
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -40710,6 +40710,9 @@ var init_checks_cli = __esm(() => {
40710
40710
 
40711
40711
  // src/precheck/checks-system.ts
40712
40712
  import { existsSync as existsSync12, statSync as statSync4 } from "fs";
40713
+ function discoverCanonicalRuleRoots(workdir) {
40714
+ return [workdir];
40715
+ }
40713
40716
  async function checkDependenciesInstalled(workdir) {
40714
40717
  const depPaths = [
40715
40718
  { path: "node_modules" },
@@ -40787,7 +40790,47 @@ async function checkTypecheckCommand(config2) {
40787
40790
  message: `Typecheck command configured: ${typecheckCommand}`
40788
40791
  };
40789
40792
  }
40790
- var init_checks_system = () => {};
40793
+ async function checkCanonicalRulesLint(workdir) {
40794
+ const roots = _checkCanonicalRulesDeps.discoverRoots(workdir);
40795
+ let ruleCount = 0;
40796
+ try {
40797
+ for (const root of roots) {
40798
+ const rules = await _checkCanonicalRulesDeps.loadCanonicalRules(root);
40799
+ ruleCount += rules.length;
40800
+ }
40801
+ } catch (err) {
40802
+ if (err instanceof NeutralityLintError) {
40803
+ const first = err.violations[0];
40804
+ const detail = first ? `${first.file}:${first.lineNumber} (${first.ruleId})` : "unknown location";
40805
+ return {
40806
+ name: "canonical-rules-lint",
40807
+ tier: "blocker",
40808
+ passed: false,
40809
+ message: `Canonical rules lint failed (${err.violations.length} violation(s)): ${detail}`
40810
+ };
40811
+ }
40812
+ return {
40813
+ name: "canonical-rules-lint",
40814
+ tier: "blocker",
40815
+ passed: false,
40816
+ message: `Canonical rules lint failed: ${errorMessage(err)}`
40817
+ };
40818
+ }
40819
+ return {
40820
+ name: "canonical-rules-lint",
40821
+ tier: "blocker",
40822
+ passed: true,
40823
+ message: `Canonical rules lint passed (${ruleCount} file(s) across ${roots.length} root(s))`
40824
+ };
40825
+ }
40826
+ var _checkCanonicalRulesDeps;
40827
+ var init_checks_system = __esm(() => {
40828
+ init_canonical_loader();
40829
+ _checkCanonicalRulesDeps = {
40830
+ discoverRoots: discoverCanonicalRuleRoots,
40831
+ loadCanonicalRules
40832
+ };
40833
+ });
40791
40834
 
40792
40835
  // src/precheck/checks-blockers.ts
40793
40836
  var init_checks_blockers = __esm(() => {
@@ -41255,6 +41298,7 @@ function getLateEnvironmentBlockers(config2, workdir) {
41255
41298
  () => checkTestCommand(config2),
41256
41299
  () => checkLintCommand(config2),
41257
41300
  () => checkTypecheckCommand(config2),
41301
+ () => checkCanonicalRulesLint(workdir),
41258
41302
  () => checkGitUserConfigured(workdir)
41259
41303
  ];
41260
41304
  }
@@ -44712,6 +44756,109 @@ var init_diff_utils = __esm(() => {
44712
44756
  DEFAULT_SUFFIX_STRIPPERS = [/\.(test|spec)\.(ts|js|tsx|jsx)$/, /_test\.go$/];
44713
44757
  });
44714
44758
 
44759
+ // src/review/finding-projection.ts
44760
+ function narrowSeverity(raw) {
44761
+ return SEVERITY_MAP[raw] ?? "info";
44762
+ }
44763
+ function slugLeadingTokens(text, tokenCount = RULE_ID_SLUG_TOKENS) {
44764
+ return text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter(Boolean).slice(0, tokenCount).join("-");
44765
+ }
44766
+ function deriveRuleId(category, issue2) {
44767
+ const prefix = category?.trim() ? category.trim() : "review";
44768
+ const slug = slugLeadingTokens(issue2) || "unspecified";
44769
+ return `${prefix}:${slug}`;
44770
+ }
44771
+ function joinMessage(issue2, suggestion) {
44772
+ const trimmedIssue = issue2.trim();
44773
+ const trimmedSuggestion = (suggestion ?? "").trim();
44774
+ if (trimmedIssue && trimmedSuggestion) {
44775
+ return `${trimmedIssue}
44776
+ \u2192 ${trimmedSuggestion}`;
44777
+ }
44778
+ return trimmedIssue;
44779
+ }
44780
+ function buildMeta(f, originalSeverity) {
44781
+ const meta3 = {};
44782
+ if (f.issue)
44783
+ meta3.issue = f.issue;
44784
+ if (f.suggestion)
44785
+ meta3.suggestion = f.suggestion;
44786
+ if (f.acQuote)
44787
+ meta3.acQuote = f.acQuote;
44788
+ if (typeof f.acIndex === "number" && f.acIndex >= 1)
44789
+ meta3.acIndex = f.acIndex;
44790
+ if ("acId" in f && f.acId)
44791
+ meta3.acId = f.acId;
44792
+ if ("verifiedBy" in f && f.verifiedBy)
44793
+ meta3.verifiedBy = f.verifiedBy;
44794
+ if (originalSeverity !== undefined)
44795
+ meta3.originalSeverity = originalSeverity;
44796
+ return Object.keys(meta3).length > 0 ? meta3 : undefined;
44797
+ }
44798
+ function findingCategory(f) {
44799
+ return "category" in f && f.category ? f.category : undefined;
44800
+ }
44801
+ function llmFindingToReviewFinding(f, opts = {}) {
44802
+ const category = findingCategory(f);
44803
+ const narrowed = narrowSeverity(f.severity);
44804
+ const result = {
44805
+ ruleId: deriveRuleId(category, f.issue),
44806
+ severity: narrowed,
44807
+ file: f.file,
44808
+ line: f.line,
44809
+ message: joinMessage(f.issue, f.suggestion)
44810
+ };
44811
+ if (category)
44812
+ result.category = category;
44813
+ if (opts.source)
44814
+ result.source = opts.source;
44815
+ const meta3 = buildMeta(f, f.severity !== narrowed ? f.severity : undefined);
44816
+ if (meta3)
44817
+ result.meta = meta3;
44818
+ return result;
44819
+ }
44820
+ function llmFindingsToReviewFindings(findings, opts = {}) {
44821
+ return findings.map((f) => llmFindingToReviewFinding(f, opts));
44822
+ }
44823
+ function findingToReviewFinding(f, opts = {}) {
44824
+ const narrowed = narrowSeverity(f.severity);
44825
+ const ruleId = f.rule?.trim() ? f.rule.trim() : deriveRuleId(f.category, f.message);
44826
+ const result = {
44827
+ ruleId,
44828
+ severity: narrowed,
44829
+ file: f.file ?? "",
44830
+ line: f.line ?? 0,
44831
+ message: f.message
44832
+ };
44833
+ if (f.category)
44834
+ result.category = f.category;
44835
+ const effectiveSource = opts.source ?? f.source;
44836
+ if (effectiveSource)
44837
+ result.source = effectiveSource;
44838
+ const meta3 = { ...f.meta ?? {} };
44839
+ if (f.suggestion)
44840
+ meta3.suggestion = f.suggestion;
44841
+ if (f.severity !== narrowed)
44842
+ meta3.originalSeverity = f.severity;
44843
+ if (Object.keys(meta3).length > 0)
44844
+ result.meta = meta3;
44845
+ return result;
44846
+ }
44847
+ function findingsToReviewFindings(findings, opts = {}) {
44848
+ return findings.map((f) => findingToReviewFinding(f, opts));
44849
+ }
44850
+ var SEVERITY_MAP, RULE_ID_SLUG_TOKENS = 6;
44851
+ var init_finding_projection = __esm(() => {
44852
+ SEVERITY_MAP = {
44853
+ critical: "critical",
44854
+ error: "error",
44855
+ warning: "warning",
44856
+ warn: "warning",
44857
+ info: "info",
44858
+ low: "low"
44859
+ };
44860
+ });
44861
+
44715
44862
  // src/review/adversarial.ts
44716
44863
  import { relative as relative11, sep as sep3 } from "path";
44717
44864
  function recordAdversarialAudit(opts) {
@@ -45002,8 +45149,11 @@ async function runAdversarialReview(opts) {
45002
45149
  failOpen: false,
45003
45150
  passed: false,
45004
45151
  blockingThreshold: threshold,
45005
- result: { passed: false, findings: parsed.findings },
45006
- advisoryFindings
45152
+ result: {
45153
+ passed: false,
45154
+ findings: llmFindingsToReviewFindings(parsed.findings, { source: "adversarial-review" })
45155
+ },
45156
+ advisoryFindings: advisoryFindings.length > 0 ? llmFindingsToReviewFindings(advisoryFindings, { source: "adversarial-review" }) : undefined
45007
45157
  });
45008
45158
  return {
45009
45159
  check: "adversarial",
@@ -45035,8 +45185,11 @@ ${formatFindings2(blockingFindings)}`,
45035
45185
  failOpen: false,
45036
45186
  passed: true,
45037
45187
  blockingThreshold: threshold,
45038
- result: { passed: true, findings: parsed.findings },
45039
- advisoryFindings
45188
+ result: {
45189
+ passed: true,
45190
+ findings: llmFindingsToReviewFindings(parsed.findings, { source: "adversarial-review" })
45191
+ },
45192
+ advisoryFindings: advisoryFindings.length > 0 ? llmFindingsToReviewFindings(advisoryFindings, { source: "adversarial-review" }) : undefined
45040
45193
  });
45041
45194
  return {
45042
45195
  check: "adversarial",
@@ -45063,8 +45216,11 @@ ${formatFindings2(blockingFindings)}`,
45063
45216
  failOpen: false,
45064
45217
  passed: parsed.passed,
45065
45218
  blockingThreshold: threshold,
45066
- result: { passed: parsed.passed, findings: parsed.findings },
45067
- advisoryFindings
45219
+ result: {
45220
+ passed: parsed.passed,
45221
+ findings: llmFindingsToReviewFindings(parsed.findings, { source: "adversarial-review" })
45222
+ },
45223
+ advisoryFindings: advisoryFindings.length > 0 ? llmFindingsToReviewFindings(advisoryFindings, { source: "adversarial-review" }) : undefined
45068
45224
  });
45069
45225
  return {
45070
45226
  check: "adversarial",
@@ -45089,6 +45245,7 @@ var init_adversarial = __esm(() => {
45089
45245
  init_ac_quote_validator();
45090
45246
  init_adversarial_helpers();
45091
45247
  init_diff_utils();
45248
+ init_finding_projection();
45092
45249
  init_review_audit();
45093
45250
  _adversarialDeps = {
45094
45251
  writeReviewAudit,
@@ -45481,7 +45638,10 @@ async function runSemanticDebate(opts) {
45481
45638
  parsed: true,
45482
45639
  passed: false,
45483
45640
  blockingThreshold,
45484
- result: { passed: false, findings }
45641
+ result: {
45642
+ passed: false,
45643
+ findings: findingsToReviewFindings(findings, { source: "semantic-debate-review" })
45644
+ }
45485
45645
  });
45486
45646
  return {
45487
45647
  check: "semantic",
@@ -45507,7 +45667,10 @@ ${findings.map((f) => `${f.rule ?? "semantic"}: ${f.message}`).join(`
45507
45667
  parsed: true,
45508
45668
  passed: true,
45509
45669
  blockingThreshold,
45510
- result: { passed: true, findings }
45670
+ result: {
45671
+ passed: true,
45672
+ findings: findingsToReviewFindings(findings, { source: "semantic-debate-review" })
45673
+ }
45511
45674
  });
45512
45675
  return {
45513
45676
  check: "semantic",
@@ -45567,8 +45730,11 @@ ${findings.map((f) => `${f.rule ?? "semantic"}: ${f.message}`).join(`
45567
45730
  parsed: true,
45568
45731
  passed: false,
45569
45732
  blockingThreshold: debateThreshold,
45570
- result: { passed: false, findings: debateFindings },
45571
- advisoryFindings: debateAdvisory
45733
+ result: {
45734
+ passed: false,
45735
+ findings: llmFindingsToReviewFindings(debateFindings, { source: "semantic-debate-review" })
45736
+ },
45737
+ advisoryFindings: debateAdvisory.length > 0 ? llmFindingsToReviewFindings(debateAdvisory, { source: "semantic-debate-review" }) : undefined
45572
45738
  });
45573
45739
  return {
45574
45740
  check: "semantic",
@@ -45596,8 +45762,11 @@ ${formatFindings(debateBlocking)}`,
45596
45762
  parsed: true,
45597
45763
  passed: true,
45598
45764
  blockingThreshold: debateThreshold,
45599
- result: { passed: true, findings: debateFindings },
45600
- advisoryFindings: debateAdvisory
45765
+ result: {
45766
+ passed: true,
45767
+ findings: llmFindingsToReviewFindings(debateFindings, { source: "semantic-debate-review" })
45768
+ },
45769
+ advisoryFindings: debateAdvisory.length > 0 ? llmFindingsToReviewFindings(debateAdvisory, { source: "semantic-debate-review" }) : undefined
45601
45770
  });
45602
45771
  return {
45603
45772
  check: "semantic",
@@ -45619,8 +45788,11 @@ ${formatFindings(debateBlocking)}`,
45619
45788
  parsed: true,
45620
45789
  passed: true,
45621
45790
  blockingThreshold: debateThreshold,
45622
- result: { passed: true, findings: debateFindings },
45623
- advisoryFindings: debateAdvisory
45791
+ result: {
45792
+ passed: true,
45793
+ findings: llmFindingsToReviewFindings(debateFindings, { source: "semantic-debate-review" })
45794
+ },
45795
+ advisoryFindings: debateAdvisory.length > 0 ? llmFindingsToReviewFindings(debateAdvisory, { source: "semantic-debate-review" }) : undefined
45624
45796
  });
45625
45797
  return {
45626
45798
  check: "semantic",
@@ -45636,6 +45808,7 @@ ${formatFindings(debateBlocking)}`,
45636
45808
  var init_semantic_debate = __esm(() => {
45637
45809
  init_logger2();
45638
45810
  init_ac_quote_validator();
45811
+ init_finding_projection();
45639
45812
  init_semantic_helpers();
45640
45813
  });
45641
45814
 
@@ -46036,8 +46209,11 @@ ${formatFindings(blockingFindings)}`;
46036
46209
  failOpen: false,
46037
46210
  passed: false,
46038
46211
  blockingThreshold: threshold,
46039
- result: { passed: false, findings: sanitizedParsed.findings },
46040
- advisoryFindings
46212
+ result: {
46213
+ passed: false,
46214
+ findings: llmFindingsToReviewFindings(sanitizedParsed.findings, { source: "semantic-review" })
46215
+ },
46216
+ advisoryFindings: advisoryFindings.length > 0 ? llmFindingsToReviewFindings(advisoryFindings, { source: "semantic-review" }) : undefined
46041
46217
  });
46042
46218
  return {
46043
46219
  check: "semantic",
@@ -46067,8 +46243,11 @@ ${formatFindings(blockingFindings)}`;
46067
46243
  failOpen: false,
46068
46244
  passed: true,
46069
46245
  blockingThreshold: threshold,
46070
- result: { passed: true, findings: sanitizedParsed.findings },
46071
- advisoryFindings
46246
+ result: {
46247
+ passed: true,
46248
+ findings: llmFindingsToReviewFindings(sanitizedParsed.findings, { source: "semantic-review" })
46249
+ },
46250
+ advisoryFindings: advisoryFindings.length > 0 ? llmFindingsToReviewFindings(advisoryFindings, { source: "semantic-review" }) : undefined
46072
46251
  });
46073
46252
  return {
46074
46253
  check: "semantic",
@@ -46095,8 +46274,11 @@ ${formatFindings(blockingFindings)}`;
46095
46274
  failOpen: false,
46096
46275
  passed: sanitizedParsed.passed,
46097
46276
  blockingThreshold: threshold,
46098
- result: { passed: sanitizedParsed.passed, findings: sanitizedParsed.findings },
46099
- advisoryFindings
46277
+ result: {
46278
+ passed: sanitizedParsed.passed,
46279
+ findings: llmFindingsToReviewFindings(sanitizedParsed.findings, { source: "semantic-review" })
46280
+ },
46281
+ advisoryFindings: advisoryFindings.length > 0 ? llmFindingsToReviewFindings(advisoryFindings, { source: "semantic-review" }) : undefined
46100
46282
  });
46101
46283
  return {
46102
46284
  check: "semantic",
@@ -46122,6 +46304,7 @@ var init_semantic = __esm(() => {
46122
46304
  init_test_runners();
46123
46305
  init_ac_quote_validator();
46124
46306
  init_diff_utils();
46307
+ init_finding_projection();
46125
46308
  init_review_audit();
46126
46309
  init_semantic_debate();
46127
46310
  init_semantic_evidence();
@@ -52583,7 +52766,18 @@ async function collectFromMetrics(context) {
52583
52766
  return observations;
52584
52767
  }
52585
52768
  function findingRuleId(finding) {
52586
- return stringValue(finding.rule ?? finding.ruleId ?? finding.checkId ?? finding.category, "unknown");
52769
+ return stringValue(finding.ruleId ?? finding.rule ?? finding.checkId ?? finding.category, "unknown");
52770
+ }
52771
+ function findingMessage(finding) {
52772
+ const message = stringValue(finding.message);
52773
+ if (message)
52774
+ return message;
52775
+ const issue2 = stringValue(finding.issue);
52776
+ const suggestion = stringValue(finding.suggestion);
52777
+ if (issue2 && suggestion)
52778
+ return `${issue2}
52779
+ \u2192 ${suggestion}`;
52780
+ return issue2;
52587
52781
  }
52588
52782
  async function collectFromReviewAudit(context) {
52589
52783
  const observations = [];
@@ -52619,7 +52813,7 @@ async function collectFromReviewAudit(context) {
52619
52813
  severity: stringValue(finding.severity, "info"),
52620
52814
  file: stringValue(finding.file),
52621
52815
  line: numberValue(finding.line, 0),
52622
- message: stringValue(finding.message)
52816
+ message: findingMessage(finding)
52623
52817
  }
52624
52818
  };
52625
52819
  observations.push(obs);
@@ -52893,31 +53087,43 @@ function mergeThresholds(thresholds) {
52893
53087
  function uniqueStoryIds(storyIds) {
52894
53088
  return [...new Set(storyIds)];
52895
53089
  }
53090
+ function firstLine2(message) {
53091
+ return message.split(`
53092
+ `)[0] ?? message;
53093
+ }
52896
53094
  function h1RepeatedReviewFinding(observations, threshold) {
52897
53095
  const findings = observations.filter((o) => o.kind === "review-finding");
52898
- const byRuleId = new Map;
53096
+ const groups = new Map;
52899
53097
  for (const obs of findings) {
52900
53098
  const ruleId = obs.payload.ruleId;
52901
- const existing = byRuleId.get(ruleId);
53099
+ const message = obs.payload.message;
53100
+ const existing = groups.get(ruleId);
52902
53101
  if (existing) {
52903
- existing.push(obs.storyId);
53102
+ existing.storyIds.push(obs.storyId);
53103
+ const sampleKey = firstLine2(message);
53104
+ if (existing.samples.length < 2 && sampleKey && !existing.samples.includes(sampleKey)) {
53105
+ existing.samples.push(sampleKey);
53106
+ }
52904
53107
  } else {
52905
- byRuleId.set(ruleId, [obs.storyId]);
53108
+ const sampleKey = firstLine2(message);
53109
+ groups.set(ruleId, { storyIds: [obs.storyId], samples: sampleKey ? [sampleKey] : [] });
52906
53110
  }
52907
53111
  }
52908
53112
  const proposals = [];
52909
- for (const [ruleId, storyIds] of byRuleId.entries()) {
53113
+ for (const [ruleId, { storyIds, samples }] of groups.entries()) {
52910
53114
  if (storyIds.length < threshold)
52911
53115
  continue;
52912
53116
  const count = storyIds.length;
52913
53117
  const severity = count >= 4 ? "HIGH" : "MED";
52914
53118
  const unique = uniqueStoryIds(storyIds);
53119
+ const sampleSection = samples.length > 0 ? `
53120
+ Examples: ${samples.join(" | ")}` : "";
52915
53121
  proposals.push({
52916
53122
  id: "H1",
52917
53123
  severity,
52918
53124
  target: { canonicalFile: ".nax/rules/curator-suggestions.md", action: "add" },
52919
53125
  description: `Repeated review finding: ${ruleId} appeared ${count}x across stories`,
52920
- evidence: `Rule ${ruleId} fired ${count}\xD7 in stories: ${unique.join(", ")}`,
53126
+ evidence: `Rule ${ruleId} fired ${count}\xD7 in stories: ${unique.join(", ")}${sampleSection}`,
52921
53127
  sourceKinds: ["review-finding"],
52922
53128
  storyIds: unique
52923
53129
  });
@@ -54080,7 +54286,7 @@ var package_default;
54080
54286
  var init_package = __esm(() => {
54081
54287
  package_default = {
54082
54288
  name: "@nathapp/nax",
54083
- version: "0.65.0",
54289
+ version: "0.65.1",
54084
54290
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
54085
54291
  type: "module",
54086
54292
  bin: {
@@ -54166,8 +54372,8 @@ var init_version = __esm(() => {
54166
54372
  NAX_VERSION = package_default.version;
54167
54373
  NAX_COMMIT = (() => {
54168
54374
  try {
54169
- if (/^[0-9a-f]{6,10}$/.test("c067a82c"))
54170
- return "c067a82c";
54375
+ if (/^[0-9a-f]{6,10}$/.test("3aa1ff6b"))
54376
+ return "3aa1ff6b";
54171
54377
  } catch {}
54172
54378
  try {
54173
54379
  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.65.0",
3
+ "version": "0.65.1",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {