@nathapp/nax 0.70.0-canary.3 → 0.70.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 +109 -29
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -32092,6 +32092,19 @@ ${STEP3_SHARED_RULES}
|
|
|
32092
32092
|
- **File output (REQUIRED)**: Write the acceptance test file DIRECTLY to the path shown below. Do NOT output the test code in your response. After writing the file, reply with a brief confirmation.
|
|
32093
32093
|
- **Path anchor (CRITICAL \u2014 do NOT deviate)**: Write the test file to this exact path: \`${p.targetTestFilePath}\`. This path is intentional and computed by the orchestrator \u2014 do not change it based on what you observe in the project. In particular: if you see a \`.nax/features/\` directory at the repo root, that is for stories scoped to the repo root. When a story belongs to a specific package (e.g. \`packages/core\`), its acceptance test lives inside that package's \`.nax/features/\` directory so the test runner can resolve the package's imports correctly. The package root is 3 levels above the test file (\`../../../\` relative to the test file).
|
|
32094
32094
|
- **Process cwd**: When spawning child processes to invoke a CLI or binary, set the working directory to the **package root** (\`join(import.meta.dir, "../../..")\`) as your default \u2014 unless your Step 2 exploration reveals the CLI uses a different working directory convention (e.g. reads config from \`~/.config/\`, or resolves paths relative to a flag value). Always check how the CLI resolves file paths before assuming.${implSection}`;
|
|
32095
|
+
}
|
|
32096
|
+
buildPathCorrection(targetTestFilePath) {
|
|
32097
|
+
return `The acceptance test file was NOT found at the required path. You likely wrote it to a different filename or directory (for example, by renaming a dotfile or replacing dashes with underscores).
|
|
32098
|
+
|
|
32099
|
+
Move (or re-write) the acceptance test you just created so it lives at EXACTLY this path:
|
|
32100
|
+
${targetTestFilePath}
|
|
32101
|
+
|
|
32102
|
+
Requirements:
|
|
32103
|
+
- The file must be at that exact path \u2014 same directory and same filename, including any leading dot and dashes. Do NOT sanitize, rename, or relocate it.
|
|
32104
|
+
- Preserve the test content you already wrote. Do not regenerate, weaken, or stub the assertions.
|
|
32105
|
+
- If you wrote it somewhere else, delete the misplaced copy after moving it so only the canonical path remains.
|
|
32106
|
+
|
|
32107
|
+
After writing the file to the exact path above, reply with a brief confirmation only.`;
|
|
32095
32108
|
}
|
|
32096
32109
|
buildGeneratorFromSpecPrompt(p) {
|
|
32097
32110
|
return `You are a senior test engineer. Your task is to generate a complete acceptance test file for the "${p.featureName}" feature.
|
|
@@ -34759,6 +34772,40 @@ ${outputFormat}`, overridable: false }
|
|
|
34759
34772
|
};
|
|
34760
34773
|
});
|
|
34761
34774
|
|
|
34775
|
+
// src/operations/self-heal.ts
|
|
34776
|
+
function makeSelfHealStep(spec) {
|
|
34777
|
+
return {
|
|
34778
|
+
async run(ctx) {
|
|
34779
|
+
const deviations = await spec.detect(ctx.input);
|
|
34780
|
+
if (deviations.length === 0)
|
|
34781
|
+
return null;
|
|
34782
|
+
if (spec.log) {
|
|
34783
|
+
getSafeLogger()?.info(spec.log.kind, spec.log.message, spec.log.meta?.(ctx.input, deviations) ?? {});
|
|
34784
|
+
}
|
|
34785
|
+
return ctx.send(spec.buildRepair(deviations, ctx.input));
|
|
34786
|
+
}
|
|
34787
|
+
};
|
|
34788
|
+
}
|
|
34789
|
+
async function runSelfHealChain(ctx, seed, steps) {
|
|
34790
|
+
let last = seed;
|
|
34791
|
+
let totalCost = seed.estimatedCostUsd ?? 0;
|
|
34792
|
+
for (const step of steps) {
|
|
34793
|
+
try {
|
|
34794
|
+
const turn = await step.run(ctx);
|
|
34795
|
+
if (turn) {
|
|
34796
|
+
totalCost += turn.estimatedCostUsd ?? 0;
|
|
34797
|
+
last = turn;
|
|
34798
|
+
}
|
|
34799
|
+
} catch (err) {
|
|
34800
|
+
getSafeLogger()?.warn("self-heal", "step threw \u2014 skipping", { error: errorMessage(err) });
|
|
34801
|
+
}
|
|
34802
|
+
}
|
|
34803
|
+
return { ...last, estimatedCostUsd: totalCost };
|
|
34804
|
+
}
|
|
34805
|
+
var init_self_heal = __esm(() => {
|
|
34806
|
+
init_logger2();
|
|
34807
|
+
});
|
|
34808
|
+
|
|
34762
34809
|
// src/operations/plan-refine.ts
|
|
34763
34810
|
import { join as join21 } from "path";
|
|
34764
34811
|
function hasToken(text, tokens) {
|
|
@@ -34897,6 +34944,28 @@ async function normalizeCreatedContextFiles(prd, workdir, fileExists) {
|
|
|
34897
34944
|
return prd;
|
|
34898
34945
|
return { ...prd, userStories: results.map((r) => r.story) };
|
|
34899
34946
|
}
|
|
34947
|
+
function verbatimSelfHealStep(builder) {
|
|
34948
|
+
return makeSelfHealStep({
|
|
34949
|
+
detect: (input) => readMissingVerbatimAcs(input),
|
|
34950
|
+
buildRepair: (missing, input) => builder.buildVerbatimRepair(missing, input.outputPath),
|
|
34951
|
+
log: {
|
|
34952
|
+
kind: "plan",
|
|
34953
|
+
message: "Refine dropped [verbatim] spec ACs \u2014 issuing one repair turn",
|
|
34954
|
+
meta: (input, missing) => ({ featureName: input.featureName, missingCount: missing.length })
|
|
34955
|
+
}
|
|
34956
|
+
});
|
|
34957
|
+
}
|
|
34958
|
+
function specDriftSelfHealStep(builder) {
|
|
34959
|
+
return makeSelfHealStep({
|
|
34960
|
+
detect: (input) => readSpecDriftViolations(input),
|
|
34961
|
+
buildRepair: (drifted, input) => builder.buildSpecDriftRepair(drifted, input.outputPath),
|
|
34962
|
+
log: {
|
|
34963
|
+
kind: "plan",
|
|
34964
|
+
message: "specGuard: spec-drift violations found \u2014 issuing one repair turn",
|
|
34965
|
+
meta: (input, drifted) => ({ featureName: input.featureName, violationCount: drifted.length })
|
|
34966
|
+
}
|
|
34967
|
+
});
|
|
34968
|
+
}
|
|
34900
34969
|
var _planRefineDeps, NEGATIVE_PATH_TOKENS, planRefineOp;
|
|
34901
34970
|
var init_plan_refine = __esm(() => {
|
|
34902
34971
|
init_retry();
|
|
@@ -34906,6 +34975,7 @@ var init_plan_refine = __esm(() => {
|
|
|
34906
34975
|
init_prd();
|
|
34907
34976
|
init_schema2();
|
|
34908
34977
|
init_prompts();
|
|
34978
|
+
init_self_heal();
|
|
34909
34979
|
init_verbatim_warn();
|
|
34910
34980
|
_planRefineDeps = {
|
|
34911
34981
|
readFile: async (path3) => {
|
|
@@ -34985,31 +35055,15 @@ ${outputFormat}`,
|
|
|
34985
35055
|
const specGuard = ctx.input.specGuard ?? false;
|
|
34986
35056
|
const turn1 = await ctx.sendWithParseRetry(initialPrompt);
|
|
34987
35057
|
const turn2 = await ctx.send(builder.buildRefineContinuation(ctx.input.outputPath, specGuard));
|
|
34988
|
-
|
|
34989
|
-
|
|
34990
|
-
|
|
34991
|
-
|
|
34992
|
-
|
|
34993
|
-
|
|
34994
|
-
|
|
34995
|
-
|
|
34996
|
-
|
|
34997
|
-
totalCost += turn3.estimatedCostUsd ?? 0;
|
|
34998
|
-
last = turn3;
|
|
34999
|
-
}
|
|
35000
|
-
if (specGuard) {
|
|
35001
|
-
const drifted = await readSpecDriftViolations(ctx.input);
|
|
35002
|
-
if (drifted.length > 0) {
|
|
35003
|
-
getSafeLogger()?.info("plan", "specGuard: spec-drift violations found \u2014 issuing one repair turn", {
|
|
35004
|
-
featureName: ctx.input.featureName,
|
|
35005
|
-
violationCount: drifted.length
|
|
35006
|
-
});
|
|
35007
|
-
const turn4 = await ctx.send(builder.buildSpecDriftRepair(drifted, ctx.input.outputPath));
|
|
35008
|
-
totalCost += turn4.estimatedCostUsd ?? 0;
|
|
35009
|
-
last = turn4;
|
|
35010
|
-
}
|
|
35011
|
-
}
|
|
35012
|
-
return { ...last, estimatedCostUsd: totalCost };
|
|
35058
|
+
const seed = {
|
|
35059
|
+
...turn2,
|
|
35060
|
+
estimatedCostUsd: (turn1.estimatedCostUsd ?? 0) + (turn2.estimatedCostUsd ?? 0)
|
|
35061
|
+
};
|
|
35062
|
+
const steps = [
|
|
35063
|
+
verbatimSelfHealStep(builder),
|
|
35064
|
+
...specGuard ? [specDriftSelfHealStep(builder)] : []
|
|
35065
|
+
];
|
|
35066
|
+
return runSelfHealChain(ctx, seed, steps);
|
|
35013
35067
|
},
|
|
35014
35068
|
parse(output, input) {
|
|
35015
35069
|
return validatePlanOutput(output, input.featureName, input.branchName);
|
|
@@ -36127,11 +36181,32 @@ function isStubTestContent(content) {
|
|
|
36127
36181
|
}
|
|
36128
36182
|
|
|
36129
36183
|
// src/operations/acceptance-generate.ts
|
|
36130
|
-
|
|
36184
|
+
function pathCorrectionStep() {
|
|
36185
|
+
return makeSelfHealStep({
|
|
36186
|
+
detect: async (input) => await _acceptanceGenerateDeps.fileExists(input.targetTestFilePath) ? [] : [input.targetTestFilePath],
|
|
36187
|
+
buildRepair: (_deviations, input) => new AcceptancePromptBuilder().buildPathCorrection(input.targetTestFilePath),
|
|
36188
|
+
log: {
|
|
36189
|
+
kind: "acceptance",
|
|
36190
|
+
message: "Acceptance test not found at target path \u2014 issuing one corrective turn",
|
|
36191
|
+
meta: (input) => ({ targetTestFilePath: input.targetTestFilePath })
|
|
36192
|
+
}
|
|
36193
|
+
});
|
|
36194
|
+
}
|
|
36195
|
+
var _acceptanceGenerateDeps, acceptanceGenerateOp;
|
|
36131
36196
|
var init_acceptance_generate = __esm(() => {
|
|
36132
36197
|
init_generator();
|
|
36133
36198
|
init_config();
|
|
36134
36199
|
init_prompts();
|
|
36200
|
+
init_self_heal();
|
|
36201
|
+
_acceptanceGenerateDeps = {
|
|
36202
|
+
fileExists: async (path4) => {
|
|
36203
|
+
try {
|
|
36204
|
+
return await Bun.file(path4).exists();
|
|
36205
|
+
} catch {
|
|
36206
|
+
return false;
|
|
36207
|
+
}
|
|
36208
|
+
}
|
|
36209
|
+
};
|
|
36135
36210
|
acceptanceGenerateOp = {
|
|
36136
36211
|
kind: "run",
|
|
36137
36212
|
name: "acceptance-generate",
|
|
@@ -36153,6 +36228,10 @@ var init_acceptance_generate = __esm(() => {
|
|
|
36153
36228
|
task: { id: "task", content: prompt, overridable: false }
|
|
36154
36229
|
};
|
|
36155
36230
|
},
|
|
36231
|
+
async hopBody(initialPrompt, ctx) {
|
|
36232
|
+
const turn1 = await ctx.sendWithParseRetry(initialPrompt);
|
|
36233
|
+
return runSelfHealChain(ctx, turn1, [pathCorrectionStep()]);
|
|
36234
|
+
},
|
|
36156
36235
|
parse(output, _input, _ctx) {
|
|
36157
36236
|
return { testCode: extractTestCode(output) };
|
|
36158
36237
|
},
|
|
@@ -40163,6 +40242,7 @@ var init_operations = __esm(() => {
|
|
|
40163
40242
|
init_call();
|
|
40164
40243
|
init_plan();
|
|
40165
40244
|
init_plan_refine();
|
|
40245
|
+
init_self_heal();
|
|
40166
40246
|
init_verbatim_warn();
|
|
40167
40247
|
init_decompose2();
|
|
40168
40248
|
init_build_hop_callback();
|
|
@@ -60245,7 +60325,7 @@ var package_default;
|
|
|
60245
60325
|
var init_package = __esm(() => {
|
|
60246
60326
|
package_default = {
|
|
60247
60327
|
name: "@nathapp/nax",
|
|
60248
|
-
version: "0.70.0-canary.
|
|
60328
|
+
version: "0.70.0-canary.4",
|
|
60249
60329
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
60250
60330
|
type: "module",
|
|
60251
60331
|
bin: {
|
|
@@ -60340,8 +60420,8 @@ var init_version = __esm(() => {
|
|
|
60340
60420
|
NAX_VERSION = package_default.version;
|
|
60341
60421
|
NAX_COMMIT = (() => {
|
|
60342
60422
|
try {
|
|
60343
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
60344
|
-
return "
|
|
60423
|
+
if (/^[0-9a-f]{6,10}$/.test("e2a854e7"))
|
|
60424
|
+
return "e2a854e7";
|
|
60345
60425
|
} catch {}
|
|
60346
60426
|
try {
|
|
60347
60427
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|