@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.
Files changed (2) hide show
  1. package/dist/nax.js +449 -295
  2. 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
- console.error(`[logger] Failed to create log directory: ${error48}`);
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
- console.error(`[logger] Failed to write to log file: ${error48}`);
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 Error(`complete() timed out after ${timeoutMs}ms`)), timeoutMs);
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
- for (const line of output.split(`
24702
- `)) {
24703
- const m = line.match(/^--- FAIL:\s+(\S+)\s+\([\d.]+s\)/);
24704
- if (m) {
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: m[1],
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 Error(`[schema] story[${index}] must be an object`);
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 Error(`[schema] story[${index}].id is required and must be non-empty`);
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 Error(`[schema] story[${index}].id must be a string`);
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 Error(`[schema] story[${index}].title is required and must be non-empty`);
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 Error(`[schema] story[${index}].description is required and must be non-empty`);
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 Error(`[schema] story[${index}].acceptanceCriteria is required and must be a non-empty array`);
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 Error(`[schema] story[${index}].acceptanceCriteria[${i}] must be a string`);
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 Error(`[schema] story[${index}].suggestedCriteria must be an array when present`);
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 Error(`[schema] story[${index}].suggestedCriteria[${i}] must be a string`);
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 Error(`[schema] story[${index}] missing complexity. Set routing.complexity to one of: ${VALID_COMPLEXITY.join(", ")}`);
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 Error(`[schema] story[${index}].routing.complexity must be a string`);
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 Error(`[schema] story[${index}].routing.complexity "${rawComplexity}" is invalid. Valid values: ${VALID_COMPLEXITY.join(", ")}`);
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 Error(`[schema] story[${index}].routing.noTestJustification is required when testStrategy is "no-test"`);
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 Error(`[schema] story[${index}].dependencies references unknown story ID "${dep}"`);
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 Error(`[schema] story[${index}].workdir must be a string`);
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 Error(`[schema] story[${index}].workdir must be relative (no leading /): "${rawWorkdir}"`);
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 Error(`[schema] story[${index}].workdir must not contain '..': "${rawWorkdir}"`);
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 Error(`[schema] story[${index}].contextFiles entry must be relative (no absolute paths): "${f}"`);
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 Error(`[schema] story[${index}].contextFiles entry must not contain '..': "${f}"`);
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 Error(`[schema] story[${index}].contextFiles entry must be relative (no absolute paths): "${path}"`);
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 Error(`[schema] story[${index}].contextFiles entry must not contain '..': "${path}"`);
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 Error(`[schema] story[${index}].expectedFiles entry must be relative (no absolute paths): "${trimmed}"`);
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 Error(`[schema] story[${index}].expectedFiles entry must not contain '..': "${trimmed}"`);
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 Error(`[schema] story[${index}].verifiedBy.kind "${vb.kind}" is invalid. Valid values: ${VALID_VERIFIED_BY_KINDS.join(", ")}`);
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 Error(`[schema] Failed to parse JSON: ${parseErr.message}`, { cause: parseErr });
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 Error("[schema] PRD output must be a JSON object");
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 Error("[schema] userStories is required and must be a non-empty array");
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 CLASSIFY_ROLE = `You are a story classifier that assigns complexity and model tier to user stories.
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: CLASSIFY_ROLE, overridable: false },
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: CLASSIFY_ROLE, overridable: false },
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: "You are an AI orchestration decision-maker.", overridable: false },
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
- return { applied: true, testEditDeclarations: declarations };
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
- return { targetFiles: [], summary: "Fixed failing tests" };
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: config2.review?.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: config2.review?.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 Error("No interaction plugin registered");
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 Error("No interaction plugin registered");
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 Error(`All interaction plugins failed: ${errorMessages}`);
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 Error(`Invalid interaction ID \u2014 must match [a-zA-Z0-9_-]{1,128}: ${id}`);
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 Error(`Path traversal detected: ${filePath} is outside ${baseDir}`);
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 { rectificationExhausted: true, unfixedFindings: cycleResult.finalFindings };
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
- }), makeAutofixTestWriterStrategy(story, config2, nbSink));
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 ? buildThreeSessionPrompt("test-writer", ctx, isLite) : Promise.resolve(""),
56172
- buildThreeSessionPrompt("implementer", ctx, isLite),
56173
- buildThreeSessionPrompt("verifier", ctx, isLite)
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
- return { action: "escalate", reason: "Rectification exhausted with unfixed findings" };
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
- let content;
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, { ai: options?.ai, force: options?.force });
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.2",
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("0e202cfa"))
60795
- return "0e202cfa";
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 Error(`Failed to create worktree: ${stderr || "unknown error"}`);
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 Error(`Not a git repository: ${projectRoot}`);
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 Error(`Failed to create worktree: ${String(error48)}`);
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 Error(`Failed to symlink .env: ${errorMessage(error48)}`);
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 Error(`Worktree not found: ${worktreePath}`);
63202
+ throw new NaxError(`Worktree not found: ${worktreePath}`, "WORKTREE_ERROR", {
63203
+ stage: "worktree",
63204
+ storyId,
63205
+ worktreePath
63206
+ });
63082
63207
  }
63083
- throw new Error(`Failed to remove worktree: ${stderr || "unknown error"}`);
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 Error) {
63216
+ if (error48 instanceof NaxError) {
63087
63217
  throw error48;
63088
63218
  }
63089
- throw new Error(`Failed to remove worktree: ${String(error48)}`);
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 Error(`Failed to list worktrees: ${stderr || "unknown error"}`);
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 Error) {
63267
+ if (error48 instanceof NaxError) {
63129
63268
  throw error48;
63130
63269
  }
63131
- throw new Error(`Failed to list worktrees: ${String(error48)}`);
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
- console.error("");
65806
- console.error(source_default.red("\u274C PRECHECK FAILED"));
65807
- console.error(source_default.red("\u2500".repeat(60)));
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
- console.error(source_default.red(`\u2717 ${blocker.name}: ${blocker.message}`));
65956
+ process.stderr.write(`${source_default.red(`\u2717 ${blocker.name}: ${blocker.message}`)}
65957
+ `);
65810
65958
  }
65811
- console.error(source_default.red("\u2500".repeat(60)));
65812
- console.error(source_default.yellow(`
65813
- Run 'nax precheck' for detailed information`));
65814
- console.error(source_default.dim(`Use --skip-precheck to bypass (not recommended)
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
- console.log(source_default.yellow(`
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
- console.log(source_default.yellow(` \u26A0 ${warning.name}: ${warning.message}`));
65979
+ process.stdout.write(`${source_default.yellow(` \u26A0 ${warning.name}: ${warning.message}`)}
65980
+ `);
65828
65981
  }
65829
- console.log("");
65982
+ process.stdout.write(`
65983
+ `);
65830
65984
  }
65831
65985
  } else {
65832
65986
  logger?.info("precheck", "All precheck validations passed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.70.2",
3
+ "version": "0.70.4",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {