xforce 0.1.0 → 0.1.2

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/cli/index.js CHANGED
@@ -310,10 +310,10 @@ function resolveRepoConfig(config, owner, name) {
310
310
  };
311
311
  }
312
312
 
313
- // src/pipeline/orchestrator.ts
314
- import { nanoid } from "nanoid";
315
- import { rm } from "fs/promises";
316
- import ora from "ora";
313
+ // src/core/credentials.ts
314
+ import { existsSync as existsSync2, mkdirSync, writeFileSync } from "fs";
315
+ import { join } from "path";
316
+ import { homedir } from "os";
317
317
 
318
318
  // src/core/logger.ts
319
319
  import pino from "pino";
@@ -335,6 +335,44 @@ function createChildLogger(name) {
335
335
  return logger.child({ component: name });
336
336
  }
337
337
 
338
+ // src/core/credentials.ts
339
+ var log = createChildLogger("credentials");
340
+ function getClaudeConfigDir() {
341
+ return process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude");
342
+ }
343
+ function ensureCredentials() {
344
+ const configDir = getClaudeConfigDir();
345
+ const credPath = join(configDir, ".credentials.json");
346
+ const envCreds = process.env.CLAUDE_CREDENTIALS;
347
+ if (existsSync2(credPath)) {
348
+ log.debug({ path: credPath }, "Credentials file exists");
349
+ return;
350
+ }
351
+ if (!envCreds) {
352
+ throw new Error(
353
+ `Claude Code credentials not found. Either:
354
+ 1. Log in with 'claude' CLI locally, or
355
+ 2. Set CLAUDE_CREDENTIALS env var with the JSON credentials content.
356
+ Checked path: ${credPath}`
357
+ );
358
+ }
359
+ try {
360
+ JSON.parse(envCreds);
361
+ } catch {
362
+ throw new Error(
363
+ "CLAUDE_CREDENTIALS env var contains invalid JSON. It should be the contents of .credentials.json from a Claude Code login."
364
+ );
365
+ }
366
+ mkdirSync(configDir, { recursive: true });
367
+ writeFileSync(credPath, envCreds, { mode: 384 });
368
+ log.info({ path: credPath }, "Wrote Claude Code credentials from env var");
369
+ }
370
+
371
+ // src/pipeline/orchestrator.ts
372
+ import { nanoid } from "nanoid";
373
+ import { rm } from "fs/promises";
374
+ import ora from "ora";
375
+
338
376
  // src/pipeline/state-machine.ts
339
377
  var VALID_TRANSITIONS = {
340
378
  parsing_issue: ["creating_branch", "failed"],
@@ -374,13 +412,13 @@ ${truncated}`;
374
412
 
375
413
  // src/github/client.ts
376
414
  import { Octokit } from "@octokit/rest";
377
- var log = createChildLogger("github");
415
+ var log2 = createChildLogger("github");
378
416
  var RETRY_OPTIONS = {
379
417
  maxRetries: 3,
380
418
  baseDelayMs: 1e3,
381
419
  maxDelayMs: 3e4,
382
420
  onRetry: (error, attempt) => {
383
- log.warn({ error: error.message, attempt }, "GitHub API call failed, retrying");
421
+ log2.warn({ error: error.message, attempt }, "GitHub API call failed, retrying");
384
422
  }
385
423
  };
386
424
  async function withGitHubRetry(fn) {
@@ -415,7 +453,7 @@ function getOctokit() {
415
453
  async function getIssue(owner, repo, issueNumber) {
416
454
  return withGitHubRetry(async () => {
417
455
  const octokit = getOctokit();
418
- log.debug({ owner, repo, issueNumber }, "Fetching issue");
456
+ log2.debug({ owner, repo, issueNumber }, "Fetching issue");
419
457
  const { data } = await octokit.issues.get({ owner, repo, issue_number: issueNumber });
420
458
  return data;
421
459
  });
@@ -434,13 +472,13 @@ async function getIssueLabels(owner, repo, issueNumber) {
434
472
  async function addLabel(owner, repo, issueNumber, label) {
435
473
  return withGitHubRetry(async () => {
436
474
  const octokit = getOctokit();
437
- log.debug({ owner, repo, issueNumber, label }, "Adding label");
475
+ log2.debug({ owner, repo, issueNumber, label }, "Adding label");
438
476
  await octokit.issues.addLabels({ owner, repo, issue_number: issueNumber, labels: [label] });
439
477
  });
440
478
  }
441
479
  async function removeLabel(owner, repo, issueNumber, label) {
442
480
  const octokit = getOctokit();
443
- log.debug({ owner, repo, issueNumber, label }, "Removing label");
481
+ log2.debug({ owner, repo, issueNumber, label }, "Removing label");
444
482
  try {
445
483
  await octokit.issues.removeLabel({ owner, repo, issue_number: issueNumber, name: label });
446
484
  } catch {
@@ -449,7 +487,7 @@ async function removeLabel(owner, repo, issueNumber, label) {
449
487
  async function addComment(owner, repo, issueNumber, body) {
450
488
  return withGitHubRetry(async () => {
451
489
  const octokit = getOctokit();
452
- log.debug({ owner, repo, issueNumber }, "Adding comment");
490
+ log2.debug({ owner, repo, issueNumber }, "Adding comment");
453
491
  const { data } = await octokit.issues.createComment({
454
492
  owner,
455
493
  repo,
@@ -462,7 +500,7 @@ async function addComment(owner, repo, issueNumber, body) {
462
500
  async function getPRDiff(owner, repo, prNumber) {
463
501
  return withGitHubRetry(async () => {
464
502
  const octokit = getOctokit();
465
- log.debug({ owner, repo, prNumber }, "Fetching PR diff");
503
+ log2.debug({ owner, repo, prNumber }, "Fetching PR diff");
466
504
  const { data } = await octokit.pulls.get({
467
505
  owner,
468
506
  repo,
@@ -497,7 +535,7 @@ function parsePRUrl(url) {
497
535
  async function getPR(owner, repo, prNumber) {
498
536
  return withGitHubRetry(async () => {
499
537
  const octokit = getOctokit();
500
- log.debug({ owner, repo, prNumber }, "Fetching PR");
538
+ log2.debug({ owner, repo, prNumber }, "Fetching PR");
501
539
  const { data } = await octokit.pulls.get({ owner, repo, pull_number: prNumber });
502
540
  return data;
503
541
  });
@@ -601,7 +639,7 @@ function parseIssueBody(params) {
601
639
 
602
640
  // src/github/branch.ts
603
641
  import { tmpdir } from "os";
604
- import { join } from "path";
642
+ import { join as join2 } from "path";
605
643
  import { mkdtemp, access, writeFile } from "fs/promises";
606
644
  import { simpleGit } from "simple-git";
607
645
  var DEFAULT_GITIGNORE = `node_modules/
@@ -614,7 +652,7 @@ build/
614
652
  *.tsbuildinfo
615
653
  .DS_Store
616
654
  `;
617
- var log2 = createChildLogger("git");
655
+ var log3 = createChildLogger("git");
618
656
  function slugify(text) {
619
657
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
620
658
  }
@@ -623,13 +661,13 @@ async function setupBranch(params) {
623
661
  const slug = slugify(issueTitle);
624
662
  const branchName = `${branchPrefix}/${issueNumber}-${slug}`;
625
663
  if (localDir) {
626
- log2.info({ workDir: localDir, branchName }, "Using local repository");
664
+ log3.info({ workDir: localDir, branchName }, "Using local repository");
627
665
  const repoGit2 = simpleGit(localDir);
628
666
  await repoGit2.fetch("origin");
629
667
  const status = await repoGit2.status();
630
668
  const hadChanges = status.files.length > 0;
631
669
  if (hadChanges) {
632
- log2.info("Stashing local changes");
670
+ log3.info("Stashing local changes");
633
671
  await repoGit2.stash();
634
672
  }
635
673
  try {
@@ -639,34 +677,34 @@ async function setupBranch(params) {
639
677
  }
640
678
  await repoGit2.addConfig("user.name", "X-Force Bot");
641
679
  await repoGit2.addConfig("user.email", "xforce-bot@users.noreply.github.com");
642
- log2.info({ branchName }, "Branch created");
680
+ log3.info({ branchName }, "Branch created");
643
681
  return { workDir: localDir, branchName, git: repoGit2, isLocal: true };
644
682
  }
645
683
  const cloneUrl = `https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${owner}/${repo}.git`;
646
- const workDir = await mkdtemp(join(tmpdir(), `xforce-${repo}-`));
647
- log2.info({ workDir, branchName }, "Cloning repository");
684
+ const workDir = await mkdtemp(join2(tmpdir(), `xforce-${repo}-`));
685
+ log3.info({ workDir, branchName }, "Cloning repository");
648
686
  const git = simpleGit();
649
687
  await git.clone(cloneUrl, workDir, ["--depth", "1", "--branch", defaultBranch]);
650
688
  const repoGit = simpleGit(workDir);
651
689
  await repoGit.addConfig("user.name", "X-Force Bot");
652
690
  await repoGit.addConfig("user.email", "xforce-bot@users.noreply.github.com");
653
691
  await repoGit.checkoutLocalBranch(branchName);
654
- const gitignorePath = join(workDir, ".gitignore");
692
+ const gitignorePath = join2(workDir, ".gitignore");
655
693
  try {
656
694
  await access(gitignorePath);
657
695
  } catch {
658
- log2.info("Creating default .gitignore");
696
+ log3.info("Creating default .gitignore");
659
697
  await writeFile(gitignorePath, DEFAULT_GITIGNORE, "utf-8");
660
698
  }
661
- log2.info({ branchName }, "Branch created");
699
+ log3.info({ branchName }, "Branch created");
662
700
  return { workDir, branchName, git: repoGit, isLocal: false };
663
701
  }
664
702
  async function restoreDefaultBranch(git, defaultBranch) {
665
703
  try {
666
704
  await git.checkout(defaultBranch);
667
- log2.info({ branch: defaultBranch }, "Restored default branch");
705
+ log3.info({ branch: defaultBranch }, "Restored default branch");
668
706
  } catch (error) {
669
- log2.warn({ error: String(error) }, "Failed to restore default branch");
707
+ log3.warn({ error: String(error) }, "Failed to restore default branch");
670
708
  }
671
709
  }
672
710
  async function commitAndPush(params) {
@@ -674,23 +712,23 @@ async function commitAndPush(params) {
674
712
  await git.add("-A");
675
713
  const status = await git.status();
676
714
  if (status.staged.length === 0 && status.files.length === 0) {
677
- log2.warn("No changes to commit");
715
+ log3.warn("No changes to commit");
678
716
  return "";
679
717
  }
680
718
  const commitResult = await git.commit(message);
681
- log2.info({ sha: commitResult.commit }, "Changes committed");
719
+ log3.info({ sha: commitResult.commit }, "Changes committed");
682
720
  await git.push("origin", branchName, ["--set-upstream", "--force"]);
683
- log2.info({ branchName }, "Pushed to remote");
721
+ log3.info({ branchName }, "Pushed to remote");
684
722
  return commitResult.commit;
685
723
  }
686
724
 
687
725
  // src/github/pr.ts
688
- var log3 = createChildLogger("pr");
726
+ var log4 = createChildLogger("pr");
689
727
  async function createPullRequest(params) {
690
728
  const { owner, repo, branchName, defaultBranch, taskSpec, pipeline } = params;
691
729
  const octokit = getOctokit();
692
730
  const body = buildPRBody(taskSpec, pipeline);
693
- log3.info({ owner, repo, branchName }, "Creating pull request");
731
+ log4.info({ owner, repo, branchName }, "Creating pull request");
694
732
  const { data } = await octokit.pulls.create({
695
733
  owner,
696
734
  repo,
@@ -699,7 +737,7 @@ async function createPullRequest(params) {
699
737
  head: branchName,
700
738
  base: defaultBranch
701
739
  });
702
- log3.info({ prNumber: data.number, prUrl: data.html_url }, "PR created");
740
+ log4.info({ prNumber: data.number, prUrl: data.html_url }, "PR created");
703
741
  return { prNumber: data.number, prUrl: data.html_url };
704
742
  }
705
743
  async function updatePullRequest(params) {
@@ -719,7 +757,7 @@ async function commentOnPR(owner, repo, prNumber, body) {
719
757
  async function mergePR(params) {
720
758
  const { owner, repo, prNumber, strategy, commitTitle } = params;
721
759
  const octokit = getOctokit();
722
- log3.info({ owner, repo, prNumber, strategy }, "Attempting to merge PR");
760
+ log4.info({ owner, repo, prNumber, strategy }, "Attempting to merge PR");
723
761
  try {
724
762
  const { data } = await octokit.pulls.merge({
725
763
  owner,
@@ -728,14 +766,14 @@ async function mergePR(params) {
728
766
  merge_method: strategy,
729
767
  commit_title: commitTitle
730
768
  });
731
- log3.info({ sha: data.sha, merged: data.merged }, "PR merge result");
769
+ log4.info({ sha: data.sha, merged: data.merged }, "PR merge result");
732
770
  return { merged: data.merged, sha: data.sha };
733
771
  } catch (error) {
734
772
  const err = error;
735
773
  const message = err.message ?? String(error);
736
774
  const status = err.status;
737
775
  if (status === 405 || status === 409) {
738
- log3.warn({ status, message }, "PR cannot be auto-merged");
776
+ log4.warn({ status, message }, "PR cannot be auto-merged");
739
777
  return { merged: false, error: message };
740
778
  }
741
779
  throw error;
@@ -916,7 +954,7 @@ Follow this plan closely. If you discover the plan is incorrect or incomplete, a
916
954
  }
917
955
 
918
956
  // src/agents/coder.ts
919
- var log4 = createChildLogger("coder");
957
+ var log5 = createChildLogger("coder");
920
958
  function truncate(s, max) {
921
959
  return s.length > max ? s.slice(0, max) + "..." : s;
922
960
  }
@@ -954,7 +992,7 @@ async function runCodingAgent(params) {
954
992
  if (commandFailures) {
955
993
  prompt += "\n\n" + buildCommandFailureFeedbackSection(commandFailures.kind, commandFailures.output);
956
994
  }
957
- log4.info(
995
+ log5.info(
958
996
  {
959
997
  task: taskSpec.title,
960
998
  model: repoConfig.model,
@@ -965,6 +1003,7 @@ async function runCodingAgent(params) {
965
1003
  },
966
1004
  "Starting coding agent"
967
1005
  );
1006
+ let stderrOutput = "";
968
1007
  const result = query({
969
1008
  prompt,
970
1009
  options: {
@@ -980,30 +1019,45 @@ async function runCodingAgent(params) {
980
1019
  preset: "claude_code",
981
1020
  append: CODER_SYSTEM_PROMPT_APPEND
982
1021
  },
1022
+ stderr: (data) => {
1023
+ stderrOutput += data;
1024
+ log5.debug({ stderr: data.trimEnd() }, "Agent SDK stderr");
1025
+ },
983
1026
  ...sessionId ? { resume: sessionId } : {}
984
1027
  }
985
1028
  });
986
1029
  let resultMessage;
987
- for await (const message of result) {
988
- if (message.type === "assistant" && "content" in message) {
989
- const content = message.content;
990
- for (const block of content) {
991
- if (block.type === "tool_use" && block.name) {
992
- const detail = describeToolUse(block.name, block.input);
993
- log4.info({ tool: block.name }, detail);
994
- onProgress?.(detail);
1030
+ try {
1031
+ for await (const message of result) {
1032
+ if (message.type === "assistant" && "content" in message) {
1033
+ const content = message.content;
1034
+ for (const block of content) {
1035
+ if (block.type === "tool_use" && block.name) {
1036
+ const detail = describeToolUse(block.name, block.input);
1037
+ log5.info({ tool: block.name }, detail);
1038
+ onProgress?.(detail);
1039
+ }
995
1040
  }
996
1041
  }
1042
+ if (message.type === "result") {
1043
+ resultMessage = message;
1044
+ }
997
1045
  }
998
- if (message.type === "result") {
999
- resultMessage = message;
1000
- }
1046
+ } catch (error) {
1047
+ const msg = error instanceof Error ? error.message : String(error);
1048
+ const stderrDetail = stderrOutput ? `
1049
+ stderr:
1050
+ ${stderrOutput.slice(0, 2e3)}` : "";
1051
+ throw new CodingAgentError(`Coding agent process failed: ${msg}${stderrDetail}`, [], 0);
1001
1052
  }
1002
1053
  if (!resultMessage) {
1003
- throw new CodingAgentError("No result message received from coding agent", [], 0);
1054
+ const detail = stderrOutput ? `
1055
+ stderr:
1056
+ ${stderrOutput.slice(0, 2e3)}` : "";
1057
+ throw new CodingAgentError(`No result message received from coding agent${detail}`, [], 0);
1004
1058
  }
1005
1059
  if (resultMessage.subtype === "error_max_turns") {
1006
- log4.warn(
1060
+ log5.warn(
1007
1061
  {
1008
1062
  cost: resultMessage.total_cost_usd,
1009
1063
  turns: resultMessage.num_turns
@@ -1018,13 +1072,16 @@ async function runCodingAgent(params) {
1018
1072
  };
1019
1073
  }
1020
1074
  if (resultMessage.subtype !== "success") {
1075
+ const stderrDetail = stderrOutput ? `
1076
+ stderr:
1077
+ ${stderrOutput.slice(0, 2e3)}` : "";
1021
1078
  throw new CodingAgentError(
1022
- `Coding agent failed: ${resultMessage.subtype}`,
1079
+ `Coding agent failed: ${resultMessage.subtype}${stderrDetail}`,
1023
1080
  "errors" in resultMessage ? resultMessage.errors : [],
1024
1081
  resultMessage.total_cost_usd
1025
1082
  );
1026
1083
  }
1027
- log4.info(
1084
+ log5.info(
1028
1085
  {
1029
1086
  cost: resultMessage.total_cost_usd,
1030
1087
  turns: resultMessage.num_turns
@@ -1042,7 +1099,7 @@ async function runCodingAgent(params) {
1042
1099
  // src/agents/planner.ts
1043
1100
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
1044
1101
  import { z as z2 } from "zod";
1045
- var log5 = createChildLogger("planner");
1102
+ var log6 = createChildLogger("planner");
1046
1103
  var ImplementationStepSchema = z2.object({
1047
1104
  order: z2.number().int().positive(),
1048
1105
  description: z2.string(),
@@ -1109,10 +1166,11 @@ function validatePlan(parsed) {
1109
1166
  async function runPlanningAgent(params) {
1110
1167
  const { taskSpec, repoConfig, workingDir, onProgress } = params;
1111
1168
  const prompt = buildPlannerPrompt(taskSpec);
1112
- log5.info(
1169
+ log6.info(
1113
1170
  { task: taskSpec.title, model: repoConfig.plannerModel },
1114
1171
  "Starting planning agent"
1115
1172
  );
1173
+ let stderrOutput = "";
1116
1174
  const result = query2({
1117
1175
  prompt,
1118
1176
  options: {
@@ -1126,34 +1184,49 @@ async function runPlanningAgent(params) {
1126
1184
  outputFormat: {
1127
1185
  type: "json_schema",
1128
1186
  schema: PLAN_JSON_SCHEMA
1187
+ },
1188
+ stderr: (data) => {
1189
+ stderrOutput += data;
1190
+ log6.debug({ stderr: data.trimEnd() }, "Agent SDK stderr");
1129
1191
  }
1130
1192
  }
1131
1193
  });
1132
1194
  let resultMessage;
1133
1195
  let lastAssistantText = "";
1134
- for await (const message of result) {
1135
- if (message.type === "assistant" && "content" in message) {
1136
- const content = message.content;
1137
- for (const block of content) {
1138
- if (block.type === "tool_use" && block.name) {
1139
- const detail = describeToolUse(block.name, block.input);
1140
- log5.info({ tool: block.name }, detail);
1141
- onProgress?.(detail);
1196
+ try {
1197
+ for await (const message of result) {
1198
+ if (message.type === "assistant" && "content" in message) {
1199
+ const content = message.content;
1200
+ for (const block of content) {
1201
+ if (block.type === "tool_use" && block.name) {
1202
+ const detail = describeToolUse(block.name, block.input);
1203
+ log6.info({ tool: block.name }, detail);
1204
+ onProgress?.(detail);
1205
+ }
1206
+ }
1207
+ const textParts = content.filter((c) => c.type === "text" && c.text).map((c) => c.text);
1208
+ if (textParts.length > 0) {
1209
+ lastAssistantText = textParts.join("\n");
1142
1210
  }
1143
1211
  }
1144
- const textParts = content.filter((c) => c.type === "text" && c.text).map((c) => c.text);
1145
- if (textParts.length > 0) {
1146
- lastAssistantText = textParts.join("\n");
1212
+ if (message.type === "result") {
1213
+ resultMessage = message;
1147
1214
  }
1148
1215
  }
1149
- if (message.type === "result") {
1150
- resultMessage = message;
1151
- }
1216
+ } catch (error) {
1217
+ const msg = error instanceof Error ? error.message : String(error);
1218
+ const stderrDetail = stderrOutput ? `
1219
+ stderr:
1220
+ ${stderrOutput.slice(0, 2e3)}` : "";
1221
+ throw new PipelineError(`Planning agent process failed: ${msg}${stderrDetail}`);
1152
1222
  }
1153
1223
  if (!resultMessage) {
1154
- throw new PipelineError("No result message received from planning agent");
1224
+ const detail = stderrOutput ? `
1225
+ stderr:
1226
+ ${stderrOutput.slice(0, 2e3)}` : "";
1227
+ throw new PipelineError(`No result message received from planning agent${detail}`);
1155
1228
  }
1156
- log5.debug(
1229
+ log6.debug(
1157
1230
  {
1158
1231
  subtype: resultMessage.subtype,
1159
1232
  hasStructuredOutput: "structured_output" in resultMessage && !!resultMessage.structured_output,
@@ -1167,12 +1240,15 @@ async function runPlanningAgent(params) {
1167
1240
  const isMaxTurns = resultMessage.subtype === "error_max_turns";
1168
1241
  if (!isSuccess && !isMaxTurns) {
1169
1242
  const errorDetail = "errors" in resultMessage ? resultMessage.errors.join(", ") : "unknown";
1243
+ const stderrDetail = stderrOutput ? `
1244
+ stderr:
1245
+ ${stderrOutput.slice(0, 2e3)}` : "";
1170
1246
  throw new PipelineError(
1171
- `Planning agent failed (${resultMessage.subtype}): ${errorDetail}`
1247
+ `Planning agent failed (${resultMessage.subtype}): ${errorDetail}${stderrDetail}`
1172
1248
  );
1173
1249
  }
1174
1250
  if (isMaxTurns) {
1175
- log5.warn("Planning agent hit max turns \u2014 attempting to extract plan");
1251
+ log6.warn("Planning agent hit max turns \u2014 attempting to extract plan");
1176
1252
  }
1177
1253
  let parsed;
1178
1254
  if ("structured_output" in resultMessage && resultMessage.structured_output) {
@@ -1187,7 +1263,7 @@ async function runPlanningAgent(params) {
1187
1263
  );
1188
1264
  }
1189
1265
  } else if (lastAssistantText) {
1190
- log5.warn("No result output \u2014 extracting plan from last assistant message");
1266
+ log6.warn("No result output \u2014 extracting plan from last assistant message");
1191
1267
  const jsonStr = extractJSON(lastAssistantText);
1192
1268
  try {
1193
1269
  parsed = JSON.parse(jsonStr);
@@ -1200,7 +1276,7 @@ async function runPlanningAgent(params) {
1200
1276
  throw new PipelineError("Planning agent produced no output");
1201
1277
  }
1202
1278
  const plan = validatePlan(parsed);
1203
- log5.info(
1279
+ log6.info(
1204
1280
  {
1205
1281
  complexity: plan.estimatedComplexity,
1206
1282
  steps: plan.implementationSteps.length,
@@ -1282,7 +1358,7 @@ Provide your review as a JSON object matching the specified schema.`;
1282
1358
  }
1283
1359
 
1284
1360
  // src/agents/reviewer.ts
1285
- var log6 = createChildLogger("reviewer");
1361
+ var log7 = createChildLogger("reviewer");
1286
1362
  var ReviewIssueSchema = z3.object({
1287
1363
  severity: z3.enum(["critical", "major", "minor", "suggestion"]),
1288
1364
  file: z3.string(),
@@ -1355,11 +1431,12 @@ function validateReviewResult(parsed) {
1355
1431
  }
1356
1432
  async function runReviewerAgent(params) {
1357
1433
  const { taskSpec, diff, repoConfig, reviewCycle } = params;
1358
- log6.info(
1434
+ log7.info(
1359
1435
  { model: repoConfig.reviewerModel, cycle: reviewCycle },
1360
1436
  "Starting reviewer agent"
1361
1437
  );
1362
1438
  const userPrompt = buildReviewerUserPrompt(taskSpec, diff, reviewCycle);
1439
+ let stderrOutput = "";
1363
1440
  const agentResult = query3({
1364
1441
  prompt: userPrompt,
1365
1442
  options: {
@@ -1372,33 +1449,51 @@ async function runReviewerAgent(params) {
1372
1449
  outputFormat: {
1373
1450
  type: "json_schema",
1374
1451
  schema: REVIEW_JSON_SCHEMA
1452
+ },
1453
+ stderr: (data) => {
1454
+ stderrOutput += data;
1455
+ log7.debug({ stderr: data.trimEnd() }, "Agent SDK stderr");
1375
1456
  }
1376
1457
  }
1377
1458
  });
1378
1459
  let resultMessage;
1379
1460
  let lastAssistantText = "";
1380
- for await (const message of agentResult) {
1381
- if (message.type === "assistant" && "content" in message) {
1382
- const textParts = message.content.filter((c) => c.type === "text" && c.text).map((c) => c.text);
1383
- if (textParts.length > 0) {
1384
- lastAssistantText = textParts.join("\n");
1461
+ try {
1462
+ for await (const message of agentResult) {
1463
+ if (message.type === "assistant" && "content" in message) {
1464
+ const textParts = message.content.filter((c) => c.type === "text" && c.text).map((c) => c.text);
1465
+ if (textParts.length > 0) {
1466
+ lastAssistantText = textParts.join("\n");
1467
+ }
1468
+ }
1469
+ if (message.type === "result") {
1470
+ resultMessage = message;
1385
1471
  }
1386
1472
  }
1387
- if (message.type === "result") {
1388
- resultMessage = message;
1389
- }
1473
+ } catch (error) {
1474
+ const msg = error instanceof Error ? error.message : String(error);
1475
+ const stderrDetail = stderrOutput ? `
1476
+ stderr:
1477
+ ${stderrOutput.slice(0, 2e3)}` : "";
1478
+ throw new ReviewerError(`Reviewer agent process failed: ${msg}${stderrDetail}`);
1390
1479
  }
1391
1480
  if (!resultMessage) {
1392
- throw new ReviewerError("No result message received from reviewer agent");
1481
+ const detail = stderrOutput ? `
1482
+ stderr:
1483
+ ${stderrOutput.slice(0, 2e3)}` : "";
1484
+ throw new ReviewerError(`No result message received from reviewer agent${detail}`);
1393
1485
  }
1394
1486
  const isSuccess = resultMessage.subtype === "success";
1395
1487
  const isMaxTurns = resultMessage.subtype === "error_max_turns";
1396
1488
  if (!isSuccess && !isMaxTurns) {
1397
1489
  const errorDetail = "errors" in resultMessage ? resultMessage.errors.join(", ") : "unknown";
1398
- throw new ReviewerError(`Reviewer agent failed (${resultMessage.subtype}): ${errorDetail}`);
1490
+ const stderrDetail = stderrOutput ? `
1491
+ stderr:
1492
+ ${stderrOutput.slice(0, 2e3)}` : "";
1493
+ throw new ReviewerError(`Reviewer agent failed (${resultMessage.subtype}): ${errorDetail}${stderrDetail}`);
1399
1494
  }
1400
1495
  if (isMaxTurns) {
1401
- log6.warn("Reviewer hit max turns \u2014 attempting to extract result");
1496
+ log7.warn("Reviewer hit max turns \u2014 attempting to extract result");
1402
1497
  }
1403
1498
  let parsed;
1404
1499
  if ("structured_output" in resultMessage && resultMessage.structured_output) {
@@ -1411,7 +1506,7 @@ async function runReviewerAgent(params) {
1411
1506
  throw new ReviewerError(`Reviewer returned invalid JSON: ${String(resultMessage.result).slice(0, 200)}`);
1412
1507
  }
1413
1508
  } else if (lastAssistantText) {
1414
- log6.warn("No result output \u2014 extracting review from last assistant message");
1509
+ log7.warn("No result output \u2014 extracting review from last assistant message");
1415
1510
  const jsonStr = extractJSON2(lastAssistantText);
1416
1511
  try {
1417
1512
  parsed = JSON.parse(jsonStr);
@@ -1424,7 +1519,7 @@ async function runReviewerAgent(params) {
1424
1519
  throw new ReviewerError("Reviewer agent produced no output");
1425
1520
  }
1426
1521
  const review = validateReviewResult(parsed);
1427
- log6.info(
1522
+ log7.info(
1428
1523
  {
1429
1524
  approved: review.approved,
1430
1525
  issueCount: review.issues.length,
@@ -1438,7 +1533,7 @@ async function runReviewerAgent(params) {
1438
1533
  // src/agents/security-scanner.ts
1439
1534
  import { query as query4 } from "@anthropic-ai/claude-agent-sdk";
1440
1535
  import { z as z4 } from "zod";
1441
- var log7 = createChildLogger("security-scanner");
1536
+ var log8 = createChildLogger("security-scanner");
1442
1537
  var SecurityFindingSchema = z4.object({
1443
1538
  severity: z4.enum(["critical", "high", "medium", "low", "info"]),
1444
1539
  category: z4.string(),
@@ -1517,7 +1612,7 @@ function validateReport(parsed) {
1517
1612
  }
1518
1613
  async function runSecurityScanner(params) {
1519
1614
  const { taskSpec, diff, repoConfig } = params;
1520
- log7.info({ task: taskSpec.title, model: repoConfig.reviewerModel }, "Starting security scan");
1615
+ log8.info({ task: taskSpec.title, model: repoConfig.reviewerModel }, "Starting security scan");
1521
1616
  const prompt = `## Security Review Request
1522
1617
 
1523
1618
  **Task**: ${taskSpec.title}
@@ -1530,6 +1625,7 @@ ${diff}
1530
1625
  \`\`\`
1531
1626
 
1532
1627
  Analyze this diff for security vulnerabilities. Provide your report as a JSON object matching the required schema.`;
1628
+ let stderrOutput = "";
1533
1629
  const result = query4({
1534
1630
  prompt,
1535
1631
  options: {
@@ -1542,31 +1638,49 @@ Analyze this diff for security vulnerabilities. Provide your report as a JSON ob
1542
1638
  outputFormat: {
1543
1639
  type: "json_schema",
1544
1640
  schema: SECURITY_REPORT_JSON_SCHEMA
1641
+ },
1642
+ stderr: (data) => {
1643
+ stderrOutput += data;
1644
+ log8.debug({ stderr: data.trimEnd() }, "Agent SDK stderr");
1545
1645
  }
1546
1646
  }
1547
1647
  });
1548
1648
  let resultMessage;
1549
1649
  let lastAssistantText = "";
1550
- for await (const message of result) {
1551
- if (message.type === "assistant" && "content" in message) {
1552
- const textParts = message.content.filter((c) => c.type === "text" && c.text).map((c) => c.text);
1553
- if (textParts.length > 0) {
1554
- lastAssistantText = textParts.join("\n");
1650
+ try {
1651
+ for await (const message of result) {
1652
+ if (message.type === "assistant" && "content" in message) {
1653
+ const textParts = message.content.filter((c) => c.type === "text" && c.text).map((c) => c.text);
1654
+ if (textParts.length > 0) {
1655
+ lastAssistantText = textParts.join("\n");
1656
+ }
1657
+ }
1658
+ if (message.type === "result") {
1659
+ resultMessage = message;
1555
1660
  }
1556
1661
  }
1557
- if (message.type === "result") {
1558
- resultMessage = message;
1559
- }
1662
+ } catch (error) {
1663
+ const msg = error instanceof Error ? error.message : String(error);
1664
+ const stderrDetail = stderrOutput ? `
1665
+ stderr:
1666
+ ${stderrOutput.slice(0, 2e3)}` : "";
1667
+ throw new PipelineError(`Security scanner process failed: ${msg}${stderrDetail}`);
1560
1668
  }
1561
1669
  if (!resultMessage) {
1562
- throw new PipelineError("No result message received from security scanner");
1670
+ const detail = stderrOutput ? `
1671
+ stderr:
1672
+ ${stderrOutput.slice(0, 2e3)}` : "";
1673
+ throw new PipelineError(`No result message received from security scanner${detail}`);
1563
1674
  }
1564
1675
  const isSuccess = resultMessage.subtype === "success";
1565
1676
  const isMaxTurns = resultMessage.subtype === "error_max_turns";
1566
1677
  if (!isSuccess && !isMaxTurns) {
1567
1678
  const errorDetail = "errors" in resultMessage ? resultMessage.errors.join(", ") : "unknown";
1679
+ const stderrDetail = stderrOutput ? `
1680
+ stderr:
1681
+ ${stderrOutput.slice(0, 2e3)}` : "";
1568
1682
  throw new PipelineError(
1569
- `Security scanner failed (${resultMessage.subtype}): ${errorDetail}`
1683
+ `Security scanner failed (${resultMessage.subtype}): ${errorDetail}${stderrDetail}`
1570
1684
  );
1571
1685
  }
1572
1686
  let parsed;
@@ -1582,7 +1696,7 @@ Analyze this diff for security vulnerabilities. Provide your report as a JSON ob
1582
1696
  );
1583
1697
  }
1584
1698
  } else if (lastAssistantText) {
1585
- log7.warn("No result output \u2014 extracting report from last assistant message");
1699
+ log8.warn("No result output \u2014 extracting report from last assistant message");
1586
1700
  const jsonStr = extractJSON3(lastAssistantText);
1587
1701
  try {
1588
1702
  parsed = JSON.parse(jsonStr);
@@ -1595,7 +1709,7 @@ Analyze this diff for security vulnerabilities. Provide your report as a JSON ob
1595
1709
  throw new PipelineError("Security scanner produced no output");
1596
1710
  }
1597
1711
  const report = validateReport(parsed);
1598
- log7.info(
1712
+ log8.info(
1599
1713
  {
1600
1714
  riskLevel: report.riskLevel,
1601
1715
  findingCount: report.findings.length
@@ -1610,11 +1724,11 @@ Analyze this diff for security vulnerabilities. Provide your report as a JSON ob
1610
1724
 
1611
1725
  // src/testing/test-runner.ts
1612
1726
  import { execa } from "execa";
1613
- var log8 = createChildLogger("test-runner");
1727
+ var log9 = createChildLogger("test-runner");
1614
1728
  async function runCommand(params) {
1615
1729
  const { workingDir, command, kind, timeoutMs = 3e5 } = params;
1616
1730
  const [cmd, ...args] = command.split(" ");
1617
- log8.info({ kind, command, workingDir }, `Running ${kind} command`);
1731
+ log9.info({ kind, command, workingDir }, `Running ${kind} command`);
1618
1732
  const start = Date.now();
1619
1733
  try {
1620
1734
  const result = await execa(cmd, args, {
@@ -1625,7 +1739,7 @@ async function runCommand(params) {
1625
1739
  });
1626
1740
  const durationMs = Date.now() - start;
1627
1741
  const passed = result.exitCode === 0;
1628
- log8.info({ kind, passed, durationMs, exitCode: result.exitCode }, `${kind} completed`);
1742
+ log9.info({ kind, passed, durationMs, exitCode: result.exitCode }, `${kind} completed`);
1629
1743
  return {
1630
1744
  passed,
1631
1745
  output: result.all ?? result.stdout + "\n" + result.stderr,
@@ -1634,7 +1748,7 @@ async function runCommand(params) {
1634
1748
  } catch (error) {
1635
1749
  const durationMs = Date.now() - start;
1636
1750
  const message = error instanceof Error ? error.message : String(error);
1637
- log8.error({ kind, error: message, durationMs }, `${kind} execution failed`);
1751
+ log9.error({ kind, error: message, durationMs }, `${kind} execution failed`);
1638
1752
  return {
1639
1753
  passed: false,
1640
1754
  output: message,
@@ -1652,7 +1766,7 @@ async function runTests(params) {
1652
1766
  }
1653
1767
 
1654
1768
  // src/notifications/sender.ts
1655
- var log9 = createChildLogger("notifications");
1769
+ var log10 = createChildLogger("notifications");
1656
1770
  async function sendSlackWebhook(webhookUrl, message) {
1657
1771
  try {
1658
1772
  const response = await fetch(webhookUrl, {
@@ -1661,11 +1775,11 @@ async function sendSlackWebhook(webhookUrl, message) {
1661
1775
  body: JSON.stringify(message)
1662
1776
  });
1663
1777
  if (!response.ok) {
1664
- log9.warn({ status: response.status }, "Slack webhook request failed");
1778
+ log10.warn({ status: response.status }, "Slack webhook request failed");
1665
1779
  }
1666
1780
  } catch (error) {
1667
1781
  const msg = error instanceof Error ? error.message : String(error);
1668
- log9.warn({ error: msg }, "Failed to send Slack notification");
1782
+ log10.warn({ error: msg }, "Failed to send Slack notification");
1669
1783
  }
1670
1784
  }
1671
1785
  function formatSlackMessage(state, event) {
@@ -1718,7 +1832,7 @@ async function postGitHubMentions(config, owner, repo, issueNumber, event, state
1718
1832
  await addComment(owner, repo, issueNumber, message);
1719
1833
  } catch (error) {
1720
1834
  const msg = error instanceof Error ? error.message : String(error);
1721
- log9.warn({ error: msg }, "Failed to post GitHub mention");
1835
+ log10.warn({ error: msg }, "Failed to post GitHub mention");
1722
1836
  }
1723
1837
  }
1724
1838
  }
@@ -1735,7 +1849,7 @@ async function notify(params) {
1735
1849
  }
1736
1850
 
1737
1851
  // src/pipeline/auto-merge.ts
1738
- var log10 = createChildLogger("auto-merge");
1852
+ var log11 = createChildLogger("auto-merge");
1739
1853
  var SIZE_ORDER = {
1740
1854
  xs: 1,
1741
1855
  s: 2,
@@ -1767,20 +1881,20 @@ function isAutoMergeEligible(params) {
1767
1881
  }
1768
1882
  }
1769
1883
  if (reasons.length > 0) {
1770
- log10.info({ reasons }, "PR not eligible for auto-merge");
1884
+ log11.info({ reasons }, "PR not eligible for auto-merge");
1771
1885
  }
1772
1886
  return { eligible: reasons.length === 0, reasons };
1773
1887
  }
1774
1888
 
1775
1889
  // src/tracking/cost-tracker.ts
1776
- import { homedir } from "os";
1777
- import { join as join2 } from "path";
1890
+ import { homedir as homedir2 } from "os";
1891
+ import { join as join3 } from "path";
1778
1892
  import { mkdir, appendFile, readFile } from "fs/promises";
1779
- var log11 = createChildLogger("cost-tracker");
1780
- var HISTORY_DIR = join2(homedir(), ".xforce");
1893
+ var log12 = createChildLogger("cost-tracker");
1894
+ var HISTORY_DIR = join3(homedir2(), ".xforce");
1781
1895
  var HISTORY_FILE = "history.jsonl";
1782
1896
  function getHistoryPath(basePath) {
1783
- return join2(basePath ?? HISTORY_DIR, HISTORY_FILE);
1897
+ return join3(basePath ?? HISTORY_DIR, HISTORY_FILE);
1784
1898
  }
1785
1899
  function buildRecordFromState(state, repoConfig) {
1786
1900
  const startedAt = state.startedAt instanceof Date ? state.startedAt : new Date(state.startedAt);
@@ -1808,10 +1922,10 @@ function buildRecordFromState(state, repoConfig) {
1808
1922
  async function appendRecord(record, basePath) {
1809
1923
  const dir = basePath ?? HISTORY_DIR;
1810
1924
  await mkdir(dir, { recursive: true });
1811
- const filePath = join2(dir, HISTORY_FILE);
1925
+ const filePath = join3(dir, HISTORY_FILE);
1812
1926
  const line = JSON.stringify(record) + "\n";
1813
1927
  await appendFile(filePath, line, "utf-8");
1814
- log11.debug({ id: record.id, repo: record.repo }, "Persisted pipeline run record");
1928
+ log12.debug({ id: record.id, repo: record.repo }, "Persisted pipeline run record");
1815
1929
  }
1816
1930
  async function readRecords(filter, basePath) {
1817
1931
  const filePath = getHistoryPath(basePath);
@@ -1832,7 +1946,7 @@ async function readRecords(filter, basePath) {
1832
1946
  if (filter?.until && new Date(record.startedAt) > filter.until) continue;
1833
1947
  records.push(record);
1834
1948
  } catch {
1835
- log11.warn("Skipping malformed line in history file");
1949
+ log12.warn("Skipping malformed line in history file");
1836
1950
  }
1837
1951
  }
1838
1952
  return records;
@@ -1869,12 +1983,12 @@ function summarize(records) {
1869
1983
  }
1870
1984
 
1871
1985
  // src/pipeline/orchestrator.ts
1872
- var log12 = createChildLogger("orchestrator");
1986
+ var log13 = createChildLogger("orchestrator");
1873
1987
  function transition(state, to, message) {
1874
1988
  validateTransition(state.status, to);
1875
1989
  state.status = to;
1876
1990
  state.logs.push({ timestamp: /* @__PURE__ */ new Date(), status: to, message });
1877
- log12.info({ status: to }, message);
1991
+ log13.info({ status: to }, message);
1878
1992
  }
1879
1993
  async function runPipeline(params) {
1880
1994
  const { config } = params;
@@ -1915,7 +2029,7 @@ async function runPipeline(params) {
1915
2029
  await removeLabel(owner, repo, issueNumber, repoConfig.labels.failed).catch(() => {
1916
2030
  });
1917
2031
  await addLabel(owner, repo, issueNumber, repoConfig.labels.inProgress);
1918
- log12.info({ owner, repo, issueNumber }, "Fetching and parsing issue");
2032
+ log13.info({ owner, repo, issueNumber }, "Fetching and parsing issue");
1919
2033
  const issue = await getIssue(owner, repo, issueNumber);
1920
2034
  const labels = await getIssueLabels(owner, repo, issueNumber);
1921
2035
  const taskSpec = parseIssueBody({
@@ -1982,7 +2096,7 @@ async function runPipeline(params) {
1982
2096
  await notify({ config: config.notifications, state, event: "completed", owner, repo, issueNumber });
1983
2097
  state.completedAt = /* @__PURE__ */ new Date();
1984
2098
  await appendRecord(buildRecordFromState(state, repoConfig)).catch((err) => {
1985
- log12.warn({ error: err.message }, "Failed to persist cost tracking record");
2099
+ log13.warn({ error: err.message }, "Failed to persist cost tracking record");
1986
2100
  });
1987
2101
  if (state.status === "merging") {
1988
2102
  transition(state, "completed", "PR auto-merged");
@@ -1995,7 +2109,7 @@ async function runPipeline(params) {
1995
2109
  state.status = "failed";
1996
2110
  state.error = message;
1997
2111
  state.completedAt = /* @__PURE__ */ new Date();
1998
- log12.error({ error: message }, "Pipeline failed");
2112
+ log13.error({ error: message }, "Pipeline failed");
1999
2113
  await notify({ config: config.notifications, state, event: "failed", owner, repo, issueNumber }).catch(() => {
2000
2114
  });
2001
2115
  await appendRecord(buildRecordFromState(state, repoConfig)).catch(() => {
@@ -2013,7 +2127,7 @@ async function runPipeline(params) {
2013
2127
  **Cost**: $${state.totalCostUsd.toFixed(4)}`
2014
2128
  );
2015
2129
  } catch {
2016
- log12.warn("Failed to update issue labels/comments after pipeline failure");
2130
+ log13.warn("Failed to update issue labels/comments after pipeline failure");
2017
2131
  }
2018
2132
  return state;
2019
2133
  } finally {
@@ -2023,7 +2137,7 @@ async function runPipeline(params) {
2023
2137
  try {
2024
2138
  await rm(workDir, { recursive: true, force: true });
2025
2139
  } catch {
2026
- log12.warn({ workDir }, "Failed to clean up working directory");
2140
+ log13.warn({ workDir }, "Failed to clean up working directory");
2027
2141
  }
2028
2142
  }
2029
2143
  }
@@ -2326,6 +2440,7 @@ ${review.securityConcerns.map((c) => `- ${c}`).join("\n")}` : ""}
2326
2440
  async function runCommand2(params) {
2327
2441
  const spinner = ora2("Loading configuration...").start();
2328
2442
  try {
2443
+ ensureCredentials();
2329
2444
  const config = loadConfig(params.configPath);
2330
2445
  spinner.succeed("Configuration loaded");
2331
2446
  let owner;
@@ -2450,7 +2565,7 @@ async function reviewCommand(params) {
2450
2565
  }
2451
2566
 
2452
2567
  // src/cli/commands/init.ts
2453
- import { join as join3, resolve as resolve3 } from "path";
2568
+ import { join as join4, resolve as resolve3 } from "path";
2454
2569
  import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile2, access as access2 } from "fs/promises";
2455
2570
  import { fileURLToPath } from "url";
2456
2571
  import ora4 from "ora";
@@ -2459,9 +2574,9 @@ async function findTemplateDir() {
2459
2574
  const thisFile = fileURLToPath(import.meta.url);
2460
2575
  let dir = resolve3(thisFile, "..");
2461
2576
  for (let i = 0; i < 6; i++) {
2462
- const pkgPath = join3(dir, "package.json");
2577
+ const pkgPath = join4(dir, "package.json");
2463
2578
  if (await fileExists(pkgPath)) {
2464
- return join3(dir, "templates");
2579
+ return join4(dir, "templates");
2465
2580
  }
2466
2581
  dir = resolve3(dir, "..");
2467
2582
  }
@@ -2532,21 +2647,21 @@ async function initCommand(params) {
2532
2647
  const testCommand = params.testCommand ?? "npm test";
2533
2648
  const created = [];
2534
2649
  const skipped = [];
2535
- const issueTemplateDir = join3(targetDir, ".github", "ISSUE_TEMPLATE");
2650
+ const issueTemplateDir = join4(targetDir, ".github", "ISSUE_TEMPLATE");
2536
2651
  await mkdir2(issueTemplateDir, { recursive: true });
2537
2652
  const templateNames = ["feature-request.yml", "bug-fix.yml", "refactor.yml"];
2538
- const srcTemplateDir = join3(await findTemplateDir(), "issue-templates");
2653
+ const srcTemplateDir = join4(await findTemplateDir(), "issue-templates");
2539
2654
  for (const tmpl of templateNames) {
2540
- const destPath = join3(issueTemplateDir, tmpl);
2655
+ const destPath = join4(issueTemplateDir, tmpl);
2541
2656
  if (await fileExists(destPath)) {
2542
2657
  skipped.push(`.github/ISSUE_TEMPLATE/${tmpl}`);
2543
2658
  } else {
2544
- const content = await readFile2(join3(srcTemplateDir, tmpl), "utf-8");
2659
+ const content = await readFile2(join4(srcTemplateDir, tmpl), "utf-8");
2545
2660
  await writeFile2(destPath, content, "utf-8");
2546
2661
  created.push(`.github/ISSUE_TEMPLATE/${tmpl}`);
2547
2662
  }
2548
2663
  }
2549
- const envPath = join3(targetDir, ".env.xforce");
2664
+ const envPath = join4(targetDir, ".env.xforce");
2550
2665
  if (await fileExists(envPath)) {
2551
2666
  skipped.push(".env.xforce");
2552
2667
  } else {
@@ -2563,7 +2678,7 @@ XFORCE_WEBHOOK_SECRET=
2563
2678
  );
2564
2679
  created.push(".env.xforce");
2565
2680
  }
2566
- const gitignorePath = join3(targetDir, ".gitignore");
2681
+ const gitignorePath = join4(targetDir, ".gitignore");
2567
2682
  let gitignoreContent = "";
2568
2683
  try {
2569
2684
  gitignoreContent = await readFile2(gitignorePath, "utf-8");
@@ -2577,7 +2692,7 @@ XFORCE_WEBHOOK_SECRET=
2577
2692
  created.push(".gitignore");
2578
2693
  }
2579
2694
  }
2580
- const configPath = join3(targetDir, "xforce.config.yaml");
2695
+ const configPath = join4(targetDir, "xforce.config.yaml");
2581
2696
  if (await fileExists(configPath)) {
2582
2697
  skipped.push("xforce.config.yaml");
2583
2698
  } else {
@@ -2702,7 +2817,7 @@ import crypto from "crypto";
2702
2817
 
2703
2818
  // src/server/queue.ts
2704
2819
  import { nanoid as nanoid2 } from "nanoid";
2705
- var log13 = createChildLogger("queue");
2820
+ var log14 = createChildLogger("queue");
2706
2821
  var DEFAULT_OPTIONS = {
2707
2822
  maxSize: 10,
2708
2823
  historySize: 50
@@ -2739,7 +2854,7 @@ var JobQueue = class {
2739
2854
  enqueuedAt: /* @__PURE__ */ new Date()
2740
2855
  };
2741
2856
  this.pending.push(job);
2742
- log13.info(
2857
+ log14.info(
2743
2858
  { jobId: job.id, owner: job.owner, repo: job.repo, issue: job.issueNumber },
2744
2859
  "Job enqueued"
2745
2860
  );
@@ -2776,7 +2891,7 @@ var JobQueue = class {
2776
2891
  job.status = "running";
2777
2892
  job.startedAt = /* @__PURE__ */ new Date();
2778
2893
  this.active = job;
2779
- log13.info(
2894
+ log14.info(
2780
2895
  { jobId: job.id, owner: job.owner, repo: job.repo, issue: job.issueNumber },
2781
2896
  "Processing job"
2782
2897
  );
@@ -2787,7 +2902,7 @@ var JobQueue = class {
2787
2902
  } catch (error) {
2788
2903
  job.status = "failed";
2789
2904
  job.error = error instanceof Error ? error.message : String(error);
2790
- log13.error(
2905
+ log14.error(
2791
2906
  { jobId: job.id, error: job.error },
2792
2907
  "Job failed"
2793
2908
  );
@@ -2800,7 +2915,7 @@ var JobQueue = class {
2800
2915
  this.completed = this.completed.slice(0, this.options.historySize);
2801
2916
  }
2802
2917
  this.processing = false;
2803
- log13.info(
2918
+ log14.info(
2804
2919
  { jobId: job.id, status: job.status, totalProcessed: this.totalProcessed },
2805
2920
  "Job finished"
2806
2921
  );
@@ -2810,7 +2925,7 @@ var JobQueue = class {
2810
2925
  };
2811
2926
 
2812
2927
  // src/server/webhook.ts
2813
- var log14 = createChildLogger("webhook-server");
2928
+ var log15 = createChildLogger("webhook-server");
2814
2929
  var MAX_BODY_SIZE = 1048576;
2815
2930
  function readBody(req) {
2816
2931
  return new Promise((resolve4, reject) => {
@@ -2927,7 +3042,7 @@ function createWebhookServer(config, options) {
2927
3042
  }
2928
3043
  try {
2929
3044
  const job = queue.enqueue({ owner, repo, issueNumber, issueUrl });
2930
- log14.info(
3045
+ log15.info(
2931
3046
  { jobId: job.id, owner, repo, issueNumber },
2932
3047
  "Webhook accepted, job enqueued"
2933
3048
  );
@@ -2946,7 +3061,7 @@ function createWebhookServer(config, options) {
2946
3061
  const server = createServer(async (req, res) => {
2947
3062
  const method = req.method ?? "GET";
2948
3063
  const url = req.url ?? "/";
2949
- log14.debug({ method, url }, "Request received");
3064
+ log15.debug({ method, url }, "Request received");
2950
3065
  try {
2951
3066
  if (method === "GET" && url === "/health") {
2952
3067
  handleHealth(req, res);
@@ -2959,7 +3074,7 @@ function createWebhookServer(config, options) {
2959
3074
  }
2960
3075
  } catch (error) {
2961
3076
  const message = error instanceof Error ? error.message : String(error);
2962
- log14.error({ error: message }, "Request handler error");
3077
+ log15.error({ error: message }, "Request handler error");
2963
3078
  if (!res.headersSent) {
2964
3079
  sendJson(res, 500, { error: "Internal server error" });
2965
3080
  }
@@ -2969,7 +3084,7 @@ function createWebhookServer(config, options) {
2969
3084
  start() {
2970
3085
  return new Promise((resolve4, reject) => {
2971
3086
  server.listen(options.port, options.host, () => {
2972
- log14.info({ port: options.port, host: options.host }, "Webhook server started");
3087
+ log15.info({ port: options.port, host: options.host }, "Webhook server started");
2973
3088
  resolve4();
2974
3089
  });
2975
3090
  server.on("error", reject);
@@ -2978,7 +3093,7 @@ function createWebhookServer(config, options) {
2978
3093
  stop() {
2979
3094
  return new Promise((resolve4) => {
2980
3095
  server.close(() => {
2981
- log14.info("Webhook server stopped");
3096
+ log15.info("Webhook server stopped");
2982
3097
  resolve4();
2983
3098
  });
2984
3099
  });
@@ -3003,6 +3118,13 @@ async function serveCommand(params) {
3003
3118
  );
3004
3119
  process.exit(1);
3005
3120
  }
3121
+ try {
3122
+ ensureCredentials();
3123
+ } catch (error) {
3124
+ const message = error instanceof Error ? error.message : String(error);
3125
+ console.error(chalk5.red(`Credentials error: ${message}`));
3126
+ process.exit(1);
3127
+ }
3006
3128
  let config;
3007
3129
  try {
3008
3130
  config = loadConfig(params.configPath);