@nathapp/nax 0.63.0-canary.3 → 0.63.0-canary.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.
- package/dist/nax.js +137 -20
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -32984,17 +32984,82 @@ function resolveImport(specifier, fromFile, workdir) {
|
|
|
32984
32984
|
}
|
|
32985
32985
|
return null;
|
|
32986
32986
|
}
|
|
32987
|
-
function
|
|
32988
|
-
const
|
|
32989
|
-
if (
|
|
32987
|
+
function decomposeTestGlob(pattern) {
|
|
32988
|
+
const lastStar = pattern.lastIndexOf("*");
|
|
32989
|
+
if (lastStar === -1)
|
|
32990
32990
|
return null;
|
|
32991
|
-
const
|
|
32992
|
-
if (
|
|
32991
|
+
const suffix = pattern.slice(lastStar + 1);
|
|
32992
|
+
if (suffix.length === 0)
|
|
32993
32993
|
return null;
|
|
32994
|
-
const
|
|
32995
|
-
|
|
32994
|
+
const firstStar = pattern.indexOf("*");
|
|
32995
|
+
let prefix = pattern.slice(0, firstStar);
|
|
32996
|
+
if (prefix.endsWith("/"))
|
|
32997
|
+
prefix = prefix.slice(0, -1);
|
|
32998
|
+
return { prefix, suffix };
|
|
32999
|
+
}
|
|
33000
|
+
function deriveSiblingTestCandidates(filePath, patterns) {
|
|
33001
|
+
const srcExtMatch = filePath.match(/\.[^.]+$/);
|
|
33002
|
+
if (!srcExtMatch)
|
|
33003
|
+
return [];
|
|
33004
|
+
const srcExt = srcExtMatch[0];
|
|
33005
|
+
const stemWithPath = filePath.slice(0, -srcExt.length);
|
|
33006
|
+
for (const pattern of patterns) {
|
|
33007
|
+
const decomposed = decomposeTestGlob(pattern);
|
|
33008
|
+
if (decomposed && filePath.endsWith(decomposed.suffix))
|
|
33009
|
+
return [];
|
|
33010
|
+
if (decomposed) {
|
|
33011
|
+
const markerFromSuffix = stripExt(decomposed.suffix);
|
|
33012
|
+
if (markerFromSuffix.length > 0 && stemWithPath.endsWith(markerFromSuffix))
|
|
33013
|
+
return [];
|
|
33014
|
+
}
|
|
33015
|
+
}
|
|
33016
|
+
if (stemWithPath.endsWith(".test") || stemWithPath.endsWith(".spec"))
|
|
33017
|
+
return [];
|
|
33018
|
+
const srcPrefixed = stemWithPath.startsWith("src/");
|
|
33019
|
+
const srcInMiddleIdx = stemWithPath.indexOf("/src/");
|
|
33020
|
+
let innerStem = null;
|
|
33021
|
+
let pkgPrefix = "";
|
|
33022
|
+
if (srcPrefixed) {
|
|
33023
|
+
innerStem = stemWithPath.slice("src/".length);
|
|
33024
|
+
} else if (srcInMiddleIdx >= 0) {
|
|
33025
|
+
pkgPrefix = `${stemWithPath.slice(0, srcInMiddleIdx)}/`;
|
|
33026
|
+
innerStem = stemWithPath.slice(srcInMiddleIdx + "/src/".length);
|
|
33027
|
+
}
|
|
33028
|
+
const candidates = [];
|
|
33029
|
+
const seen = new Set;
|
|
33030
|
+
const push = (path8) => {
|
|
33031
|
+
if (path8 === filePath)
|
|
33032
|
+
return;
|
|
33033
|
+
if (!seen.has(path8)) {
|
|
33034
|
+
seen.add(path8);
|
|
33035
|
+
candidates.push(path8);
|
|
33036
|
+
}
|
|
33037
|
+
};
|
|
33038
|
+
for (const pattern of patterns) {
|
|
33039
|
+
const decomposed = decomposeTestGlob(pattern);
|
|
33040
|
+
if (!decomposed)
|
|
33041
|
+
continue;
|
|
33042
|
+
const { prefix, suffix } = decomposed;
|
|
33043
|
+
const suffixExt = (suffix.match(/\.[^.]+$/) ?? [""])[0];
|
|
33044
|
+
const marker = suffixExt ? suffix.slice(0, -suffixExt.length) : suffix;
|
|
33045
|
+
if (marker.length === 0)
|
|
33046
|
+
continue;
|
|
33047
|
+
const effectiveSuffix = `${marker}${srcExt}`;
|
|
33048
|
+
push(`${stemWithPath}${effectiveSuffix}`);
|
|
33049
|
+
if (innerStem !== null && prefix.length > 0) {
|
|
33050
|
+
push(`${pkgPrefix}${prefix}/${innerStem}${effectiveSuffix}`);
|
|
33051
|
+
}
|
|
33052
|
+
}
|
|
33053
|
+
return candidates;
|
|
32996
33054
|
}
|
|
32997
|
-
|
|
33055
|
+
function stripExt(s) {
|
|
33056
|
+
const m = s.match(/\.[^.]+$/);
|
|
33057
|
+
return m ? s.slice(0, -m[0].length) : s;
|
|
33058
|
+
}
|
|
33059
|
+
function isTestFile2(filePath, regex) {
|
|
33060
|
+
return regex.some((re) => re.test(filePath));
|
|
33061
|
+
}
|
|
33062
|
+
async function collectNeighbors(filePath, workdir, extraGlobWorkdirs, siblingTestContext) {
|
|
32998
33063
|
const neighbors = new Set;
|
|
32999
33064
|
if (await _codeNeighborDeps.fileExists(join20(workdir, filePath))) {
|
|
33000
33065
|
const content = await _codeNeighborDeps.readFile(join20(workdir, filePath));
|
|
@@ -33035,9 +33100,24 @@ async function collectNeighbors(filePath, workdir, extraGlobWorkdirs) {
|
|
|
33035
33100
|
await scanForReverseDeps(extraDir);
|
|
33036
33101
|
}
|
|
33037
33102
|
}
|
|
33038
|
-
|
|
33039
|
-
|
|
33040
|
-
|
|
33103
|
+
if (siblingTestContext && !isTestFile2(filePath, siblingTestContext.regex)) {
|
|
33104
|
+
const candidates = deriveSiblingTestCandidates(filePath, siblingTestContext.globs);
|
|
33105
|
+
let chosen = null;
|
|
33106
|
+
for (const candidate of candidates) {
|
|
33107
|
+
if (await _codeNeighborDeps.fileExists(join20(workdir, candidate))) {
|
|
33108
|
+
chosen = candidate;
|
|
33109
|
+
break;
|
|
33110
|
+
}
|
|
33111
|
+
}
|
|
33112
|
+
if (chosen === null) {
|
|
33113
|
+
const colocated = candidates[0];
|
|
33114
|
+
const mirrored = candidates.find((c, i) => i > 0 && c !== colocated);
|
|
33115
|
+
if (mirrored)
|
|
33116
|
+
chosen = mirrored;
|
|
33117
|
+
}
|
|
33118
|
+
if (chosen !== null && chosen !== filePath)
|
|
33119
|
+
neighbors.add(chosen);
|
|
33120
|
+
}
|
|
33041
33121
|
return [...neighbors].slice(0, MAX_NEIGHBORS_PER_FILE);
|
|
33042
33122
|
}
|
|
33043
33123
|
async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRoot, packageDir) {
|
|
@@ -33071,9 +33151,13 @@ class CodeNeighborProvider {
|
|
|
33071
33151
|
return { chunks: [], pullTools: [] };
|
|
33072
33152
|
}
|
|
33073
33153
|
const filesToProcess = touchedFiles.filter(isRelativeAndSafe).slice(0, MAX_FILES);
|
|
33154
|
+
const siblingTestContext = request.resolvedTestPatterns ? {
|
|
33155
|
+
globs: request.resolvedTestPatterns.globs,
|
|
33156
|
+
regex: request.resolvedTestPatterns.regex
|
|
33157
|
+
} : undefined;
|
|
33074
33158
|
const sections2 = [];
|
|
33075
33159
|
for (const file3 of filesToProcess) {
|
|
33076
|
-
const neighbors = await collectNeighbors(file3, workdir, extraGlobWorkdirs);
|
|
33160
|
+
const neighbors = await collectNeighbors(file3, workdir, extraGlobWorkdirs, siblingTestContext);
|
|
33077
33161
|
if (neighbors.length > 0) {
|
|
33078
33162
|
sections2.push(`### ${file3}
|
|
33079
33163
|
${neighbors.map((n) => `- ${n}`).join(`
|
|
@@ -33437,7 +33521,7 @@ class PullToolBudget {
|
|
|
33437
33521
|
return this.sessionCalls;
|
|
33438
33522
|
}
|
|
33439
33523
|
}
|
|
33440
|
-
async function handleQueryNeighbor(input, repoRoot, budget, maxTokensPerCall = DEFAULT_MAX_TOKENS_PER_CALL) {
|
|
33524
|
+
async function handleQueryNeighbor(input, repoRoot, budget, maxTokensPerCall = DEFAULT_MAX_TOKENS_PER_CALL, resolvedTestPatterns) {
|
|
33441
33525
|
budget.consume();
|
|
33442
33526
|
const provider = new CodeNeighborProvider;
|
|
33443
33527
|
const request = {
|
|
@@ -33447,7 +33531,8 @@ async function handleQueryNeighbor(input, repoRoot, budget, maxTokensPerCall = D
|
|
|
33447
33531
|
stage: "pull-tool",
|
|
33448
33532
|
role: "implementer",
|
|
33449
33533
|
budgetTokens: maxTokensPerCall,
|
|
33450
|
-
touchedFiles: [input.filePath]
|
|
33534
|
+
touchedFiles: [input.filePath],
|
|
33535
|
+
...resolvedTestPatterns && { resolvedTestPatterns }
|
|
33451
33536
|
};
|
|
33452
33537
|
const result = await provider.fetch(request);
|
|
33453
33538
|
const content = result.chunks.map((c) => c.content).join(`
|
|
@@ -35113,6 +35198,21 @@ function createContextToolRuntime(options) {
|
|
|
35113
35198
|
const budgets = new Map;
|
|
35114
35199
|
const runCounter = options.runCounter ?? createRunCallCounter();
|
|
35115
35200
|
const maxCallsPerRun = config2.context?.v2?.pull?.maxCallsPerRun ?? 50;
|
|
35201
|
+
let resolvedTestPatternsPromise = null;
|
|
35202
|
+
async function getResolvedTestPatterns() {
|
|
35203
|
+
if (resolvedTestPatternsPromise === null) {
|
|
35204
|
+
resolvedTestPatternsPromise = resolveTestFilePatterns(config2, repoRoot, story.workdir || undefined, {
|
|
35205
|
+
storyId: story.id
|
|
35206
|
+
}).catch((err) => {
|
|
35207
|
+
getLogger().warn("context", "Pull-tool runtime: failed to resolve test patterns", {
|
|
35208
|
+
storyId: story.id,
|
|
35209
|
+
error: errorMessage(err)
|
|
35210
|
+
});
|
|
35211
|
+
return;
|
|
35212
|
+
});
|
|
35213
|
+
}
|
|
35214
|
+
return resolvedTestPatternsPromise;
|
|
35215
|
+
}
|
|
35116
35216
|
function getBudget(tool) {
|
|
35117
35217
|
const existing = budgets.get(tool.name);
|
|
35118
35218
|
if (existing)
|
|
@@ -35128,8 +35228,10 @@ function createContextToolRuntime(options) {
|
|
|
35128
35228
|
throw new Error(`Unknown context tool: ${name}`);
|
|
35129
35229
|
}
|
|
35130
35230
|
switch (name) {
|
|
35131
|
-
case "query_neighbor":
|
|
35132
|
-
|
|
35231
|
+
case "query_neighbor": {
|
|
35232
|
+
const patterns = await getResolvedTestPatterns();
|
|
35233
|
+
return handleQueryNeighbor(input, repoRoot, getBudget(tool), tool.maxTokensPerCall, patterns);
|
|
35234
|
+
}
|
|
35133
35235
|
case "query_feature_context":
|
|
35134
35236
|
return handleQueryFeatureContext(input, story, config2, repoRoot, getBudget(tool), tool.maxTokensPerCall);
|
|
35135
35237
|
default:
|
|
@@ -35139,6 +35241,8 @@ function createContextToolRuntime(options) {
|
|
|
35139
35241
|
};
|
|
35140
35242
|
}
|
|
35141
35243
|
var init_tool_runtime = __esm(() => {
|
|
35244
|
+
init_logger2();
|
|
35245
|
+
init_resolver();
|
|
35142
35246
|
init_pull_tools();
|
|
35143
35247
|
});
|
|
35144
35248
|
|
|
@@ -38370,6 +38474,17 @@ async function runV2Path(ctx) {
|
|
|
38370
38474
|
}
|
|
38371
38475
|
}
|
|
38372
38476
|
const touchedFiles = getContextFiles(ctx.story);
|
|
38477
|
+
let resolvedTestPatterns;
|
|
38478
|
+
try {
|
|
38479
|
+
resolvedTestPatterns = await resolveTestFilePatterns(ctx.config, ctx.workdir, ctx.story.workdir || undefined, {
|
|
38480
|
+
storyId: ctx.story.id
|
|
38481
|
+
});
|
|
38482
|
+
} catch (err) {
|
|
38483
|
+
logger.warn("context", "Failed to resolve test-file patterns \u2014 providers will skip sibling-test hints", {
|
|
38484
|
+
storyId: ctx.story.id,
|
|
38485
|
+
error: errorMessage(err)
|
|
38486
|
+
});
|
|
38487
|
+
}
|
|
38373
38488
|
const request = {
|
|
38374
38489
|
storyId: ctx.story.id,
|
|
38375
38490
|
featureId: ctx.featureDir?.replace(/\/$/, "").split("/").pop(),
|
|
@@ -38391,7 +38506,8 @@ async function runV2Path(ctx) {
|
|
|
38391
38506
|
agentId: agentName,
|
|
38392
38507
|
availableBudgetTokens: estimateAvailableBudgetTokens(agentName, ctx.prompt),
|
|
38393
38508
|
deterministic: ctx.config.context.v2.deterministic,
|
|
38394
|
-
planDigestBoost: getStageContextConfig(ctx.routing?.testStrategy ?? "").planDigestBoost
|
|
38509
|
+
planDigestBoost: getStageContextConfig(ctx.routing?.testStrategy ?? "").planDigestBoost,
|
|
38510
|
+
...resolvedTestPatterns && { resolvedTestPatterns }
|
|
38395
38511
|
};
|
|
38396
38512
|
const pluginConfigs = ctx.config.context.v2.pluginProviders ?? [];
|
|
38397
38513
|
const pluginProviders = pluginConfigs.length > 0 ? await _contextStageDeps.loadPlugins(pluginConfigs, ctx.projectDir ?? ctx.workdir) : [];
|
|
@@ -38510,6 +38626,7 @@ var init_context2 = __esm(() => {
|
|
|
38510
38626
|
init_logger2();
|
|
38511
38627
|
init_prd();
|
|
38512
38628
|
init_scratch_writer();
|
|
38629
|
+
init_resolver();
|
|
38513
38630
|
_contextStageDeps = {
|
|
38514
38631
|
createOrchestrator: createDefaultOrchestrator,
|
|
38515
38632
|
loadPlugins: loadPluginProviders,
|
|
@@ -43433,7 +43550,7 @@ var package_default;
|
|
|
43433
43550
|
var init_package = __esm(() => {
|
|
43434
43551
|
package_default = {
|
|
43435
43552
|
name: "@nathapp/nax",
|
|
43436
|
-
version: "0.63.0-canary.
|
|
43553
|
+
version: "0.63.0-canary.4",
|
|
43437
43554
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
43438
43555
|
type: "module",
|
|
43439
43556
|
bin: {
|
|
@@ -43513,8 +43630,8 @@ var init_version = __esm(() => {
|
|
|
43513
43630
|
NAX_VERSION = package_default.version;
|
|
43514
43631
|
NAX_COMMIT = (() => {
|
|
43515
43632
|
try {
|
|
43516
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
43517
|
-
return "
|
|
43633
|
+
if (/^[0-9a-f]{6,10}$/.test("543ca776"))
|
|
43634
|
+
return "543ca776";
|
|
43518
43635
|
} catch {}
|
|
43519
43636
|
try {
|
|
43520
43637
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|