@nathapp/nax 0.70.2 → 0.70.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 +449 -295
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -18185,7 +18185,8 @@ class Logger {
|
|
|
18185
18185
|
mkdirSync(dir, { recursive: true });
|
|
18186
18186
|
}
|
|
18187
18187
|
} catch (error48) {
|
|
18188
|
-
|
|
18188
|
+
process.stderr.write(`[logger] Failed to create log directory: ${error48}
|
|
18189
|
+
`);
|
|
18189
18190
|
}
|
|
18190
18191
|
}
|
|
18191
18192
|
shouldLog(level) {
|
|
@@ -18254,7 +18255,8 @@ ${JSON.stringify(entry.data, null, 2)}`;
|
|
|
18254
18255
|
`;
|
|
18255
18256
|
const filePath = this.filePath;
|
|
18256
18257
|
this.writeQueueTail = this.writeQueueTail.then(() => appendFile(filePath, line).catch((error48) => {
|
|
18257
|
-
|
|
18258
|
+
process.stderr.write(`[logger] Failed to write to log file: ${error48}
|
|
18259
|
+
`);
|
|
18258
18260
|
}));
|
|
18259
18261
|
}
|
|
18260
18262
|
async flush() {
|
|
@@ -20898,7 +20900,7 @@ class AcpAgentAdapter {
|
|
|
20898
20900
|
session = await client.createSession({ agentName, permissionMode, sessionName: completeSessionName });
|
|
20899
20901
|
let timeoutId;
|
|
20900
20902
|
const timeoutPromise = new Promise((_, reject) => {
|
|
20901
|
-
timeoutId = setTimeout(() => reject(new
|
|
20903
|
+
timeoutId = setTimeout(() => reject(new NaxError("complete() timed out", "AGENT_TIMEOUT", { stage: "acp", timeoutMs })), timeoutMs);
|
|
20902
20904
|
});
|
|
20903
20905
|
timeoutPromise.catch(() => {});
|
|
20904
20906
|
let response;
|
|
@@ -21226,6 +21228,7 @@ class AcpAgentAdapter {
|
|
|
21226
21228
|
}
|
|
21227
21229
|
var MAX_AGENT_OUTPUT_CHARS = 5000, INTERACTION_TIMEOUT_MS, AGENT_REGISTRY, DEFAULT_ENTRY;
|
|
21228
21230
|
var init_adapter = __esm(() => {
|
|
21231
|
+
init_errors();
|
|
21229
21232
|
init_logger2();
|
|
21230
21233
|
init_cost();
|
|
21231
21234
|
init_types3();
|
|
@@ -24689,26 +24692,79 @@ function parsePytestOutput(output) {
|
|
|
24689
24692
|
});
|
|
24690
24693
|
}
|
|
24691
24694
|
}
|
|
24695
|
+
const verboseStacks = parsePytestVerboseStacks(output);
|
|
24696
|
+
for (const failure of failures) {
|
|
24697
|
+
const leafName = failure.testName.split(" > ").pop() ?? failure.testName;
|
|
24698
|
+
const stack = verboseStacks.get(leafName) ?? verboseStacks.get(failure.testName) ?? verboseStacks.get(failure.testName.replace(/ > /g, "."));
|
|
24699
|
+
if (stack && stack.length > 0) {
|
|
24700
|
+
failure.stackTrace = stack;
|
|
24701
|
+
}
|
|
24702
|
+
}
|
|
24692
24703
|
return {
|
|
24693
24704
|
passed: common.passed,
|
|
24694
24705
|
failed: common.failed,
|
|
24695
24706
|
failures: failures.length > 0 ? failures : common.failures
|
|
24696
24707
|
};
|
|
24697
24708
|
}
|
|
24709
|
+
function parsePytestVerboseStacks(output) {
|
|
24710
|
+
const result = new Map;
|
|
24711
|
+
const failuresIdx = output.indexOf("FAILURES");
|
|
24712
|
+
if (failuresIdx === -1)
|
|
24713
|
+
return result;
|
|
24714
|
+
let currentTest = null;
|
|
24715
|
+
for (const line of output.slice(failuresIdx).split(`
|
|
24716
|
+
`)) {
|
|
24717
|
+
const headerMatch = line.match(/^_{4,}\s+(.+?)\s+_{4,}$/);
|
|
24718
|
+
if (headerMatch) {
|
|
24719
|
+
currentTest = headerMatch[1].trim();
|
|
24720
|
+
continue;
|
|
24721
|
+
}
|
|
24722
|
+
if (currentTest) {
|
|
24723
|
+
const fileLineMatch = line.match(/^(\S+\.py):(\d+):/);
|
|
24724
|
+
if (fileLineMatch) {
|
|
24725
|
+
const entry = `${fileLineMatch[1]}:${fileLineMatch[2]}`;
|
|
24726
|
+
const existing = result.get(currentTest) ?? [];
|
|
24727
|
+
result.set(currentTest, [...existing, entry]);
|
|
24728
|
+
}
|
|
24729
|
+
}
|
|
24730
|
+
}
|
|
24731
|
+
return result;
|
|
24732
|
+
}
|
|
24698
24733
|
function parseGoTestOutput(output) {
|
|
24699
24734
|
const common = parseCommonOutput(output);
|
|
24700
24735
|
const failures = [];
|
|
24701
|
-
|
|
24702
|
-
`)
|
|
24703
|
-
|
|
24704
|
-
|
|
24736
|
+
const lines = output.split(`
|
|
24737
|
+
`);
|
|
24738
|
+
let i = 0;
|
|
24739
|
+
while (i < lines.length) {
|
|
24740
|
+
const failMatch = lines[i].match(/^--- FAIL:\s+(\S+)\s+\([\d.]+s\)/);
|
|
24741
|
+
if (failMatch) {
|
|
24742
|
+
const testName = failMatch[1];
|
|
24743
|
+
i++;
|
|
24744
|
+
const errorLines = [];
|
|
24745
|
+
while (i < lines.length) {
|
|
24746
|
+
const line = lines[i];
|
|
24747
|
+
if (!line.trim()) {
|
|
24748
|
+
i++;
|
|
24749
|
+
continue;
|
|
24750
|
+
}
|
|
24751
|
+
const errMatch = line.match(/^\s{4,}(\S+\.go):(\d+):\s+(.+)$/);
|
|
24752
|
+
if (errMatch) {
|
|
24753
|
+
errorLines.push({ file: errMatch[1], lineNum: errMatch[2], msg: errMatch[3] });
|
|
24754
|
+
i++;
|
|
24755
|
+
} else {
|
|
24756
|
+
break;
|
|
24757
|
+
}
|
|
24758
|
+
}
|
|
24705
24759
|
failures.push({
|
|
24706
|
-
file: "unknown",
|
|
24707
|
-
testName
|
|
24708
|
-
error: "Unknown error",
|
|
24709
|
-
stackTrace:
|
|
24760
|
+
file: errorLines[0]?.file ?? "unknown",
|
|
24761
|
+
testName,
|
|
24762
|
+
error: errorLines[0]?.msg ?? "Unknown error",
|
|
24763
|
+
stackTrace: errorLines.map((e) => `${e.file}:${e.lineNum}: ${e.msg}`)
|
|
24710
24764
|
});
|
|
24765
|
+
continue;
|
|
24711
24766
|
}
|
|
24767
|
+
i++;
|
|
24712
24768
|
}
|
|
24713
24769
|
return {
|
|
24714
24770
|
passed: common.passed,
|
|
@@ -28130,39 +28186,51 @@ function normalizeComplexity(raw) {
|
|
|
28130
28186
|
}
|
|
28131
28187
|
function validateStory(raw, index, allIds) {
|
|
28132
28188
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
28133
|
-
throw new
|
|
28189
|
+
throw new NaxError(`[schema] story[${index}] must be an object`, "SCHEMA_VALIDATION_FAILED", {
|
|
28190
|
+
stage: "schema",
|
|
28191
|
+
index
|
|
28192
|
+
});
|
|
28134
28193
|
}
|
|
28135
28194
|
const s = raw;
|
|
28136
28195
|
const rawId = s.id;
|
|
28137
28196
|
if (rawId === undefined || rawId === null || rawId === "") {
|
|
28138
|
-
throw new
|
|
28197
|
+
throw new NaxError(`[schema] story[${index}].id is required and must be non-empty`, "SCHEMA_VALIDATION_FAILED", {
|
|
28198
|
+
stage: "schema",
|
|
28199
|
+
index
|
|
28200
|
+
});
|
|
28139
28201
|
}
|
|
28140
28202
|
if (typeof rawId !== "string") {
|
|
28141
|
-
throw new
|
|
28203
|
+
throw new NaxError(`[schema] story[${index}].id must be a string`, "SCHEMA_VALIDATION_FAILED", {
|
|
28204
|
+
stage: "schema",
|
|
28205
|
+
index
|
|
28206
|
+
});
|
|
28142
28207
|
}
|
|
28143
28208
|
const id = normalizeStoryId(rawId);
|
|
28144
28209
|
validateStoryId(id);
|
|
28145
28210
|
const title = s.title;
|
|
28146
28211
|
if (!title || typeof title !== "string" || title.trim() === "") {
|
|
28147
|
-
throw new
|
|
28212
|
+
throw new NaxError(`[schema] story[${index}].title is required and must be non-empty`, "SCHEMA_VALIDATION_FAILED", {
|
|
28213
|
+
stage: "schema",
|
|
28214
|
+
index
|
|
28215
|
+
});
|
|
28148
28216
|
}
|
|
28149
28217
|
const description = s.description;
|
|
28150
28218
|
if (!description || typeof description !== "string" || description.trim() === "") {
|
|
28151
|
-
throw new
|
|
28219
|
+
throw new NaxError(`[schema] story[${index}].description is required and must be non-empty`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index });
|
|
28152
28220
|
}
|
|
28153
28221
|
const ac = s.acceptanceCriteria;
|
|
28154
28222
|
if (!Array.isArray(ac) || ac.length === 0) {
|
|
28155
|
-
throw new
|
|
28223
|
+
throw new NaxError(`[schema] story[${index}].acceptanceCriteria is required and must be a non-empty array`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index });
|
|
28156
28224
|
}
|
|
28157
28225
|
for (let i = 0;i < ac.length; i++) {
|
|
28158
28226
|
if (typeof ac[i] !== "string") {
|
|
28159
|
-
throw new
|
|
28227
|
+
throw new NaxError(`[schema] story[${index}].acceptanceCriteria[${i}] must be a string`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, acIndex: i });
|
|
28160
28228
|
}
|
|
28161
28229
|
}
|
|
28162
28230
|
let suggestedCriteria;
|
|
28163
28231
|
if (s.suggestedCriteria !== undefined && s.suggestedCriteria !== null) {
|
|
28164
28232
|
if (!Array.isArray(s.suggestedCriteria)) {
|
|
28165
|
-
throw new
|
|
28233
|
+
throw new NaxError(`[schema] story[${index}].suggestedCriteria must be an array when present`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index });
|
|
28166
28234
|
}
|
|
28167
28235
|
if (s.suggestedCriteria.length > 0) {
|
|
28168
28236
|
const coerced = [];
|
|
@@ -28173,7 +28241,7 @@ function validateStory(raw, index, allIds) {
|
|
|
28173
28241
|
} else if (item !== null && typeof item === "object" && typeof item.criterion === "string") {
|
|
28174
28242
|
coerced.push(item.criterion);
|
|
28175
28243
|
} else {
|
|
28176
|
-
throw new
|
|
28244
|
+
throw new NaxError(`[schema] story[${index}].suggestedCriteria[${i}] must be a string`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, scIndex: i });
|
|
28177
28245
|
}
|
|
28178
28246
|
}
|
|
28179
28247
|
suggestedCriteria = coerced;
|
|
@@ -28182,21 +28250,24 @@ function validateStory(raw, index, allIds) {
|
|
|
28182
28250
|
const routing = typeof s.routing === "object" && s.routing !== null ? s.routing : {};
|
|
28183
28251
|
const rawComplexity = routing.complexity ?? s.complexity;
|
|
28184
28252
|
if (rawComplexity === undefined || rawComplexity === null) {
|
|
28185
|
-
throw new
|
|
28253
|
+
throw new NaxError(`[schema] story[${index}] missing complexity. Set routing.complexity to one of: ${VALID_COMPLEXITY.join(", ")}`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index });
|
|
28186
28254
|
}
|
|
28187
28255
|
if (typeof rawComplexity !== "string") {
|
|
28188
|
-
throw new
|
|
28256
|
+
throw new NaxError(`[schema] story[${index}].routing.complexity must be a string`, "SCHEMA_VALIDATION_FAILED", {
|
|
28257
|
+
stage: "schema",
|
|
28258
|
+
index
|
|
28259
|
+
});
|
|
28189
28260
|
}
|
|
28190
28261
|
const complexity = normalizeComplexity(rawComplexity);
|
|
28191
28262
|
if (complexity === null) {
|
|
28192
|
-
throw new
|
|
28263
|
+
throw new NaxError(`[schema] story[${index}].routing.complexity "${rawComplexity}" is invalid. Valid values: ${VALID_COMPLEXITY.join(", ")}`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, rawComplexity });
|
|
28193
28264
|
}
|
|
28194
28265
|
const rawTestStrategy = routing.testStrategy ?? s.testStrategy;
|
|
28195
28266
|
let testStrategy = resolveTestStrategy(typeof rawTestStrategy === "string" ? rawTestStrategy : undefined);
|
|
28196
28267
|
const rawJustification = routing.noTestJustification ?? s.noTestJustification;
|
|
28197
28268
|
if (testStrategy === "no-test") {
|
|
28198
28269
|
if (!rawJustification || typeof rawJustification !== "string" || rawJustification.trim() === "") {
|
|
28199
|
-
throw new
|
|
28270
|
+
throw new NaxError(`[schema] story[${index}].routing.noTestJustification is required when testStrategy is "no-test"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index });
|
|
28200
28271
|
}
|
|
28201
28272
|
}
|
|
28202
28273
|
if (testStrategy !== "no-test" && typeof rawJustification === "string" && rawJustification.trim() !== "") {
|
|
@@ -28207,7 +28278,7 @@ function validateStory(raw, index, allIds) {
|
|
|
28207
28278
|
const dependencies = Array.isArray(rawDeps) ? rawDeps : [];
|
|
28208
28279
|
for (const dep of dependencies) {
|
|
28209
28280
|
if (!allIds.has(dep)) {
|
|
28210
|
-
throw new
|
|
28281
|
+
throw new NaxError(`[schema] story[${index}].dependencies references unknown story ID "${dep}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, dep });
|
|
28211
28282
|
}
|
|
28212
28283
|
}
|
|
28213
28284
|
const rawTags = s.tags;
|
|
@@ -28216,13 +28287,16 @@ function validateStory(raw, index, allIds) {
|
|
|
28216
28287
|
let workdir;
|
|
28217
28288
|
if (rawWorkdir !== undefined && rawWorkdir !== null) {
|
|
28218
28289
|
if (typeof rawWorkdir !== "string") {
|
|
28219
|
-
throw new
|
|
28290
|
+
throw new NaxError(`[schema] story[${index}].workdir must be a string`, "SCHEMA_VALIDATION_FAILED", {
|
|
28291
|
+
stage: "schema",
|
|
28292
|
+
index
|
|
28293
|
+
});
|
|
28220
28294
|
}
|
|
28221
28295
|
if (rawWorkdir.startsWith("/")) {
|
|
28222
|
-
throw new
|
|
28296
|
+
throw new NaxError(`[schema] story[${index}].workdir must be relative (no leading /): "${rawWorkdir}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, rawWorkdir });
|
|
28223
28297
|
}
|
|
28224
28298
|
if (rawWorkdir.includes("..")) {
|
|
28225
|
-
throw new
|
|
28299
|
+
throw new NaxError(`[schema] story[${index}].workdir must not contain '..': "${rawWorkdir}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, rawWorkdir });
|
|
28226
28300
|
}
|
|
28227
28301
|
workdir = rawWorkdir;
|
|
28228
28302
|
}
|
|
@@ -28234,10 +28308,10 @@ function validateStory(raw, index, allIds) {
|
|
|
28234
28308
|
if (f.trim() === "")
|
|
28235
28309
|
continue;
|
|
28236
28310
|
if (f.startsWith("/")) {
|
|
28237
|
-
throw new
|
|
28311
|
+
throw new NaxError(`[schema] story[${index}].contextFiles entry must be relative (no absolute paths): "${f}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, filePath: f });
|
|
28238
28312
|
}
|
|
28239
28313
|
if (f.includes("..")) {
|
|
28240
|
-
throw new
|
|
28314
|
+
throw new NaxError(`[schema] story[${index}].contextFiles entry must not contain '..': "${f}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, filePath: f });
|
|
28241
28315
|
}
|
|
28242
28316
|
contextFiles.push(f);
|
|
28243
28317
|
} else if (typeof f === "object" && f !== null && typeof f.path === "string") {
|
|
@@ -28246,10 +28320,10 @@ function validateStory(raw, index, allIds) {
|
|
|
28246
28320
|
if (path === "")
|
|
28247
28321
|
continue;
|
|
28248
28322
|
if (path.startsWith("/")) {
|
|
28249
|
-
throw new
|
|
28323
|
+
throw new NaxError(`[schema] story[${index}].contextFiles entry must be relative (no absolute paths): "${path}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, filePath: path });
|
|
28250
28324
|
}
|
|
28251
28325
|
if (path.includes("..")) {
|
|
28252
|
-
throw new
|
|
28326
|
+
throw new NaxError(`[schema] story[${index}].contextFiles entry must not contain '..': "${path}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, filePath: path });
|
|
28253
28327
|
}
|
|
28254
28328
|
const entry = { path };
|
|
28255
28329
|
if (typeof obj.factId === "string" && obj.factId.length > 0) {
|
|
@@ -28269,10 +28343,10 @@ function validateStory(raw, index, allIds) {
|
|
|
28269
28343
|
if (trimmed === "")
|
|
28270
28344
|
continue;
|
|
28271
28345
|
if (trimmed.startsWith("/")) {
|
|
28272
|
-
throw new
|
|
28346
|
+
throw new NaxError(`[schema] story[${index}].expectedFiles entry must be relative (no absolute paths): "${trimmed}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, filePath: trimmed });
|
|
28273
28347
|
}
|
|
28274
28348
|
if (trimmed.includes("..")) {
|
|
28275
|
-
throw new
|
|
28349
|
+
throw new NaxError(`[schema] story[${index}].expectedFiles entry must not contain '..': "${trimmed}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, filePath: trimmed });
|
|
28276
28350
|
}
|
|
28277
28351
|
expectedFiles.push(trimmed);
|
|
28278
28352
|
}
|
|
@@ -28282,7 +28356,7 @@ function validateStory(raw, index, allIds) {
|
|
|
28282
28356
|
if (s.verifiedBy !== undefined && s.verifiedBy !== null) {
|
|
28283
28357
|
const vb = s.verifiedBy;
|
|
28284
28358
|
if (typeof vb.kind !== "string" || !VALID_VERIFIED_BY_KINDS.includes(vb.kind)) {
|
|
28285
|
-
throw new
|
|
28359
|
+
throw new NaxError(`[schema] story[${index}].verifiedBy.kind "${vb.kind}" is invalid. Valid values: ${VALID_VERIFIED_BY_KINDS.join(", ")}`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, kind: vb.kind });
|
|
28286
28360
|
}
|
|
28287
28361
|
verifiedBy = {
|
|
28288
28362
|
kind: vb.kind,
|
|
@@ -28337,18 +28411,23 @@ function parseRawString(text) {
|
|
|
28337
28411
|
return JSON.parse(sanitized);
|
|
28338
28412
|
} catch (err) {
|
|
28339
28413
|
const parseErr = err;
|
|
28340
|
-
throw new
|
|
28414
|
+
throw new NaxError(`[schema] Failed to parse JSON: ${parseErr.message}`, "SCHEMA_VALIDATION_FAILED", {
|
|
28415
|
+
stage: "schema",
|
|
28416
|
+
cause: parseErr
|
|
28417
|
+
});
|
|
28341
28418
|
}
|
|
28342
28419
|
}
|
|
28343
28420
|
function validatePlanOutput(raw, feature, branch) {
|
|
28344
28421
|
const parsed = typeof raw === "string" ? parseRawString(raw) : raw;
|
|
28345
28422
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
28346
|
-
throw new
|
|
28423
|
+
throw new NaxError("[schema] PRD output must be a JSON object", "SCHEMA_VALIDATION_FAILED", { stage: "schema" });
|
|
28347
28424
|
}
|
|
28348
28425
|
const obj = parsed;
|
|
28349
28426
|
const rawStories = obj.userStories;
|
|
28350
28427
|
if (!Array.isArray(rawStories) || rawStories.length === 0) {
|
|
28351
|
-
throw new
|
|
28428
|
+
throw new NaxError("[schema] userStories is required and must be a non-empty array", "SCHEMA_VALIDATION_FAILED", {
|
|
28429
|
+
stage: "schema"
|
|
28430
|
+
});
|
|
28352
28431
|
}
|
|
28353
28432
|
const allIds = new Set;
|
|
28354
28433
|
for (const story of rawStories) {
|
|
@@ -28375,6 +28454,7 @@ function validatePlanOutput(raw, feature, branch) {
|
|
|
28375
28454
|
var VALID_COMPLEXITY, STORY_ID_NO_SEPARATOR;
|
|
28376
28455
|
var init_schema2 = __esm(() => {
|
|
28377
28456
|
init_test_strategy();
|
|
28457
|
+
init_errors();
|
|
28378
28458
|
VALID_COMPLEXITY = ["simple", "medium", "complex", "expert"];
|
|
28379
28459
|
STORY_ID_NO_SEPARATOR = /^([A-Za-z]+)(\d+)$/;
|
|
28380
28460
|
});
|
|
@@ -28676,7 +28756,12 @@ async function scanTestFiles(options) {
|
|
|
28676
28756
|
describes
|
|
28677
28757
|
});
|
|
28678
28758
|
}
|
|
28679
|
-
} catch {
|
|
28759
|
+
} catch (err) {
|
|
28760
|
+
getLogger().debug("test-scanner", "Failed to read file during glob scan", {
|
|
28761
|
+
path: fullPath,
|
|
28762
|
+
error: errorMessage(err)
|
|
28763
|
+
});
|
|
28764
|
+
}
|
|
28680
28765
|
}
|
|
28681
28766
|
files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
28682
28767
|
return files;
|
|
@@ -35298,145 +35383,11 @@ var init_decompose = __esm(() => {
|
|
|
35298
35383
|
init_errors();
|
|
35299
35384
|
});
|
|
35300
35385
|
|
|
35301
|
-
// src/agents/shared/decompose-prompt.ts
|
|
35302
|
-
function buildPlanModeInstructions(targetStoryId, maxAcCount) {
|
|
35303
|
-
const acConstraint = maxAcCount != null ? `
|
|
35304
|
-
## Acceptance Criteria Constraint
|
|
35305
|
-
|
|
35306
|
-
Every sub-story must have at most ${maxAcCount} acceptance criteria. If a story would exceed this limit, split it into additional sub-stories instead of adding more ACs.` : "";
|
|
35307
|
-
return `Decompose the target story (${targetStoryId}) into smaller, implementable sub-stories.
|
|
35308
|
-
|
|
35309
|
-
${COMPLEXITY_GUIDE}
|
|
35310
|
-
|
|
35311
|
-
${TEST_STRATEGY_GUIDE}
|
|
35312
|
-
|
|
35313
|
-
${GROUPING_RULES}${acConstraint}`;
|
|
35314
|
-
}
|
|
35315
|
-
function buildDecomposePromptSync(input) {
|
|
35316
|
-
if (input.targetStory) {
|
|
35317
|
-
return buildPlanModePromptSync(input);
|
|
35318
|
-
}
|
|
35319
|
-
return buildSpecModePromptSync(input);
|
|
35320
|
-
}
|
|
35321
|
-
async function buildDecomposePromptAsync(options) {
|
|
35322
|
-
if (options.targetStory) {
|
|
35323
|
-
return buildPlanModePrompt(options);
|
|
35324
|
-
}
|
|
35325
|
-
return buildSpecModePrompt(options);
|
|
35326
|
-
}
|
|
35327
|
-
function buildPlanModePromptSync(input) {
|
|
35328
|
-
const targetStory = input.targetStory;
|
|
35329
|
-
const siblings = input.siblings ?? [];
|
|
35330
|
-
const instructions = buildPlanModeInstructions(targetStory.id, input.maxAcCount ?? null);
|
|
35331
|
-
let builder = OneShotPromptBuilder.for("decomposer").instructions(instructions).inputData("Target Story", JSON.stringify(targetStory, null, 2)).inputData("Codebase Context", input.codebaseContext);
|
|
35332
|
-
if (siblings.length > 0) {
|
|
35333
|
-
const siblingsSummary = siblings.map((s) => `- ${s.id}: ${s.title}`).join(`
|
|
35334
|
-
`);
|
|
35335
|
-
builder = builder.inputData("Sibling Stories", siblingsSummary);
|
|
35336
|
-
}
|
|
35337
|
-
return builder.agentProfiles(input.profiles ?? []).jsonSchema(DECOMPOSE_PLAN_SCHEMA).build();
|
|
35338
|
-
}
|
|
35339
|
-
function buildSpecModePromptSync(input) {
|
|
35340
|
-
return OneShotPromptBuilder.for("decomposer").instructions(SPEC_DECOMPOSE_INSTRUCTIONS).inputData("Codebase Context", input.codebaseContext).inputData("Feature Specification", input.specContent).agentProfiles(input.profiles ?? []).jsonSchema(DECOMPOSE_SPEC_SCHEMA).build();
|
|
35341
|
-
}
|
|
35342
|
-
async function buildPlanModePrompt(options) {
|
|
35343
|
-
const targetStory = options.targetStory;
|
|
35344
|
-
const siblings = options.siblings ?? [];
|
|
35345
|
-
const maxAcCount = options.maxAcCount ?? null;
|
|
35346
|
-
const instructions = buildPlanModeInstructions(targetStory.id, maxAcCount);
|
|
35347
|
-
let builder = OneShotPromptBuilder.for("decomposer").instructions(instructions).inputData("Target Story", JSON.stringify(targetStory, null, 2)).inputData("Codebase Context", options.codebaseContext);
|
|
35348
|
-
if (siblings.length > 0) {
|
|
35349
|
-
const siblingsSummary = siblings.map((s) => `- ${s.id}: ${s.title}`).join(`
|
|
35350
|
-
`);
|
|
35351
|
-
builder = builder.inputData("Sibling Stories", siblingsSummary);
|
|
35352
|
-
}
|
|
35353
|
-
return builder.agentProfiles(options.profiles ?? []).jsonSchema(DECOMPOSE_PLAN_SCHEMA).build();
|
|
35354
|
-
}
|
|
35355
|
-
async function buildSpecModePrompt(options) {
|
|
35356
|
-
return OneShotPromptBuilder.for("decomposer").instructions(SPEC_DECOMPOSE_INSTRUCTIONS).inputData("Codebase Context", options.codebaseContext).inputData("Feature Specification", options.specContent).agentProfiles(options.profiles ?? []).jsonSchema(DECOMPOSE_SPEC_SCHEMA).build();
|
|
35357
|
-
}
|
|
35358
|
-
var DECOMPOSE_SPEC_SCHEMA, DECOMPOSE_PLAN_SCHEMA, SPEC_DECOMPOSE_INSTRUCTIONS;
|
|
35359
|
-
var init_decompose_prompt = __esm(() => {
|
|
35360
|
-
init_test_strategy();
|
|
35361
|
-
init_prompts();
|
|
35362
|
-
DECOMPOSE_SPEC_SCHEMA = {
|
|
35363
|
-
name: "DecomposedStory[]",
|
|
35364
|
-
description: "Respond with ONLY a JSON array \u2014 no markdown code fences, no explanation text before or after.",
|
|
35365
|
-
example: [
|
|
35366
|
-
{
|
|
35367
|
-
id: "US-001",
|
|
35368
|
-
title: "Story title",
|
|
35369
|
-
description: "Story description",
|
|
35370
|
-
acceptanceCriteria: ["Criterion 1"],
|
|
35371
|
-
tags: ["tag1"],
|
|
35372
|
-
dependencies: [],
|
|
35373
|
-
complexity: "medium",
|
|
35374
|
-
contextFiles: ["src/path/to/file.ts"],
|
|
35375
|
-
reasoning: "Why this complexity level",
|
|
35376
|
-
estimatedLOC: 150,
|
|
35377
|
-
risks: ["Risk 1"],
|
|
35378
|
-
testStrategy: "test-after",
|
|
35379
|
-
agentProfileId: ""
|
|
35380
|
-
}
|
|
35381
|
-
]
|
|
35382
|
-
};
|
|
35383
|
-
DECOMPOSE_PLAN_SCHEMA = {
|
|
35384
|
-
name: "SubStory[]",
|
|
35385
|
-
description: "Return a JSON array of sub-stories \u2014 no markdown code fences, no explanation \u2014 JSON array only.",
|
|
35386
|
-
example: [
|
|
35387
|
-
{
|
|
35388
|
-
id: "US-001-A",
|
|
35389
|
-
title: "Sub-story title",
|
|
35390
|
-
description: "Description",
|
|
35391
|
-
acceptanceCriteria: ["Behavioral, testable criterion"],
|
|
35392
|
-
contextFiles: ["src/path/to/file.ts"],
|
|
35393
|
-
tags: [],
|
|
35394
|
-
dependencies: [],
|
|
35395
|
-
complexity: "simple",
|
|
35396
|
-
reasoning: "Why this complexity",
|
|
35397
|
-
estimatedLOC: 0,
|
|
35398
|
-
risks: [],
|
|
35399
|
-
testStrategy: "no-test | tdd-simple | three-session-tdd-lite | three-session-tdd | test-after",
|
|
35400
|
-
agentProfileId: ""
|
|
35401
|
-
}
|
|
35402
|
-
]
|
|
35403
|
-
};
|
|
35404
|
-
SPEC_DECOMPOSE_INSTRUCTIONS = `Break down the feature specification into user stories and classify each story's complexity.
|
|
35405
|
-
|
|
35406
|
-
For each story, provide:
|
|
35407
|
-
1. id: Story ID (e.g., "US-001")
|
|
35408
|
-
2. title: Concise story title
|
|
35409
|
-
3. description: What needs to be implemented
|
|
35410
|
-
4. acceptanceCriteria: Array of testable criteria
|
|
35411
|
-
5. tags: Array of routing tags (e.g., ["security", "api"])
|
|
35412
|
-
6. dependencies: Array of story IDs this depends on (e.g., ["US-001"])
|
|
35413
|
-
7. complexity: "simple" | "medium" | "complex" | "expert"
|
|
35414
|
-
8. contextFiles: Array of file paths to inject into agent prompt before execution
|
|
35415
|
-
9. reasoning: Why this complexity level
|
|
35416
|
-
10. estimatedLOC: Estimated lines of code to change
|
|
35417
|
-
11. risks: Array of implementation risks
|
|
35418
|
-
12. testStrategy: "no-test" | "test-after" | "tdd-simple" | "three-session-tdd" | "three-session-tdd-lite"
|
|
35419
|
-
13. noTestJustification: string (REQUIRED when testStrategy is "no-test" \u2014 explain why tests are unnecessary)
|
|
35420
|
-
14. agentProfileId: (optional) profile id from the Agent Profiles table \u2014 assign the best-matching profile for each story; omit if no profiles are listed or none fits well
|
|
35421
|
-
|
|
35422
|
-
${COMPLEXITY_GUIDE}
|
|
35423
|
-
|
|
35424
|
-
${TEST_STRATEGY_GUIDE}
|
|
35425
|
-
|
|
35426
|
-
${GROUPING_RULES}
|
|
35427
|
-
|
|
35428
|
-
Consider:
|
|
35429
|
-
1. Does infrastructure exist? (e.g., "add caching" when no cache layer exists = complex)
|
|
35430
|
-
2. How many files will be touched?
|
|
35431
|
-
3. Are there cross-cutting concerns (auth, validation, error handling)?
|
|
35432
|
-
4. Does it require new dependencies or architectural decisions?`;
|
|
35433
|
-
});
|
|
35434
|
-
|
|
35435
35386
|
// src/operations/decompose.ts
|
|
35436
35387
|
var decomposeOp;
|
|
35437
35388
|
var init_decompose2 = __esm(() => {
|
|
35389
|
+
init_prompts();
|
|
35438
35390
|
init_decompose();
|
|
35439
|
-
init_decompose_prompt();
|
|
35440
35391
|
init_config();
|
|
35441
35392
|
init_logger2();
|
|
35442
35393
|
decomposeOp = {
|
|
@@ -35892,10 +35843,10 @@ var init_routing = __esm(() => {
|
|
|
35892
35843
|
});
|
|
35893
35844
|
|
|
35894
35845
|
// src/operations/classify-route.ts
|
|
35895
|
-
var
|
|
35896
|
-
Respond with JSON only \u2014 no explanation text before or after.`, classifyRouteOp, BATCH_ROUTING_SCHEMA_INLINE = `Respond with a JSON array \u2014 no explanation, no markdown.
|
|
35846
|
+
var classifyRouteOp, BATCH_ROUTING_SCHEMA_INLINE = `Respond with a JSON array \u2014 no explanation, no markdown.
|
|
35897
35847
|
Example: [{"id":"US-001","complexity":"simple","modelTier":"fast","reasoning":"<one line>"}]`, classifyRouteBatchOp;
|
|
35898
35848
|
var init_classify_route = __esm(() => {
|
|
35849
|
+
init_prompts();
|
|
35899
35850
|
init_config();
|
|
35900
35851
|
init_routing();
|
|
35901
35852
|
init_llm_parsing();
|
|
@@ -35923,7 +35874,7 @@ ${criteria}`,
|
|
|
35923
35874
|
].join(`
|
|
35924
35875
|
`);
|
|
35925
35876
|
return {
|
|
35926
|
-
role: { id: "role", content:
|
|
35877
|
+
role: { id: "role", content: OneShotPromptBuilder.classifierRoleContent(), overridable: false },
|
|
35927
35878
|
task: { id: "task", content: `${ROUTING_INSTRUCTIONS}
|
|
35928
35879
|
|
|
35929
35880
|
## Story
|
|
@@ -35970,7 +35921,7 @@ ${storyBlocks}
|
|
|
35970
35921
|
|
|
35971
35922
|
${BATCH_ROUTING_SCHEMA_INLINE}`;
|
|
35972
35923
|
return {
|
|
35973
|
-
role: { id: "role", content:
|
|
35924
|
+
role: { id: "role", content: OneShotPromptBuilder.classifierRoleContent(), overridable: false },
|
|
35974
35925
|
task: { id: "task", content: taskContent, overridable: false }
|
|
35975
35926
|
};
|
|
35976
35927
|
},
|
|
@@ -37536,7 +37487,7 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
|
|
|
37536
37487
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
37537
37488
|
const kind = pickSelectorKind(stageConfig);
|
|
37538
37489
|
if ((kind === "majority-fail-closed" || kind === "majority-fail-open") && workdir !== undefined) {
|
|
37539
|
-
logger?.warn("debate", "majority resolver does not support implementer session resumption \u2014 switch to synthesis or custom resolver for context-aware semantic review");
|
|
37490
|
+
logger?.warn("debate", "majority resolver does not support implementer session resumption \u2014 switch to synthesis or custom resolver for context-aware semantic review", { storyId });
|
|
37540
37491
|
}
|
|
37541
37492
|
const proposalList = debaters ? debaters.map((debater, i) => ({
|
|
37542
37493
|
debater,
|
|
@@ -38472,7 +38423,7 @@ Options:`);
|
|
|
38472
38423
|
const prompt = OneShotPromptBuilder.for("auto-approver").instructions(AUTO_APPROVER_INSTRUCTIONS).inputData("Interaction Request", requestLines.join(`
|
|
38473
38424
|
`)).jsonSchema(AUTO_APPROVER_SCHEMA).build();
|
|
38474
38425
|
return {
|
|
38475
|
-
role: { id: "role", content:
|
|
38426
|
+
role: { id: "role", content: OneShotPromptBuilder.autoApproverRoleContent(), overridable: false },
|
|
38476
38427
|
task: { id: "task", content: prompt, overridable: false }
|
|
38477
38428
|
};
|
|
38478
38429
|
},
|
|
@@ -39330,7 +39281,12 @@ var init_full_suite_rectify_op = __esm(() => {
|
|
|
39330
39281
|
},
|
|
39331
39282
|
parse(output, _input, _ctx) {
|
|
39332
39283
|
const declarations = parseTestEditDeclarations(output);
|
|
39333
|
-
|
|
39284
|
+
const unresolvedMatch = output.match(/^UNRESOLVED:\s*(.+)$/m);
|
|
39285
|
+
return {
|
|
39286
|
+
applied: true,
|
|
39287
|
+
testEditDeclarations: declarations,
|
|
39288
|
+
...unresolvedMatch ? { unresolvedReason: unresolvedMatch[1]?.trim() } : {}
|
|
39289
|
+
};
|
|
39334
39290
|
}
|
|
39335
39291
|
};
|
|
39336
39292
|
});
|
|
@@ -39352,7 +39308,13 @@ function makeFullSuiteRectifyStrategy(story, config2, sink) {
|
|
|
39352
39308
|
sink.testEdits.push(d);
|
|
39353
39309
|
}
|
|
39354
39310
|
}
|
|
39355
|
-
|
|
39311
|
+
const hasDeclarations = output.testEditDeclarations.length > 0;
|
|
39312
|
+
const unresolved = output.unresolvedReason && !hasDeclarations ? output.unresolvedReason : undefined;
|
|
39313
|
+
return {
|
|
39314
|
+
targetFiles: [],
|
|
39315
|
+
summary: unresolved ?? "Fixed failing tests",
|
|
39316
|
+
...unresolved ? { unresolved } : {}
|
|
39317
|
+
};
|
|
39356
39318
|
},
|
|
39357
39319
|
maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
|
|
39358
39320
|
coRun: "exclusive"
|
|
@@ -39413,6 +39375,7 @@ var init__finding_to_check = __esm(() => {
|
|
|
39413
39375
|
function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
|
|
39414
39376
|
const claimsAdversarial = opts.includeAdversarialReview === true;
|
|
39415
39377
|
const adversarialReviewByFixTarget = opts.adversarialReviewByFixTarget;
|
|
39378
|
+
const blockingThreshold = opts.promptSeverityFloor ?? config2.review?.blockingThreshold;
|
|
39416
39379
|
return {
|
|
39417
39380
|
name: "autofix-implementer",
|
|
39418
39381
|
appliesTo: (f) => (f.fixTarget === "source" || f.fixTarget == null) && IMPLEMENTER_SOURCES.has(f.source) || adversarialReviewByFixTarget !== undefined && f.source === "adversarial-review" && f.fixTarget === adversarialReviewByFixTarget || adversarialReviewByFixTarget === undefined && claimsAdversarial && f.source === "adversarial-review",
|
|
@@ -39420,7 +39383,8 @@ function makeAutofixImplementerStrategy(story, config2, sink, opts = {}) {
|
|
|
39420
39383
|
buildInput: (findings, _prior, _cycleCtx) => ({
|
|
39421
39384
|
failedChecks: findingsToFailedChecks(findings),
|
|
39422
39385
|
story,
|
|
39423
|
-
findings
|
|
39386
|
+
findings,
|
|
39387
|
+
blockingThreshold
|
|
39424
39388
|
}),
|
|
39425
39389
|
extractApplied: (output) => {
|
|
39426
39390
|
for (const decl of output.testEditDeclarations) {
|
|
@@ -39449,6 +39413,7 @@ var init_autofix_implementer_strategy = __esm(() => {
|
|
|
39449
39413
|
// src/operations/autofix-test-writer-strategy.ts
|
|
39450
39414
|
function makeAutofixTestWriterStrategy(story, config2, sink, opts = {}) {
|
|
39451
39415
|
const includeAdversarial = opts.includeAdversarialReview !== false;
|
|
39416
|
+
const blockingThreshold = opts.promptSeverityFloor ?? config2.review?.blockingThreshold;
|
|
39452
39417
|
return {
|
|
39453
39418
|
name: "autofix-test-writer",
|
|
39454
39419
|
appliesTo: (f) => f.fixTarget === "test" || includeAdversarial && f.source === "adversarial-review" || sink.mockHandoffs.length > 0,
|
|
@@ -39473,7 +39438,7 @@ function makeAutofixTestWriterStrategy(story, config2, sink, opts = {}) {
|
|
|
39473
39438
|
failedChecks: findingsToFailedChecks(findings),
|
|
39474
39439
|
story,
|
|
39475
39440
|
mode: "mock-restructure",
|
|
39476
|
-
blockingThreshold
|
|
39441
|
+
blockingThreshold,
|
|
39477
39442
|
handoffReason,
|
|
39478
39443
|
handoffFiles
|
|
39479
39444
|
};
|
|
@@ -39481,7 +39446,7 @@ function makeAutofixTestWriterStrategy(story, config2, sink, opts = {}) {
|
|
|
39481
39446
|
return {
|
|
39482
39447
|
failedChecks: findingsToFailedChecks(findings),
|
|
39483
39448
|
story,
|
|
39484
|
-
blockingThreshold
|
|
39449
|
+
blockingThreshold
|
|
39485
39450
|
};
|
|
39486
39451
|
},
|
|
39487
39452
|
maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
|
|
@@ -43351,6 +43316,13 @@ ${OneShotPromptBuilder.agentProfileInstruction()}`
|
|
|
43351
43316
|
].join(`
|
|
43352
43317
|
`);
|
|
43353
43318
|
}
|
|
43319
|
+
static classifierRoleContent() {
|
|
43320
|
+
return `You are a story classifier that assigns complexity and model tier to user stories.
|
|
43321
|
+
Respond with JSON only \u2014 no explanation text before or after.`;
|
|
43322
|
+
}
|
|
43323
|
+
static autoApproverRoleContent() {
|
|
43324
|
+
return "You are an AI orchestration decision-maker.";
|
|
43325
|
+
}
|
|
43354
43326
|
static agentCapabilityCards(profiles) {
|
|
43355
43327
|
if (profiles.length === 0)
|
|
43356
43328
|
return "";
|
|
@@ -44038,6 +44010,140 @@ Please enhance the original proposal to address these runner-up criteria while m
|
|
|
44038
44010
|
}
|
|
44039
44011
|
}
|
|
44040
44012
|
|
|
44013
|
+
// src/prompts/builders/decompose-builder.ts
|
|
44014
|
+
function buildPlanModeInstructions(targetStoryId, maxAcCount) {
|
|
44015
|
+
const acConstraint = maxAcCount != null ? `
|
|
44016
|
+
## Acceptance Criteria Constraint
|
|
44017
|
+
|
|
44018
|
+
Every sub-story must have at most ${maxAcCount} acceptance criteria. If a story would exceed this limit, split it into additional sub-stories instead of adding more ACs.` : "";
|
|
44019
|
+
return `Decompose the target story (${targetStoryId}) into smaller, implementable sub-stories.
|
|
44020
|
+
|
|
44021
|
+
${COMPLEXITY_GUIDE}
|
|
44022
|
+
|
|
44023
|
+
${TEST_STRATEGY_GUIDE}
|
|
44024
|
+
|
|
44025
|
+
${GROUPING_RULES}${acConstraint}`;
|
|
44026
|
+
}
|
|
44027
|
+
function buildDecomposePromptSync(input) {
|
|
44028
|
+
if (input.targetStory) {
|
|
44029
|
+
return buildPlanModePromptSync(input);
|
|
44030
|
+
}
|
|
44031
|
+
return buildSpecModePromptSync(input);
|
|
44032
|
+
}
|
|
44033
|
+
async function buildDecomposePromptAsync(options) {
|
|
44034
|
+
if (options.targetStory) {
|
|
44035
|
+
return buildPlanModePrompt(options);
|
|
44036
|
+
}
|
|
44037
|
+
return buildSpecModePrompt(options);
|
|
44038
|
+
}
|
|
44039
|
+
function buildPlanModePromptSync(input) {
|
|
44040
|
+
const targetStory = input.targetStory;
|
|
44041
|
+
const siblings = input.siblings ?? [];
|
|
44042
|
+
const instructions = buildPlanModeInstructions(targetStory.id, input.maxAcCount ?? null);
|
|
44043
|
+
let builder = OneShotPromptBuilder.for("decomposer").instructions(instructions).inputData("Target Story", JSON.stringify(targetStory, null, 2)).inputData("Codebase Context", input.codebaseContext);
|
|
44044
|
+
if (siblings.length > 0) {
|
|
44045
|
+
const siblingsSummary = siblings.map((s) => `- ${s.id}: ${s.title}`).join(`
|
|
44046
|
+
`);
|
|
44047
|
+
builder = builder.inputData("Sibling Stories", siblingsSummary);
|
|
44048
|
+
}
|
|
44049
|
+
return builder.agentProfiles(input.profiles ?? []).jsonSchema(DECOMPOSE_PLAN_SCHEMA).build();
|
|
44050
|
+
}
|
|
44051
|
+
function buildSpecModePromptSync(input) {
|
|
44052
|
+
return OneShotPromptBuilder.for("decomposer").instructions(SPEC_DECOMPOSE_INSTRUCTIONS).inputData("Codebase Context", input.codebaseContext).inputData("Feature Specification", input.specContent).agentProfiles(input.profiles ?? []).jsonSchema(DECOMPOSE_SPEC_SCHEMA).build();
|
|
44053
|
+
}
|
|
44054
|
+
async function buildPlanModePrompt(options) {
|
|
44055
|
+
const targetStory = options.targetStory;
|
|
44056
|
+
const siblings = options.siblings ?? [];
|
|
44057
|
+
const maxAcCount = options.maxAcCount ?? null;
|
|
44058
|
+
const instructions = buildPlanModeInstructions(targetStory.id, maxAcCount);
|
|
44059
|
+
let builder = OneShotPromptBuilder.for("decomposer").instructions(instructions).inputData("Target Story", JSON.stringify(targetStory, null, 2)).inputData("Codebase Context", options.codebaseContext);
|
|
44060
|
+
if (siblings.length > 0) {
|
|
44061
|
+
const siblingsSummary = siblings.map((s) => `- ${s.id}: ${s.title}`).join(`
|
|
44062
|
+
`);
|
|
44063
|
+
builder = builder.inputData("Sibling Stories", siblingsSummary);
|
|
44064
|
+
}
|
|
44065
|
+
return builder.agentProfiles(options.profiles ?? []).jsonSchema(DECOMPOSE_PLAN_SCHEMA).build();
|
|
44066
|
+
}
|
|
44067
|
+
async function buildSpecModePrompt(options) {
|
|
44068
|
+
return OneShotPromptBuilder.for("decomposer").instructions(SPEC_DECOMPOSE_INSTRUCTIONS).inputData("Codebase Context", options.codebaseContext).inputData("Feature Specification", options.specContent).agentProfiles(options.profiles ?? []).jsonSchema(DECOMPOSE_SPEC_SCHEMA).build();
|
|
44069
|
+
}
|
|
44070
|
+
var DECOMPOSE_SPEC_SCHEMA, DECOMPOSE_PLAN_SCHEMA, SPEC_DECOMPOSE_INSTRUCTIONS;
|
|
44071
|
+
var init_decompose_builder = __esm(() => {
|
|
44072
|
+
init_test_strategy();
|
|
44073
|
+
init_one_shot_builder();
|
|
44074
|
+
DECOMPOSE_SPEC_SCHEMA = {
|
|
44075
|
+
name: "DecomposedStory[]",
|
|
44076
|
+
description: "Respond with ONLY a JSON array \u2014 no markdown code fences, no explanation text before or after.",
|
|
44077
|
+
example: [
|
|
44078
|
+
{
|
|
44079
|
+
id: "US-001",
|
|
44080
|
+
title: "Story title",
|
|
44081
|
+
description: "Story description",
|
|
44082
|
+
acceptanceCriteria: ["Criterion 1"],
|
|
44083
|
+
tags: ["tag1"],
|
|
44084
|
+
dependencies: [],
|
|
44085
|
+
complexity: "medium",
|
|
44086
|
+
contextFiles: ["src/path/to/file.ts"],
|
|
44087
|
+
reasoning: "Why this complexity level",
|
|
44088
|
+
estimatedLOC: 150,
|
|
44089
|
+
risks: ["Risk 1"],
|
|
44090
|
+
testStrategy: "test-after",
|
|
44091
|
+
agentProfileId: ""
|
|
44092
|
+
}
|
|
44093
|
+
]
|
|
44094
|
+
};
|
|
44095
|
+
DECOMPOSE_PLAN_SCHEMA = {
|
|
44096
|
+
name: "SubStory[]",
|
|
44097
|
+
description: "Return a JSON array of sub-stories \u2014 no markdown code fences, no explanation \u2014 JSON array only.",
|
|
44098
|
+
example: [
|
|
44099
|
+
{
|
|
44100
|
+
id: "US-001-A",
|
|
44101
|
+
title: "Sub-story title",
|
|
44102
|
+
description: "Description",
|
|
44103
|
+
acceptanceCriteria: ["Behavioral, testable criterion"],
|
|
44104
|
+
contextFiles: ["src/path/to/file.ts"],
|
|
44105
|
+
tags: [],
|
|
44106
|
+
dependencies: [],
|
|
44107
|
+
complexity: "simple",
|
|
44108
|
+
reasoning: "Why this complexity",
|
|
44109
|
+
estimatedLOC: 0,
|
|
44110
|
+
risks: [],
|
|
44111
|
+
testStrategy: "no-test | tdd-simple | three-session-tdd-lite | three-session-tdd | test-after",
|
|
44112
|
+
agentProfileId: ""
|
|
44113
|
+
}
|
|
44114
|
+
]
|
|
44115
|
+
};
|
|
44116
|
+
SPEC_DECOMPOSE_INSTRUCTIONS = `Break down the feature specification into user stories and classify each story's complexity.
|
|
44117
|
+
|
|
44118
|
+
For each story, provide:
|
|
44119
|
+
1. id: Story ID (e.g., "US-001")
|
|
44120
|
+
2. title: Concise story title
|
|
44121
|
+
3. description: What needs to be implemented
|
|
44122
|
+
4. acceptanceCriteria: Array of testable criteria
|
|
44123
|
+
5. tags: Array of routing tags (e.g., ["security", "api"])
|
|
44124
|
+
6. dependencies: Array of story IDs this depends on (e.g., ["US-001"])
|
|
44125
|
+
7. complexity: "simple" | "medium" | "complex" | "expert"
|
|
44126
|
+
8. contextFiles: Array of file paths to inject into agent prompt before execution
|
|
44127
|
+
9. reasoning: Why this complexity level
|
|
44128
|
+
10. estimatedLOC: Estimated lines of code to change
|
|
44129
|
+
11. risks: Array of implementation risks
|
|
44130
|
+
12. testStrategy: "no-test" | "test-after" | "tdd-simple" | "three-session-tdd" | "three-session-tdd-lite"
|
|
44131
|
+
13. noTestJustification: string (REQUIRED when testStrategy is "no-test" \u2014 explain why tests are unnecessary)
|
|
44132
|
+
14. agentProfileId: (optional) profile id from the Agent Profiles table \u2014 assign the best-matching profile for each story; omit if no profiles are listed or none fits well
|
|
44133
|
+
|
|
44134
|
+
${COMPLEXITY_GUIDE}
|
|
44135
|
+
|
|
44136
|
+
${TEST_STRATEGY_GUIDE}
|
|
44137
|
+
|
|
44138
|
+
${GROUPING_RULES}
|
|
44139
|
+
|
|
44140
|
+
Consider:
|
|
44141
|
+
1. Does infrastructure exist? (e.g., "add caching" when no cache layer exists = complex)
|
|
44142
|
+
2. How many files will be touched?
|
|
44143
|
+
3. Are there cross-cutting concerns (auth, validation, error handling)?
|
|
44144
|
+
4. Does it require new dependencies or architectural decisions?`;
|
|
44145
|
+
});
|
|
44146
|
+
|
|
44041
44147
|
// src/prompts/index.ts
|
|
44042
44148
|
var init_prompts = __esm(() => {
|
|
44043
44149
|
init_tdd_builder();
|
|
@@ -44052,6 +44158,7 @@ var init_prompts = __esm(() => {
|
|
|
44052
44158
|
init_plan_builder();
|
|
44053
44159
|
init_types5();
|
|
44054
44160
|
init_compose2();
|
|
44161
|
+
init_decompose_builder();
|
|
44055
44162
|
});
|
|
44056
44163
|
|
|
44057
44164
|
// src/operations/build-hop-callback.ts
|
|
@@ -47963,7 +48070,7 @@ async function runHybrid(ctx, prompt) {
|
|
|
47963
48070
|
const resolved = [];
|
|
47964
48071
|
for (const debater of debaters) {
|
|
47965
48072
|
if (!agentManager.getAgent(debater.agent)) {
|
|
47966
|
-
logger?.warn("debate", `Agent '${debater.agent}' not found \u2014 skipping debater
|
|
48073
|
+
logger?.warn("debate", `Agent '${debater.agent}' not found \u2014 skipping debater`, { storyId: ctx.storyId });
|
|
47967
48074
|
continue;
|
|
47968
48075
|
}
|
|
47969
48076
|
resolved.push({ debater, agentName: debater.agent });
|
|
@@ -48912,7 +49019,7 @@ async function runStateful(ctx, prompt) {
|
|
|
48912
49019
|
const resolved = debaters.flatMap((debater) => ctx.agentManager.getAgent(debater.agent) ? [{ debater, agentName: debater.agent }] : []);
|
|
48913
49020
|
for (const debater of debaters) {
|
|
48914
49021
|
if (!ctx.agentManager.getAgent(debater.agent)) {
|
|
48915
|
-
logger?.warn("debate", `Agent '${debater.agent}' not found \u2014 skipping debater
|
|
49022
|
+
logger?.warn("debate", `Agent '${debater.agent}' not found \u2014 skipping debater`, { storyId: ctx.storyId });
|
|
48916
49023
|
}
|
|
48917
49024
|
}
|
|
48918
49025
|
logger?.info("debate", "debate:start", {
|
|
@@ -49083,7 +49190,7 @@ class DebateRunner {
|
|
|
49083
49190
|
if (sessionMode === "stateful")
|
|
49084
49191
|
return this.runSessionModeWithScopes(prompt, runHybrid);
|
|
49085
49192
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
49086
|
-
logger?.warn("debate", `hybrid mode requires sessionMode: stateful, but got '${sessionMode}' \u2014 falling back to one-shot
|
|
49193
|
+
logger?.warn("debate", `hybrid mode requires sessionMode: stateful, but got '${sessionMode}' \u2014 falling back to one-shot`, { storyId: this.ctx.storyId });
|
|
49087
49194
|
return this.runPanelOneShot(prompt);
|
|
49088
49195
|
}
|
|
49089
49196
|
if (sessionMode === "stateful")
|
|
@@ -49127,7 +49234,9 @@ class DebateRunner {
|
|
|
49127
49234
|
const resolved = [];
|
|
49128
49235
|
for (const debater of debaters) {
|
|
49129
49236
|
if (!agentManager.getAgent(debater.agent)) {
|
|
49130
|
-
logger?.warn("debate", `Agent '${debater.agent}' not found \u2014 skipping debater
|
|
49237
|
+
logger?.warn("debate", `Agent '${debater.agent}' not found \u2014 skipping debater`, {
|
|
49238
|
+
storyId: this.ctx.storyId
|
|
49239
|
+
});
|
|
49131
49240
|
continue;
|
|
49132
49241
|
}
|
|
49133
49242
|
resolved.push({ debater, agentName: debater.agent });
|
|
@@ -49442,13 +49551,13 @@ class InteractionChain {
|
|
|
49442
49551
|
async send(request) {
|
|
49443
49552
|
const plugin = this.getPrimary();
|
|
49444
49553
|
if (!plugin) {
|
|
49445
|
-
throw new
|
|
49554
|
+
throw new NaxError("No interaction plugin registered", "INTERACTION_ERROR", { stage: "run" });
|
|
49446
49555
|
}
|
|
49447
49556
|
await plugin.send(request);
|
|
49448
49557
|
}
|
|
49449
49558
|
async receive(requestId, timeout) {
|
|
49450
49559
|
if (this.plugins.length === 0) {
|
|
49451
|
-
throw new
|
|
49560
|
+
throw new NaxError("No interaction plugin registered", "INTERACTION_ERROR", { stage: "run", requestId });
|
|
49452
49561
|
}
|
|
49453
49562
|
const timeoutMs = timeout ?? this.config.defaultTimeout;
|
|
49454
49563
|
const errors3 = [];
|
|
@@ -49462,7 +49571,11 @@ class InteractionChain {
|
|
|
49462
49571
|
}
|
|
49463
49572
|
}
|
|
49464
49573
|
const errorMessages = errors3.map((e) => e.message).join("; ");
|
|
49465
|
-
throw new
|
|
49574
|
+
throw new NaxError(`All interaction plugins failed: ${errorMessages}`, "INTERACTION_ERROR", {
|
|
49575
|
+
stage: "run",
|
|
49576
|
+
requestId,
|
|
49577
|
+
pluginCount: this.plugins.length
|
|
49578
|
+
});
|
|
49466
49579
|
}
|
|
49467
49580
|
async prompt(request) {
|
|
49468
49581
|
await this.send(request);
|
|
@@ -49512,19 +49625,29 @@ class InteractionChain {
|
|
|
49512
49625
|
}
|
|
49513
49626
|
}
|
|
49514
49627
|
}
|
|
49628
|
+
var init_chain = __esm(() => {
|
|
49629
|
+
init_errors();
|
|
49630
|
+
});
|
|
49515
49631
|
|
|
49516
49632
|
// src/interaction/state.ts
|
|
49517
49633
|
import * as path6 from "path";
|
|
49518
49634
|
function validateInteractionId(id) {
|
|
49519
49635
|
if (!SAFE_INTERACTION_ID_RE.test(id)) {
|
|
49520
|
-
throw new
|
|
49636
|
+
throw new NaxError(`Invalid interaction ID \u2014 must match [a-zA-Z0-9_-]{1,128}: ${id}`, "INTERACTION_ERROR", {
|
|
49637
|
+
stage: "run",
|
|
49638
|
+
id
|
|
49639
|
+
});
|
|
49521
49640
|
}
|
|
49522
49641
|
}
|
|
49523
49642
|
function assertPathWithin(filePath, baseDir) {
|
|
49524
49643
|
const resolved = path6.resolve(filePath);
|
|
49525
49644
|
const base = path6.resolve(baseDir);
|
|
49526
49645
|
if (!resolved.startsWith(base + path6.sep) && resolved !== base) {
|
|
49527
|
-
throw new
|
|
49646
|
+
throw new NaxError(`Path traversal detected: ${filePath} is outside ${baseDir}`, "INTERACTION_ERROR", {
|
|
49647
|
+
stage: "run",
|
|
49648
|
+
filePath,
|
|
49649
|
+
baseDir
|
|
49650
|
+
});
|
|
49528
49651
|
}
|
|
49529
49652
|
}
|
|
49530
49653
|
async function loadPendingInteraction(requestId, featureDir) {
|
|
@@ -49565,6 +49688,7 @@ async function listPendingInteractions(featureDir) {
|
|
|
49565
49688
|
}
|
|
49566
49689
|
var SAFE_INTERACTION_ID_RE;
|
|
49567
49690
|
var init_state = __esm(() => {
|
|
49691
|
+
init_errors();
|
|
49568
49692
|
SAFE_INTERACTION_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
49569
49693
|
});
|
|
49570
49694
|
|
|
@@ -50875,6 +50999,7 @@ async function initInteractionChain(config2, headless) {
|
|
|
50875
50999
|
}
|
|
50876
51000
|
var init_init = __esm(() => {
|
|
50877
51001
|
init_logger2();
|
|
51002
|
+
init_chain();
|
|
50878
51003
|
init_auto();
|
|
50879
51004
|
init_cli();
|
|
50880
51005
|
init_telegram();
|
|
@@ -50917,6 +51042,7 @@ var init_bridge_builder = __esm(() => {
|
|
|
50917
51042
|
// src/interaction/index.ts
|
|
50918
51043
|
var init_interaction = __esm(() => {
|
|
50919
51044
|
init_types8();
|
|
51045
|
+
init_chain();
|
|
50920
51046
|
init_state();
|
|
50921
51047
|
init_cli();
|
|
50922
51048
|
init_telegram();
|
|
@@ -52346,9 +52472,9 @@ async function runReplanLoop(workdir, config2, options) {
|
|
|
52346
52472
|
_planDeps.processExit(1);
|
|
52347
52473
|
}
|
|
52348
52474
|
var init_plan_decompose = __esm(() => {
|
|
52475
|
+
init_prompts();
|
|
52349
52476
|
init_agents();
|
|
52350
52477
|
init_decompose();
|
|
52351
|
-
init_decompose_prompt();
|
|
52352
52478
|
init_errors();
|
|
52353
52479
|
init_logger2();
|
|
52354
52480
|
init_operations();
|
|
@@ -54396,18 +54522,20 @@ var init_constitution2 = __esm(() => {
|
|
|
54396
54522
|
if (result) {
|
|
54397
54523
|
ctx.constitution = result;
|
|
54398
54524
|
logger.debug("constitution", "Constitution loaded", {
|
|
54525
|
+
storyId: ctx.story.id,
|
|
54399
54526
|
tokens: result.tokens,
|
|
54400
54527
|
truncated: result.truncated
|
|
54401
54528
|
});
|
|
54402
54529
|
if (result.truncated) {
|
|
54403
54530
|
logger.warn("constitution", "Constitution truncated", {
|
|
54531
|
+
storyId: ctx.story.id,
|
|
54404
54532
|
originalTokens: result.originalTokens,
|
|
54405
54533
|
tokens: result.tokens,
|
|
54406
54534
|
maxTokens: ctx.config.constitution.maxTokens
|
|
54407
54535
|
});
|
|
54408
54536
|
}
|
|
54409
54537
|
} else {
|
|
54410
|
-
logger.debug("constitution", "Constitution not found or failed to load");
|
|
54538
|
+
logger.debug("constitution", "Constitution not found or failed to load", { storyId: ctx.story.id });
|
|
54411
54539
|
}
|
|
54412
54540
|
return { action: "continue" };
|
|
54413
54541
|
}
|
|
@@ -55640,7 +55768,11 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides)
|
|
|
55640
55768
|
});
|
|
55641
55769
|
}
|
|
55642
55770
|
if (EXHAUSTED_EXIT_REASONS.has(cycleResult.exitReason) && cycleResult.finalFindings.length > 0) {
|
|
55643
|
-
return {
|
|
55771
|
+
return {
|
|
55772
|
+
rectificationExhausted: true,
|
|
55773
|
+
unfixedFindings: cycleResult.finalFindings,
|
|
55774
|
+
...cycleResult.unresolvedDetail ? { unresolvedDetail: cycleResult.unresolvedDetail } : {}
|
|
55775
|
+
};
|
|
55644
55776
|
}
|
|
55645
55777
|
if (cycleResult.exitReason === "validate-short-circuit") {
|
|
55646
55778
|
return { liteScopeIncomplete: true };
|
|
@@ -56047,18 +56179,24 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
56047
56179
|
const nbSink = makeDeclarationSink();
|
|
56048
56180
|
if (nbf.scope === "source") {
|
|
56049
56181
|
nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
|
|
56050
|
-
includeAdversarialReview: true
|
|
56182
|
+
includeAdversarialReview: true,
|
|
56183
|
+
promptSeverityFloor: "info"
|
|
56051
56184
|
}));
|
|
56052
56185
|
} else if (nbf.scope === "triage") {
|
|
56053
56186
|
nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
|
|
56054
|
-
adversarialReviewByFixTarget: "source"
|
|
56187
|
+
adversarialReviewByFixTarget: "source",
|
|
56188
|
+
promptSeverityFloor: "info"
|
|
56055
56189
|
}), makeAutofixTestWriterStrategy(story, config2, nbSink, {
|
|
56056
|
-
includeAdversarialReview: false
|
|
56190
|
+
includeAdversarialReview: false,
|
|
56191
|
+
promptSeverityFloor: "info"
|
|
56057
56192
|
}));
|
|
56058
56193
|
} else {
|
|
56059
56194
|
nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
|
|
56060
|
-
includeAdversarialReview: false
|
|
56061
|
-
|
|
56195
|
+
includeAdversarialReview: false,
|
|
56196
|
+
promptSeverityFloor: "info"
|
|
56197
|
+
}), makeAutofixTestWriterStrategy(story, config2, nbSink, {
|
|
56198
|
+
promptSeverityFloor: "info"
|
|
56199
|
+
}));
|
|
56062
56200
|
}
|
|
56063
56201
|
nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2, nbSink));
|
|
56064
56202
|
const nbPostValidate = async (findings, _validateCtx) => {
|
|
@@ -56127,15 +56265,6 @@ function toVerifyScopedMode(mode) {
|
|
|
56127
56265
|
function hasReviewEscalation(story) {
|
|
56128
56266
|
return (story.priorFailures ?? []).some((f) => f.stage === "review");
|
|
56129
56267
|
}
|
|
56130
|
-
async function buildThreeSessionPrompt(role, ctx, lite) {
|
|
56131
|
-
return TddPromptBuilder.buildForRole(role, ctx.workdir, ctx.config, ctx.story, {
|
|
56132
|
-
lite,
|
|
56133
|
-
contextMarkdown: ctx.contextMarkdown,
|
|
56134
|
-
featureContextMarkdown: ctx.featureContextMarkdown,
|
|
56135
|
-
contextBundle: ctx.contextBundle,
|
|
56136
|
-
constitution: ctx.constitution?.content
|
|
56137
|
-
});
|
|
56138
|
-
}
|
|
56139
56268
|
function buildFeatureCtxBlock(ctx, role) {
|
|
56140
56269
|
const bundleMarkdown = ctx.contextBundle?.pushMarkdown.trim();
|
|
56141
56270
|
if (bundleMarkdown) {
|
|
@@ -56167,10 +56296,17 @@ async function assemblePlanInputsFromCtx(ctx) {
|
|
|
56167
56296
|
}
|
|
56168
56297
|
const packageDirRel = packageDirRelative(ctx.projectDir, ctx.workdir);
|
|
56169
56298
|
const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.projectDir, packageDirRel);
|
|
56299
|
+
const tddOpts = {
|
|
56300
|
+
lite: isLite,
|
|
56301
|
+
contextMarkdown: ctx.contextMarkdown,
|
|
56302
|
+
featureContextMarkdown: ctx.featureContextMarkdown,
|
|
56303
|
+
contextBundle: ctx.contextBundle,
|
|
56304
|
+
constitution: ctx.constitution?.content
|
|
56305
|
+
};
|
|
56170
56306
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = _isTdd ? await Promise.all([
|
|
56171
|
-
_isFreshRun ?
|
|
56172
|
-
|
|
56173
|
-
|
|
56307
|
+
_isFreshRun ? TddPromptBuilder.buildForRole("test-writer", ctx.workdir, ctx.config, ctx.story, tddOpts) : Promise.resolve(""),
|
|
56308
|
+
TddPromptBuilder.buildForRole("implementer", ctx.workdir, ctx.config, ctx.story, tddOpts),
|
|
56309
|
+
TddPromptBuilder.buildForRole("verifier", ctx.workdir, ctx.config, ctx.story, tddOpts)
|
|
56174
56310
|
]) : ["", ctx.prompt, ""];
|
|
56175
56311
|
const testWriterInput = _isTdd && _isFreshRun ? {
|
|
56176
56312
|
story,
|
|
@@ -56662,10 +56798,12 @@ async function decideStageAction(ctx, planResult, inspection, opts) {
|
|
|
56662
56798
|
logger.error("execution", "Rectification exhausted with unfixed findings", {
|
|
56663
56799
|
storyId: ctx.story.id,
|
|
56664
56800
|
findingsCount: planResult.unfixedFindings.length,
|
|
56665
|
-
findingSources
|
|
56801
|
+
findingSources,
|
|
56802
|
+
...planResult.unresolvedDetail ? { unresolvedDetail: planResult.unresolvedDetail } : {}
|
|
56666
56803
|
});
|
|
56667
56804
|
await cleanupSessionOnFailure(ctx);
|
|
56668
|
-
|
|
56805
|
+
const exhaustedReason = planResult.unresolvedDetail ? `Rectification exhausted: ${planResult.unresolvedDetail}` : "Rectification exhausted with unfixed findings";
|
|
56806
|
+
return { action: "escalate", reason: exhaustedReason };
|
|
56669
56807
|
}
|
|
56670
56808
|
}
|
|
56671
56809
|
if (selfVerificationFailed) {
|
|
@@ -57156,7 +57294,7 @@ var init_optimizer2 = __esm(() => {
|
|
|
57156
57294
|
async execute(ctx) {
|
|
57157
57295
|
const logger = _optimizerDeps.getLogger();
|
|
57158
57296
|
if (!ctx.prompt) {
|
|
57159
|
-
logger.debug("optimizer", "No prompt to optimize, skipping");
|
|
57297
|
+
logger.debug("optimizer", "No prompt to optimize, skipping", { storyId: ctx.story?.id ?? "unknown" });
|
|
57160
57298
|
return { action: "continue" };
|
|
57161
57299
|
}
|
|
57162
57300
|
const optimizer = resolveOptimizer(ctx.config, ctx.plugins);
|
|
@@ -57169,6 +57307,7 @@ var init_optimizer2 = __esm(() => {
|
|
|
57169
57307
|
ctx.prompt = result.prompt;
|
|
57170
57308
|
const savingsPercent = Math.round(result.savings * 100);
|
|
57171
57309
|
logger.info("optimizer", `${optimizer.name}: ${savingsPercent}% savings`, {
|
|
57310
|
+
storyId: ctx.story?.id ?? "unknown",
|
|
57172
57311
|
originalTokens: result.originalTokens,
|
|
57173
57312
|
optimizedTokens: result.optimizedTokens,
|
|
57174
57313
|
tokensSaved: result.originalTokens - result.optimizedTokens,
|
|
@@ -57241,6 +57380,7 @@ var init_prompt = __esm(() => {
|
|
|
57241
57380
|
ctx.prompt = prompt;
|
|
57242
57381
|
if (isBatch) {
|
|
57243
57382
|
logger.info("prompt", "Batch session prepared", {
|
|
57383
|
+
storyId: "batch",
|
|
57244
57384
|
storyCount: ctx.stories.length,
|
|
57245
57385
|
testStrategy: ctx.routing.testStrategy
|
|
57246
57386
|
});
|
|
@@ -57369,12 +57509,12 @@ var init_queue_check = __esm(() => {
|
|
|
57369
57509
|
}
|
|
57370
57510
|
for (const cmd of queueCommands) {
|
|
57371
57511
|
if (cmd.type === "PAUSE") {
|
|
57372
|
-
logger.warn("queue", "Paused by user", { command: "PAUSE" });
|
|
57512
|
+
logger.warn("queue", "Paused by user", { storyId: ctx.story?.id ?? "unknown", command: "PAUSE" });
|
|
57373
57513
|
await clearQueueFile(ctx.workdir);
|
|
57374
57514
|
return { action: "pause", reason: "User requested pause via .queue.txt" };
|
|
57375
57515
|
}
|
|
57376
57516
|
if (cmd.type === "ABORT") {
|
|
57377
|
-
logger.warn("queue", "Aborting: marking remaining stories as skipped");
|
|
57517
|
+
logger.warn("queue", "Aborting: marking remaining stories as skipped", { storyId: ctx.story?.id ?? "unknown" });
|
|
57378
57518
|
for (const s of ctx.prd.userStories) {
|
|
57379
57519
|
if (s.status === "pending") {
|
|
57380
57520
|
markStorySkipped(ctx.prd, s.id);
|
|
@@ -57969,8 +58109,7 @@ __export(exports_init_context, {
|
|
|
57969
58109
|
initPackage: () => initPackage,
|
|
57970
58110
|
initContext: () => initContext,
|
|
57971
58111
|
generatePackageContextTemplate: () => generatePackageContextTemplate,
|
|
57972
|
-
generateContextTemplate: () => generateContextTemplate
|
|
57973
|
-
_initContextDeps: () => _initContextDeps
|
|
58112
|
+
generateContextTemplate: () => generateContextTemplate
|
|
57974
58113
|
});
|
|
57975
58114
|
import { basename as basename9, join as join52 } from "path";
|
|
57976
58115
|
async function bunFileExists(path14) {
|
|
@@ -58159,39 +58298,6 @@ function generateContextTemplate(scan) {
|
|
|
58159
58298
|
`).trim()}
|
|
58160
58299
|
`;
|
|
58161
58300
|
}
|
|
58162
|
-
async function generateContextWithLLM(scan) {
|
|
58163
|
-
const logger = getLogger();
|
|
58164
|
-
const scanSummary = `
|
|
58165
|
-
Project: ${scan.projectName}
|
|
58166
|
-
Entry Points: ${scan.entryPoints.join(", ") || "None detected"}
|
|
58167
|
-
Config Files: ${scan.configFiles.join(", ") || "None detected"}
|
|
58168
|
-
Total Files: ${scan.fileTree.length}
|
|
58169
|
-
Description: ${scan.packageManifest?.description || "Not provided"}
|
|
58170
|
-
`;
|
|
58171
|
-
const prompt = `
|
|
58172
|
-
You are a technical documentation expert. Generate a concise, well-structured context.md file for a software project based on this scan:
|
|
58173
|
-
|
|
58174
|
-
${scanSummary}
|
|
58175
|
-
|
|
58176
|
-
The context.md should include:
|
|
58177
|
-
1. Project overview (name, purpose, key technologies)
|
|
58178
|
-
2. Entry points and main modules
|
|
58179
|
-
3. Key dependencies and why they're used
|
|
58180
|
-
4. Development setup and common commands
|
|
58181
|
-
5. Architecture overview (brief)
|
|
58182
|
-
6. Development guidelines
|
|
58183
|
-
|
|
58184
|
-
Keep it under 2000 tokens. Use markdown formatting. Be specific to the detected stack and structure.
|
|
58185
|
-
`;
|
|
58186
|
-
try {
|
|
58187
|
-
const result = await _initContextDeps.callLLM(prompt);
|
|
58188
|
-
logger.info("init", "Generated context.md with LLM", { storyId: "init-context" });
|
|
58189
|
-
return result;
|
|
58190
|
-
} catch (err) {
|
|
58191
|
-
logger.warn("init", `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}`, { storyId: "init-context" });
|
|
58192
|
-
return generateContextTemplate(scan);
|
|
58193
|
-
}
|
|
58194
|
-
}
|
|
58195
58301
|
function generatePackageContextTemplate(packagePath) {
|
|
58196
58302
|
const packageName = packagePath.split("/").pop() ?? packagePath;
|
|
58197
58303
|
return `# ${packageName} \u2014 Context
|
|
@@ -58246,26 +58352,15 @@ async function initContext(projectRoot, options = {}) {
|
|
|
58246
58352
|
await bunMkdirp(naxDir);
|
|
58247
58353
|
}
|
|
58248
58354
|
const scan = await scanProject(projectRoot);
|
|
58249
|
-
|
|
58250
|
-
if (options.ai) {
|
|
58251
|
-
content = await generateContextWithLLM(scan);
|
|
58252
|
-
} else {
|
|
58253
|
-
content = generateContextTemplate(scan);
|
|
58254
|
-
}
|
|
58355
|
+
const content = generateContextTemplate(scan);
|
|
58255
58356
|
await Bun.write(contextPath, content);
|
|
58256
58357
|
logger.info("init", "Generated .nax/context.md template from project scan", {
|
|
58257
58358
|
storyId: "init-context",
|
|
58258
58359
|
path: contextPath
|
|
58259
58360
|
});
|
|
58260
58361
|
}
|
|
58261
|
-
var _initContextDeps;
|
|
58262
58362
|
var init_init_context = __esm(() => {
|
|
58263
58363
|
init_logger2();
|
|
58264
|
-
_initContextDeps = {
|
|
58265
|
-
callLLM: async (_prompt) => {
|
|
58266
|
-
throw new Error("callLLM not implemented");
|
|
58267
|
-
}
|
|
58268
|
-
};
|
|
58269
58364
|
});
|
|
58270
58365
|
|
|
58271
58366
|
// src/cli/init-detect.ts
|
|
@@ -58699,7 +58794,7 @@ async function initProject(projectRoot, options) {
|
|
|
58699
58794
|
} else {
|
|
58700
58795
|
logger.info("init", "Project config already exists", { path: configPath });
|
|
58701
58796
|
}
|
|
58702
|
-
await initContext(projectRoot, {
|
|
58797
|
+
await initContext(projectRoot, { force: options?.force });
|
|
58703
58798
|
const constitutionPath = join54(projectDir, "constitution.md");
|
|
58704
58799
|
if (!existsSync23(constitutionPath) || options?.force) {
|
|
58705
58800
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
@@ -60691,7 +60786,7 @@ var package_default;
|
|
|
60691
60786
|
var init_package = __esm(() => {
|
|
60692
60787
|
package_default = {
|
|
60693
60788
|
name: "@nathapp/nax",
|
|
60694
|
-
version: "0.70.
|
|
60789
|
+
version: "0.70.4",
|
|
60695
60790
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
60696
60791
|
type: "module",
|
|
60697
60792
|
bin: {
|
|
@@ -60791,8 +60886,8 @@ var init_version = __esm(() => {
|
|
|
60791
60886
|
NAX_VERSION = package_default.version;
|
|
60792
60887
|
NAX_COMMIT = (() => {
|
|
60793
60888
|
try {
|
|
60794
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
60795
|
-
return "
|
|
60889
|
+
if (/^[0-9a-f]{6,10}$/.test("b2d5d046"))
|
|
60890
|
+
return "b2d5d046";
|
|
60796
60891
|
} catch {}
|
|
60797
60892
|
try {
|
|
60798
60893
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -63043,16 +63138,37 @@ ${missing.join(`
|
|
|
63043
63138
|
});
|
|
63044
63139
|
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
63045
63140
|
if (exitCode !== 0) {
|
|
63046
|
-
throw new
|
|
63141
|
+
throw new NaxError(`Failed to create worktree: ${stderr || "unknown error"}`, "WORKTREE_ERROR", {
|
|
63142
|
+
stage: "worktree",
|
|
63143
|
+
storyId,
|
|
63144
|
+
projectRoot,
|
|
63145
|
+
stderr
|
|
63146
|
+
});
|
|
63047
63147
|
}
|
|
63048
63148
|
} catch (error48) {
|
|
63149
|
+
if (error48 instanceof NaxError) {
|
|
63150
|
+
throw error48;
|
|
63151
|
+
}
|
|
63049
63152
|
if (error48 instanceof Error) {
|
|
63050
63153
|
if (error48.message.includes("not a git repository")) {
|
|
63051
|
-
throw new
|
|
63154
|
+
throw new NaxError(`Not a git repository: ${projectRoot}`, "WORKTREE_ERROR", {
|
|
63155
|
+
stage: "worktree",
|
|
63156
|
+
storyId,
|
|
63157
|
+
projectRoot
|
|
63158
|
+
});
|
|
63052
63159
|
}
|
|
63053
|
-
throw error48
|
|
63160
|
+
throw new NaxError(error48.message, "WORKTREE_ERROR", {
|
|
63161
|
+
stage: "worktree",
|
|
63162
|
+
storyId,
|
|
63163
|
+
projectRoot,
|
|
63164
|
+
cause: error48
|
|
63165
|
+
});
|
|
63054
63166
|
}
|
|
63055
|
-
throw new
|
|
63167
|
+
throw new NaxError(`Failed to create worktree: ${String(error48)}`, "WORKTREE_ERROR", {
|
|
63168
|
+
stage: "worktree",
|
|
63169
|
+
storyId,
|
|
63170
|
+
projectRoot
|
|
63171
|
+
});
|
|
63056
63172
|
}
|
|
63057
63173
|
const envSource = join80(projectRoot, ".env");
|
|
63058
63174
|
if (existsSync31(envSource)) {
|
|
@@ -63061,7 +63177,12 @@ ${missing.join(`
|
|
|
63061
63177
|
symlinkSync(envSource, envTarget, "file");
|
|
63062
63178
|
} catch (error48) {
|
|
63063
63179
|
await this.remove(projectRoot, storyId);
|
|
63064
|
-
throw new
|
|
63180
|
+
throw new NaxError(`Failed to symlink .env: ${errorMessage(error48)}`, "WORKTREE_ERROR", {
|
|
63181
|
+
stage: "worktree",
|
|
63182
|
+
storyId,
|
|
63183
|
+
envSource,
|
|
63184
|
+
envTarget
|
|
63185
|
+
});
|
|
63065
63186
|
}
|
|
63066
63187
|
}
|
|
63067
63188
|
}
|
|
@@ -63078,15 +63199,29 @@ ${missing.join(`
|
|
|
63078
63199
|
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
63079
63200
|
if (exitCode !== 0) {
|
|
63080
63201
|
if (stderr.includes("not found") || stderr.includes("does not exist") || stderr.includes("no such worktree") || stderr.includes("is not a working tree")) {
|
|
63081
|
-
throw new
|
|
63202
|
+
throw new NaxError(`Worktree not found: ${worktreePath}`, "WORKTREE_ERROR", {
|
|
63203
|
+
stage: "worktree",
|
|
63204
|
+
storyId,
|
|
63205
|
+
worktreePath
|
|
63206
|
+
});
|
|
63082
63207
|
}
|
|
63083
|
-
throw new
|
|
63208
|
+
throw new NaxError(`Failed to remove worktree: ${stderr || "unknown error"}`, "WORKTREE_ERROR", {
|
|
63209
|
+
stage: "worktree",
|
|
63210
|
+
storyId,
|
|
63211
|
+
worktreePath,
|
|
63212
|
+
stderr
|
|
63213
|
+
});
|
|
63084
63214
|
}
|
|
63085
63215
|
} catch (error48) {
|
|
63086
|
-
if (error48 instanceof
|
|
63216
|
+
if (error48 instanceof NaxError) {
|
|
63087
63217
|
throw error48;
|
|
63088
63218
|
}
|
|
63089
|
-
throw new
|
|
63219
|
+
throw new NaxError(error48 instanceof Error ? error48.message : String(error48), "WORKTREE_ERROR", {
|
|
63220
|
+
stage: "worktree",
|
|
63221
|
+
storyId,
|
|
63222
|
+
worktreePath,
|
|
63223
|
+
cause: error48 instanceof Error ? error48 : undefined
|
|
63224
|
+
});
|
|
63090
63225
|
}
|
|
63091
63226
|
try {
|
|
63092
63227
|
const proc = _managerDeps.spawn(["git", "branch", "-D", branchName], {
|
|
@@ -63121,14 +63256,22 @@ ${missing.join(`
|
|
|
63121
63256
|
new Response(proc.stdout).text()
|
|
63122
63257
|
]);
|
|
63123
63258
|
if (exitCode !== 0) {
|
|
63124
|
-
throw new
|
|
63259
|
+
throw new NaxError(`Failed to list worktrees: ${stderr || "unknown error"}`, "WORKTREE_ERROR", {
|
|
63260
|
+
stage: "worktree",
|
|
63261
|
+
projectRoot,
|
|
63262
|
+
stderr
|
|
63263
|
+
});
|
|
63125
63264
|
}
|
|
63126
63265
|
return this.parseWorktreeList(stdout);
|
|
63127
63266
|
} catch (error48) {
|
|
63128
|
-
if (error48 instanceof
|
|
63267
|
+
if (error48 instanceof NaxError) {
|
|
63129
63268
|
throw error48;
|
|
63130
63269
|
}
|
|
63131
|
-
throw new
|
|
63270
|
+
throw new NaxError(error48 instanceof Error ? error48.message : String(error48), "WORKTREE_ERROR", {
|
|
63271
|
+
stage: "worktree",
|
|
63272
|
+
projectRoot,
|
|
63273
|
+
cause: error48 instanceof Error ? error48 : undefined
|
|
63274
|
+
});
|
|
63132
63275
|
}
|
|
63133
63276
|
}
|
|
63134
63277
|
parseWorktreeList(output) {
|
|
@@ -63156,6 +63299,7 @@ ${missing.join(`
|
|
|
63156
63299
|
}
|
|
63157
63300
|
var _managerDeps;
|
|
63158
63301
|
var init_manager3 = __esm(() => {
|
|
63302
|
+
init_errors();
|
|
63159
63303
|
init_logger2();
|
|
63160
63304
|
init_bun_deps();
|
|
63161
63305
|
init_gitignore();
|
|
@@ -65802,17 +65946,24 @@ async function runPrecheckValidation(ctx) {
|
|
|
65802
65946
|
ctx.statusWriter.setRunStatus("precheck-failed");
|
|
65803
65947
|
ctx.statusWriter.setCurrentStory(null);
|
|
65804
65948
|
await ctx.statusWriter.update(0, 0);
|
|
65805
|
-
|
|
65806
|
-
|
|
65807
|
-
|
|
65949
|
+
process.stderr.write(`
|
|
65950
|
+
`);
|
|
65951
|
+
process.stderr.write(`${source_default.red("\u274C PRECHECK FAILED")}
|
|
65952
|
+
`);
|
|
65953
|
+
process.stderr.write(`${source_default.red("\u2500".repeat(60))}
|
|
65954
|
+
`);
|
|
65808
65955
|
for (const blocker of precheckResult.output.blockers) {
|
|
65809
|
-
|
|
65956
|
+
process.stderr.write(`${source_default.red(`\u2717 ${blocker.name}: ${blocker.message}`)}
|
|
65957
|
+
`);
|
|
65810
65958
|
}
|
|
65811
|
-
|
|
65812
|
-
|
|
65813
|
-
|
|
65814
|
-
|
|
65815
|
-
`)
|
|
65959
|
+
process.stderr.write(`${source_default.red("\u2500".repeat(60))}
|
|
65960
|
+
`);
|
|
65961
|
+
process.stderr.write(`${source_default.yellow(`
|
|
65962
|
+
Run 'nax precheck' for detailed information`)}
|
|
65963
|
+
`);
|
|
65964
|
+
process.stderr.write(`${source_default.dim(`Use --skip-precheck to bypass (not recommended)
|
|
65965
|
+
`)}
|
|
65966
|
+
`);
|
|
65816
65967
|
throw new Error(`Precheck failed: ${precheckResult.output.blockers.map((b) => b.name).join(", ")}`);
|
|
65817
65968
|
}
|
|
65818
65969
|
if (precheckResult.output.warnings.length > 0) {
|
|
@@ -65821,12 +65972,15 @@ Run 'nax precheck' for detailed information`));
|
|
|
65821
65972
|
issues: precheckResult.output.warnings.map((w) => w.name)
|
|
65822
65973
|
});
|
|
65823
65974
|
if (ctx.headless && ctx.formatterMode !== "json") {
|
|
65824
|
-
|
|
65825
|
-
\u26A0\uFE0F Precheck warnings:`)
|
|
65975
|
+
process.stdout.write(`${source_default.yellow(`
|
|
65976
|
+
\u26A0\uFE0F Precheck warnings:`)}
|
|
65977
|
+
`);
|
|
65826
65978
|
for (const warning of precheckResult.output.warnings) {
|
|
65827
|
-
|
|
65979
|
+
process.stdout.write(`${source_default.yellow(` \u26A0 ${warning.name}: ${warning.message}`)}
|
|
65980
|
+
`);
|
|
65828
65981
|
}
|
|
65829
|
-
|
|
65982
|
+
process.stdout.write(`
|
|
65983
|
+
`);
|
|
65830
65984
|
}
|
|
65831
65985
|
} else {
|
|
65832
65986
|
logger?.info("precheck", "All precheck validations passed");
|