@nathapp/nax 0.63.0-canary.13 → 0.63.0-canary.14
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.
- package/dist/nax.js +152 -20
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -33735,7 +33735,7 @@ function packChunks(chunks, budgetTokens, availableBudgetTokens) {
|
|
|
33735
33735
|
}
|
|
33736
33736
|
var FLOOR_KINDS;
|
|
33737
33737
|
var init_packing = __esm(() => {
|
|
33738
|
-
FLOOR_KINDS = ["static", "feature"];
|
|
33738
|
+
FLOOR_KINDS = ["static", "feature", "test-coverage"];
|
|
33739
33739
|
});
|
|
33740
33740
|
|
|
33741
33741
|
// src/utils/path-security.ts
|
|
@@ -34517,6 +34517,7 @@ var init_scoring = __esm(() => {
|
|
|
34517
34517
|
KIND_WEIGHTS = {
|
|
34518
34518
|
static: 1,
|
|
34519
34519
|
feature: 1,
|
|
34520
|
+
"test-coverage": 1,
|
|
34520
34521
|
session: 0.9,
|
|
34521
34522
|
history: 0.8,
|
|
34522
34523
|
neighbor: 0.75,
|
|
@@ -34561,8 +34562,8 @@ var init_stage_config = __esm(() => {
|
|
|
34561
34562
|
PHASE_0_PROVIDERS = ["static-rules", "feature-context"];
|
|
34562
34563
|
PHASE_1_PROVIDERS = ["static-rules", "feature-context", "session-scratch"];
|
|
34563
34564
|
PHASE_3_TDD_TEST_WRITER = [...PHASE_1_PROVIDERS, "code-neighbor"];
|
|
34564
|
-
PHASE_3_TDD_IMPLEMENTER = [...PHASE_1_PROVIDERS, "git-history", "code-neighbor"];
|
|
34565
|
-
PHASE_3_EXECUTION = [...PHASE_1_PROVIDERS, "git-history", "code-neighbor"];
|
|
34565
|
+
PHASE_3_TDD_IMPLEMENTER = [...PHASE_1_PROVIDERS, "git-history", "code-neighbor", "test-coverage"];
|
|
34566
|
+
PHASE_3_EXECUTION = [...PHASE_1_PROVIDERS, "git-history", "code-neighbor", "test-coverage"];
|
|
34566
34567
|
PHASE_3_RECTIFY = [...PHASE_1_PROVIDERS, "code-neighbor"];
|
|
34567
34568
|
DEFAULT_STAGE_CONFIG = {
|
|
34568
34569
|
role: "implementer",
|
|
@@ -34870,8 +34871,8 @@ class ContextOrchestrator {
|
|
|
34870
34871
|
return matches ? { ...c, roleFiltered: false } : { ...c, roleFiltered: true };
|
|
34871
34872
|
});
|
|
34872
34873
|
const roleFiltered = postRoleFilter.filter((c) => c.roleFiltered);
|
|
34873
|
-
const belowMin = postRoleFilter.filter((c) => !c.roleFiltered && c.belowMinScore);
|
|
34874
|
-
const kept = postRoleFilter.filter((c) => !c.roleFiltered && !c.belowMinScore);
|
|
34874
|
+
const belowMin = postRoleFilter.filter((c) => !c.roleFiltered && c.belowMinScore && !FLOOR_KINDS.includes(c.kind));
|
|
34875
|
+
const kept = postRoleFilter.filter((c) => !c.roleFiltered && (!c.belowMinScore || FLOOR_KINDS.includes(c.kind)));
|
|
34875
34876
|
const { packed, budgetExcludedIds, usedTokens, floorPackedIds, floorOverageIds } = packChunks(kept, effectiveBudgetTokens, request.availableBudgetTokens);
|
|
34876
34877
|
const pushMarkdown = renderChunks(packed, {
|
|
34877
34878
|
priorStageDigest: request.priorStageDigest
|
|
@@ -35814,6 +35815,82 @@ var init_static_rules = __esm(() => {
|
|
|
35814
35815
|
LEGACY_CANDIDATE_FILES = ["CLAUDE.md", ".cursorrules", "AGENTS.md"];
|
|
35815
35816
|
});
|
|
35816
35817
|
|
|
35818
|
+
// src/context/engine/providers/test-coverage.ts
|
|
35819
|
+
import { createHash as createHash8 } from "crypto";
|
|
35820
|
+
function contentHash86(content) {
|
|
35821
|
+
return createHash8("sha256").update(content).digest("hex").slice(0, 8);
|
|
35822
|
+
}
|
|
35823
|
+
|
|
35824
|
+
class TestCoverageProvider {
|
|
35825
|
+
story;
|
|
35826
|
+
config;
|
|
35827
|
+
id = "test-coverage";
|
|
35828
|
+
kind = "test-coverage";
|
|
35829
|
+
constructor(story, config2) {
|
|
35830
|
+
this.story = story;
|
|
35831
|
+
this.config = config2;
|
|
35832
|
+
}
|
|
35833
|
+
async fetch(request) {
|
|
35834
|
+
const tcConfig = this.config.context?.testCoverage;
|
|
35835
|
+
if (tcConfig?.enabled === false) {
|
|
35836
|
+
return { chunks: [], pullTools: [] };
|
|
35837
|
+
}
|
|
35838
|
+
if (!request.packageDir) {
|
|
35839
|
+
return { chunks: [], pullTools: [] };
|
|
35840
|
+
}
|
|
35841
|
+
try {
|
|
35842
|
+
const resolved = await _testCoverageProviderDeps.resolveTestFilePatterns(this.config, request.repoRoot, request.packageDir);
|
|
35843
|
+
const contextFiles = _testCoverageProviderDeps.getContextFiles(this.story);
|
|
35844
|
+
const globs = resolved.patterns ?? resolved.globs;
|
|
35845
|
+
const scanOptions = {
|
|
35846
|
+
workdir: request.packageDir,
|
|
35847
|
+
testDir: tcConfig.testDir,
|
|
35848
|
+
maxTokens: tcConfig.maxTokens ?? 500,
|
|
35849
|
+
detail: tcConfig.detail ?? "names-and-counts",
|
|
35850
|
+
scopeToStory: tcConfig.scopeToStory ?? true,
|
|
35851
|
+
contextFiles,
|
|
35852
|
+
resolvedTestGlobs: globs
|
|
35853
|
+
};
|
|
35854
|
+
const result = await _testCoverageProviderDeps.generateTestCoverageSummary(scanOptions);
|
|
35855
|
+
if (!result.summary) {
|
|
35856
|
+
return { chunks: [], pullTools: [] };
|
|
35857
|
+
}
|
|
35858
|
+
const hash2 = contentHash86(result.summary);
|
|
35859
|
+
const chunk = {
|
|
35860
|
+
id: `test-coverage:${hash2}`,
|
|
35861
|
+
kind: "test-coverage",
|
|
35862
|
+
scope: "story",
|
|
35863
|
+
role: ["implementer", "tdd"],
|
|
35864
|
+
content: result.summary,
|
|
35865
|
+
tokens: result.tokens,
|
|
35866
|
+
rawScore: 0.85
|
|
35867
|
+
};
|
|
35868
|
+
return { chunks: [chunk], pullTools: [] };
|
|
35869
|
+
} catch (err) {
|
|
35870
|
+
const logger = _testCoverageProviderDeps.getLogger();
|
|
35871
|
+
logger.warn("test-coverage", "Scanner failed \u2014 returning empty chunks", {
|
|
35872
|
+
storyId: this.story.id,
|
|
35873
|
+
packageDir: request.packageDir,
|
|
35874
|
+
error: errorMessage(err)
|
|
35875
|
+
});
|
|
35876
|
+
return { chunks: [], pullTools: [] };
|
|
35877
|
+
}
|
|
35878
|
+
}
|
|
35879
|
+
}
|
|
35880
|
+
var _testCoverageProviderDeps;
|
|
35881
|
+
var init_test_coverage = __esm(() => {
|
|
35882
|
+
init_logger2();
|
|
35883
|
+
init_prd();
|
|
35884
|
+
init_resolver();
|
|
35885
|
+
init_test_scanner();
|
|
35886
|
+
_testCoverageProviderDeps = {
|
|
35887
|
+
generateTestCoverageSummary,
|
|
35888
|
+
resolveTestFilePatterns,
|
|
35889
|
+
getLogger: () => getLogger(),
|
|
35890
|
+
getContextFiles
|
|
35891
|
+
};
|
|
35892
|
+
});
|
|
35893
|
+
|
|
35817
35894
|
// src/context/engine/orchestrator-factory.ts
|
|
35818
35895
|
function createDefaultOrchestrator(story, config2, _storyScratchDirs, additionalProviders = []) {
|
|
35819
35896
|
const allowLegacyClaudeMd = config2.context?.v2?.rules?.allowLegacyClaudeMd ?? false;
|
|
@@ -35822,6 +35899,7 @@ function createDefaultOrchestrator(story, config2, _storyScratchDirs, additional
|
|
|
35822
35899
|
new StaticRulesProvider({ allowLegacyClaudeMd, budgetTokens: rulesBudgetTokens }),
|
|
35823
35900
|
new FeatureContextProviderV2(story, config2)
|
|
35824
35901
|
];
|
|
35902
|
+
providers.push(new TestCoverageProvider(story, config2));
|
|
35825
35903
|
providers.push(new SessionScratchProvider);
|
|
35826
35904
|
const providerConfig = config2.context?.v2?.providers;
|
|
35827
35905
|
providers.push(new GitHistoryProvider({ historyScope: providerConfig?.historyScope ?? "package" }));
|
|
@@ -35840,6 +35918,7 @@ var init_orchestrator_factory = __esm(() => {
|
|
|
35840
35918
|
init_git_history();
|
|
35841
35919
|
init_session_scratch();
|
|
35842
35920
|
init_static_rules();
|
|
35921
|
+
init_test_coverage();
|
|
35843
35922
|
});
|
|
35844
35923
|
|
|
35845
35924
|
// src/context/engine/providers/plugin-loader.ts
|
|
@@ -38306,6 +38385,9 @@ async function recheckReview(ctx) {
|
|
|
38306
38385
|
function collectFailedChecks(ctx) {
|
|
38307
38386
|
return (ctx.reviewResult?.checks ?? []).filter((c) => !c.success);
|
|
38308
38387
|
}
|
|
38388
|
+
function getCheckSignature(checks3) {
|
|
38389
|
+
return [...new Set(checks3.map((check2) => check2.check))].sort().join("|");
|
|
38390
|
+
}
|
|
38309
38391
|
function buildAutofixEscalationPreamble(attempt, maxAttempts, rethinkAtAttempt, urgencyAtAttempt) {
|
|
38310
38392
|
return buildProgressivePromptPreamble({
|
|
38311
38393
|
attempt,
|
|
@@ -38397,6 +38479,8 @@ async function runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWor
|
|
|
38397
38479
|
const loopState = {
|
|
38398
38480
|
attempt: 0,
|
|
38399
38481
|
failedChecks: implementerChecks,
|
|
38482
|
+
checkSignature: getCheckSignature(implementerChecks),
|
|
38483
|
+
checkSignatureChanged: false,
|
|
38400
38484
|
consecutiveNoOps: 0,
|
|
38401
38485
|
lastWasNoOp: false
|
|
38402
38486
|
};
|
|
@@ -38430,6 +38514,10 @@ async function runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWor
|
|
|
38430
38514
|
}
|
|
38431
38515
|
const isSessionContinuation = attempt > 1 && sessionConfirmedOpen;
|
|
38432
38516
|
if (isSessionContinuation) {
|
|
38517
|
+
if (state.checkSignatureChanged) {
|
|
38518
|
+
const attemptsRemaining = Math.max(1, maxAttempts - attempt + 1);
|
|
38519
|
+
return RectifierPromptBuilder.firstAttemptDelta(state.failedChecks, attemptsRemaining);
|
|
38520
|
+
}
|
|
38433
38521
|
return RectifierPromptBuilder.continuation(state.failedChecks, attempt, Math.min(rethinkAtAttempt, maxAttempts), Math.min(urgencyAtAttempt, maxAttempts));
|
|
38434
38522
|
}
|
|
38435
38523
|
let prompt = RectifierPromptBuilder.reviewRectification(state.failedChecks, ctx.story);
|
|
@@ -38587,7 +38675,12 @@ async function runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWor
|
|
|
38587
38675
|
}
|
|
38588
38676
|
}
|
|
38589
38677
|
if (updatedFailed.length > 0) {
|
|
38590
|
-
|
|
38678
|
+
const updatedCheckSignature = getCheckSignature(updatedFailed);
|
|
38679
|
+
state.checkSignatureChanged = updatedCheckSignature !== state.checkSignature;
|
|
38680
|
+
state.checkSignature = updatedCheckSignature;
|
|
38681
|
+
state.failedChecks.splice(0, state.failedChecks.length, ...updatedFailed);
|
|
38682
|
+
} else {
|
|
38683
|
+
state.checkSignatureChanged = false;
|
|
38591
38684
|
}
|
|
38592
38685
|
return false;
|
|
38593
38686
|
},
|
|
@@ -40094,7 +40187,14 @@ var init_isolation = __esm(() => {
|
|
|
40094
40187
|
});
|
|
40095
40188
|
|
|
40096
40189
|
// src/tdd/rectification-gate.ts
|
|
40097
|
-
async function
|
|
40190
|
+
async function getStoryChangedFiles(workdir, fromRef) {
|
|
40191
|
+
const result = await _rectificationGateDeps.executeWithTimeout(`git diff --name-only ${fromRef} HEAD`, 15, undefined, { cwd: workdir });
|
|
40192
|
+
if (!result.output)
|
|
40193
|
+
return new Set;
|
|
40194
|
+
return new Set(result.output.split(`
|
|
40195
|
+
`).map((l) => l.trim()).filter(Boolean));
|
|
40196
|
+
}
|
|
40197
|
+
async function runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, storyFromRef) {
|
|
40098
40198
|
const rectificationEnabled = config2.execution.rectification?.enabled ?? false;
|
|
40099
40199
|
if (!rectificationEnabled)
|
|
40100
40200
|
return { passed: false, cost: 0 };
|
|
@@ -40111,7 +40211,36 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
40111
40211
|
if (!fullSuitePassed && fullSuiteResult.output) {
|
|
40112
40212
|
const testSummary = _rectificationGateDeps.parseTestOutput(fullSuiteResult.output);
|
|
40113
40213
|
if (testSummary.failed > 0) {
|
|
40114
|
-
|
|
40214
|
+
let filteredFailures = testSummary.failures;
|
|
40215
|
+
if (storyFromRef && testSummary.failures.length > 0) {
|
|
40216
|
+
const storyFiles = await getStoryChangedFiles(workdir, storyFromRef);
|
|
40217
|
+
if (storyFiles.size > 0) {
|
|
40218
|
+
filteredFailures = testSummary.failures.filter((f) => storyFiles.has(f.file));
|
|
40219
|
+
}
|
|
40220
|
+
}
|
|
40221
|
+
const wasFiltered = filteredFailures.length < testSummary.failures.length;
|
|
40222
|
+
if (wasFiltered && filteredFailures.length === 0) {
|
|
40223
|
+
logger.info("tdd", "Full suite gate: all failures are pre-existing \u2014 accepting as pass", {
|
|
40224
|
+
storyId: story.id,
|
|
40225
|
+
suppressedCount: testSummary.failures.length,
|
|
40226
|
+
suppressedFiles: testSummary.failures.map((f) => f.file)
|
|
40227
|
+
});
|
|
40228
|
+
return { passed: true, cost: 0 };
|
|
40229
|
+
}
|
|
40230
|
+
if (wasFiltered) {
|
|
40231
|
+
logger.info("tdd", "Full suite gate: suppressed pre-existing failures", {
|
|
40232
|
+
storyId: story.id,
|
|
40233
|
+
total: testSummary.failures.length,
|
|
40234
|
+
suppressed: testSummary.failures.length - filteredFailures.length,
|
|
40235
|
+
remaining: filteredFailures.length
|
|
40236
|
+
});
|
|
40237
|
+
}
|
|
40238
|
+
const filteredSummary = {
|
|
40239
|
+
...testSummary,
|
|
40240
|
+
failures: filteredFailures,
|
|
40241
|
+
failed: wasFiltered ? filteredFailures.length : testSummary.failed
|
|
40242
|
+
};
|
|
40243
|
+
return await runRectificationLoop(story, config2, workdir, agentManager, implementerTier, lite, logger, filteredSummary, rectificationConfig, effectiveTestCmd, fullSuiteTimeout, featureName, projectDir);
|
|
40115
40244
|
}
|
|
40116
40245
|
if (testSummary.passed > 0) {
|
|
40117
40246
|
logger.info("tdd", "Full suite gate passed (non-zero exit, 0 failures, tests detected)", {
|
|
@@ -40958,7 +41087,7 @@ async function runThreeSessionTdd(options) {
|
|
|
40958
41087
|
lite
|
|
40959
41088
|
};
|
|
40960
41089
|
}
|
|
40961
|
-
const { passed: fullSuiteGatePassed, cost: fullSuiteGateCost } = await runFullSuiteGate(story, config2, workdir, wrapAdapterAsManager(agent), implementerTier, lite, logger, featureName, projectDir);
|
|
41090
|
+
const { passed: fullSuiteGatePassed, cost: fullSuiteGateCost } = await runFullSuiteGate(story, config2, workdir, wrapAdapterAsManager(agent), implementerTier, lite, logger, featureName, projectDir, initialRef);
|
|
40962
41091
|
const session3Ref = await captureGitRef(workdir) ?? "HEAD";
|
|
40963
41092
|
const verifierTier = config2.tdd.sessionTiers?.verifier ?? "fast";
|
|
40964
41093
|
const verifierBundle = await getTddContextBundle?.("verifier") ?? tddContextBundles?.verifier;
|
|
@@ -43370,7 +43499,7 @@ function coerceSmartTestRunner(val) {
|
|
|
43370
43499
|
return DEFAULT_SMART_RUNNER_CONFIG2;
|
|
43371
43500
|
if (val === false)
|
|
43372
43501
|
return { ...DEFAULT_SMART_RUNNER_CONFIG2, enabled: false };
|
|
43373
|
-
return val;
|
|
43502
|
+
return { ...DEFAULT_SMART_RUNNER_CONFIG2, ...val };
|
|
43374
43503
|
}
|
|
43375
43504
|
function buildScopedCommand2(testFiles, baseCommand, testScopedTemplate) {
|
|
43376
43505
|
if (testScopedTemplate) {
|
|
@@ -43427,6 +43556,10 @@ var init_verify = __esm(() => {
|
|
|
43427
43556
|
command: effectiveCommand
|
|
43428
43557
|
});
|
|
43429
43558
|
}
|
|
43559
|
+
} else if (!smartRunnerConfig.enabled) {
|
|
43560
|
+
logger.info("verify", "[smart-runner] Disabled by config", {
|
|
43561
|
+
storyId: ctx.story.id
|
|
43562
|
+
});
|
|
43430
43563
|
} else if (smartRunnerConfig.enabled) {
|
|
43431
43564
|
const repoRoot = ctx.projectDir ?? ctx.workdir;
|
|
43432
43565
|
const resolvedPatterns = await _verifyDeps.resolveTestFilePatterns(ctx.config, repoRoot, ctx.story.workdir);
|
|
@@ -43463,15 +43596,13 @@ var init_verify = __esm(() => {
|
|
|
43463
43596
|
}
|
|
43464
43597
|
}
|
|
43465
43598
|
if (isFullSuite && regressionMode === "deferred") {
|
|
43466
|
-
|
|
43467
|
-
|
|
43468
|
-
});
|
|
43599
|
+
const message = !isMonorepoOrchestrator && !smartRunnerConfig.enabled ? "[smart-runner] Disabled by config \u2014 deferring full suite to run-end (mode: deferred)" : "[smart-runner] No mapped tests \u2014 deferring full suite to run-end (mode: deferred)";
|
|
43600
|
+
logger.info("verify", message, { storyId: ctx.story.id });
|
|
43469
43601
|
return { action: "continue" };
|
|
43470
43602
|
}
|
|
43471
43603
|
if (isFullSuite) {
|
|
43472
|
-
|
|
43473
|
-
|
|
43474
|
-
});
|
|
43604
|
+
const message = !isMonorepoOrchestrator && !smartRunnerConfig.enabled ? "[smart-runner] Disabled by config \u2014 running full suite" : "[smart-runner] No mapped tests \u2014 falling back to full suite";
|
|
43605
|
+
logger.info("verify", message, { storyId: ctx.story.id });
|
|
43475
43606
|
}
|
|
43476
43607
|
logger.info("verify", isFullSuite ? "Running full suite" : "Running scoped tests", {
|
|
43477
43608
|
storyId: ctx.story.id,
|
|
@@ -44687,7 +44818,7 @@ var package_default;
|
|
|
44687
44818
|
var init_package = __esm(() => {
|
|
44688
44819
|
package_default = {
|
|
44689
44820
|
name: "@nathapp/nax",
|
|
44690
|
-
version: "0.63.0-canary.
|
|
44821
|
+
version: "0.63.0-canary.14",
|
|
44691
44822
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
44692
44823
|
type: "module",
|
|
44693
44824
|
bin: {
|
|
@@ -44768,8 +44899,8 @@ var init_version = __esm(() => {
|
|
|
44768
44899
|
NAX_VERSION = package_default.version;
|
|
44769
44900
|
NAX_COMMIT = (() => {
|
|
44770
44901
|
try {
|
|
44771
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
44772
|
-
return "
|
|
44902
|
+
if (/^[0-9a-f]{6,10}$/.test("374c8fbc"))
|
|
44903
|
+
return "374c8fbc";
|
|
44773
44904
|
} catch {}
|
|
44774
44905
|
try {
|
|
44775
44906
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -83991,7 +84122,8 @@ async function generateCommand(options) {
|
|
|
83991
84122
|
const suffix = dryRun ? " (dry run)" : "";
|
|
83992
84123
|
console.log(source_default.green(`\u2713 ${agent} \u2192 ${result.outputFile} (${result.content.length} bytes${suffix})`));
|
|
83993
84124
|
} else {
|
|
83994
|
-
|
|
84125
|
+
const projectNaxDir = findProjectDir(workdir);
|
|
84126
|
+
let configAgents = projectNaxDir ? config2?.generate?.agents : null;
|
|
83995
84127
|
const misplacedAgents = config2?.autoMode?.generate;
|
|
83996
84128
|
if (!configAgents && misplacedAgents?.agents && misplacedAgents.agents.length > 0) {
|
|
83997
84129
|
console.warn(source_default.yellow('\u26A0 Warning: "generate.agents" is nested under "autoMode" in your config \u2014 it should be at the top level.'));
|