@nathapp/nax 0.63.0-canary.2 → 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 +139 -18
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -32984,14 +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
|
+
return null;
|
|
32991
|
+
const suffix = pattern.slice(lastStar + 1);
|
|
32992
|
+
if (suffix.length === 0)
|
|
32990
32993
|
return null;
|
|
32991
|
-
const
|
|
32992
|
-
|
|
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;
|
|
33054
|
+
}
|
|
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));
|
|
32993
33061
|
}
|
|
32994
|
-
async function collectNeighbors(filePath, workdir, extraGlobWorkdirs) {
|
|
33062
|
+
async function collectNeighbors(filePath, workdir, extraGlobWorkdirs, siblingTestContext) {
|
|
32995
33063
|
const neighbors = new Set;
|
|
32996
33064
|
if (await _codeNeighborDeps.fileExists(join20(workdir, filePath))) {
|
|
32997
33065
|
const content = await _codeNeighborDeps.readFile(join20(workdir, filePath));
|
|
@@ -33032,9 +33100,24 @@ async function collectNeighbors(filePath, workdir, extraGlobWorkdirs) {
|
|
|
33032
33100
|
await scanForReverseDeps(extraDir);
|
|
33033
33101
|
}
|
|
33034
33102
|
}
|
|
33035
|
-
|
|
33036
|
-
|
|
33037
|
-
|
|
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
|
+
}
|
|
33038
33121
|
return [...neighbors].slice(0, MAX_NEIGHBORS_PER_FILE);
|
|
33039
33122
|
}
|
|
33040
33123
|
async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRoot, packageDir) {
|
|
@@ -33068,9 +33151,13 @@ class CodeNeighborProvider {
|
|
|
33068
33151
|
return { chunks: [], pullTools: [] };
|
|
33069
33152
|
}
|
|
33070
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;
|
|
33071
33158
|
const sections2 = [];
|
|
33072
33159
|
for (const file3 of filesToProcess) {
|
|
33073
|
-
const neighbors = await collectNeighbors(file3, workdir, extraGlobWorkdirs);
|
|
33160
|
+
const neighbors = await collectNeighbors(file3, workdir, extraGlobWorkdirs, siblingTestContext);
|
|
33074
33161
|
if (neighbors.length > 0) {
|
|
33075
33162
|
sections2.push(`### ${file3}
|
|
33076
33163
|
${neighbors.map((n) => `- ${n}`).join(`
|
|
@@ -33434,7 +33521,7 @@ class PullToolBudget {
|
|
|
33434
33521
|
return this.sessionCalls;
|
|
33435
33522
|
}
|
|
33436
33523
|
}
|
|
33437
|
-
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) {
|
|
33438
33525
|
budget.consume();
|
|
33439
33526
|
const provider = new CodeNeighborProvider;
|
|
33440
33527
|
const request = {
|
|
@@ -33444,7 +33531,8 @@ async function handleQueryNeighbor(input, repoRoot, budget, maxTokensPerCall = D
|
|
|
33444
33531
|
stage: "pull-tool",
|
|
33445
33532
|
role: "implementer",
|
|
33446
33533
|
budgetTokens: maxTokensPerCall,
|
|
33447
|
-
touchedFiles: [input.filePath]
|
|
33534
|
+
touchedFiles: [input.filePath],
|
|
33535
|
+
...resolvedTestPatterns && { resolvedTestPatterns }
|
|
33448
33536
|
};
|
|
33449
33537
|
const result = await provider.fetch(request);
|
|
33450
33538
|
const content = result.chunks.map((c) => c.content).join(`
|
|
@@ -35110,6 +35198,21 @@ function createContextToolRuntime(options) {
|
|
|
35110
35198
|
const budgets = new Map;
|
|
35111
35199
|
const runCounter = options.runCounter ?? createRunCallCounter();
|
|
35112
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
|
+
}
|
|
35113
35216
|
function getBudget(tool) {
|
|
35114
35217
|
const existing = budgets.get(tool.name);
|
|
35115
35218
|
if (existing)
|
|
@@ -35125,8 +35228,10 @@ function createContextToolRuntime(options) {
|
|
|
35125
35228
|
throw new Error(`Unknown context tool: ${name}`);
|
|
35126
35229
|
}
|
|
35127
35230
|
switch (name) {
|
|
35128
|
-
case "query_neighbor":
|
|
35129
|
-
|
|
35231
|
+
case "query_neighbor": {
|
|
35232
|
+
const patterns = await getResolvedTestPatterns();
|
|
35233
|
+
return handleQueryNeighbor(input, repoRoot, getBudget(tool), tool.maxTokensPerCall, patterns);
|
|
35234
|
+
}
|
|
35130
35235
|
case "query_feature_context":
|
|
35131
35236
|
return handleQueryFeatureContext(input, story, config2, repoRoot, getBudget(tool), tool.maxTokensPerCall);
|
|
35132
35237
|
default:
|
|
@@ -35136,6 +35241,8 @@ function createContextToolRuntime(options) {
|
|
|
35136
35241
|
};
|
|
35137
35242
|
}
|
|
35138
35243
|
var init_tool_runtime = __esm(() => {
|
|
35244
|
+
init_logger2();
|
|
35245
|
+
init_resolver();
|
|
35139
35246
|
init_pull_tools();
|
|
35140
35247
|
});
|
|
35141
35248
|
|
|
@@ -38367,6 +38474,17 @@ async function runV2Path(ctx) {
|
|
|
38367
38474
|
}
|
|
38368
38475
|
}
|
|
38369
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
|
+
}
|
|
38370
38488
|
const request = {
|
|
38371
38489
|
storyId: ctx.story.id,
|
|
38372
38490
|
featureId: ctx.featureDir?.replace(/\/$/, "").split("/").pop(),
|
|
@@ -38388,7 +38506,8 @@ async function runV2Path(ctx) {
|
|
|
38388
38506
|
agentId: agentName,
|
|
38389
38507
|
availableBudgetTokens: estimateAvailableBudgetTokens(agentName, ctx.prompt),
|
|
38390
38508
|
deterministic: ctx.config.context.v2.deterministic,
|
|
38391
|
-
planDigestBoost: getStageContextConfig(ctx.routing?.testStrategy ?? "").planDigestBoost
|
|
38509
|
+
planDigestBoost: getStageContextConfig(ctx.routing?.testStrategy ?? "").planDigestBoost,
|
|
38510
|
+
...resolvedTestPatterns && { resolvedTestPatterns }
|
|
38392
38511
|
};
|
|
38393
38512
|
const pluginConfigs = ctx.config.context.v2.pluginProviders ?? [];
|
|
38394
38513
|
const pluginProviders = pluginConfigs.length > 0 ? await _contextStageDeps.loadPlugins(pluginConfigs, ctx.projectDir ?? ctx.workdir) : [];
|
|
@@ -38507,6 +38626,7 @@ var init_context2 = __esm(() => {
|
|
|
38507
38626
|
init_logger2();
|
|
38508
38627
|
init_prd();
|
|
38509
38628
|
init_scratch_writer();
|
|
38629
|
+
init_resolver();
|
|
38510
38630
|
_contextStageDeps = {
|
|
38511
38631
|
createOrchestrator: createDefaultOrchestrator,
|
|
38512
38632
|
loadPlugins: loadPluginProviders,
|
|
@@ -43430,7 +43550,7 @@ var package_default;
|
|
|
43430
43550
|
var init_package = __esm(() => {
|
|
43431
43551
|
package_default = {
|
|
43432
43552
|
name: "@nathapp/nax",
|
|
43433
|
-
version: "0.63.0-canary.
|
|
43553
|
+
version: "0.63.0-canary.4",
|
|
43434
43554
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
43435
43555
|
type: "module",
|
|
43436
43556
|
bin: {
|
|
@@ -43510,8 +43630,8 @@ var init_version = __esm(() => {
|
|
|
43510
43630
|
NAX_VERSION = package_default.version;
|
|
43511
43631
|
NAX_COMMIT = (() => {
|
|
43512
43632
|
try {
|
|
43513
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
43514
|
-
return "
|
|
43633
|
+
if (/^[0-9a-f]{6,10}$/.test("543ca776"))
|
|
43634
|
+
return "543ca776";
|
|
43515
43635
|
} catch {}
|
|
43516
43636
|
try {
|
|
43517
43637
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -45198,6 +45318,7 @@ class SessionManager {
|
|
|
45198
45318
|
continue;
|
|
45199
45319
|
const updated = { ...session, state: "COMPLETED", lastActivityAt: now };
|
|
45200
45320
|
this._sessions.set(id, updated);
|
|
45321
|
+
this._persistDescriptor(updated);
|
|
45201
45322
|
closed.push({ ...updated });
|
|
45202
45323
|
getLogger().debug("session", "Session closed by closeStory", {
|
|
45203
45324
|
storyId,
|