@rallycry/conveyor-agent 6.0.7 → 6.0.9

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.
@@ -487,6 +487,10 @@ var ConveyorConnection = class _ConveyorConnection {
487
487
  if (!this.socket) return;
488
488
  this.socket.emit("agentRunner:debugReproduceRequested", { hypothesis });
489
489
  }
490
+ emitCodeReviewResult(content, approved) {
491
+ if (!this.socket) throw new Error("Not connected");
492
+ this.socket.emit("agentRunner:codeReviewResult", { content, approved });
493
+ }
490
494
  searchIncidents(status, source) {
491
495
  return searchIncidents(this.socket, status, source);
492
496
  }
@@ -2035,10 +2039,52 @@ function buildModePrompt(agentMode, context) {
2035
2039
  ].join("\n");
2036
2040
  case "auto":
2037
2041
  return buildAutoPrompt(context);
2042
+ case "code-review":
2043
+ return buildCodeReviewPrompt();
2038
2044
  default:
2039
2045
  return null;
2040
2046
  }
2041
2047
  }
2048
+ function buildCodeReviewPrompt() {
2049
+ return [
2050
+ `
2051
+ ## Mode: Code Review`,
2052
+ `You are an automated code reviewer. A PR has passed all CI checks and you are performing a final code quality review before merge.`,
2053
+ ``,
2054
+ `## Review Process`,
2055
+ `1. Run \`git diff <devBranch>..HEAD\` to see all changes in this PR`,
2056
+ `2. Read the task plan to understand the intended changes`,
2057
+ `3. Explore the surrounding codebase to verify pattern consistency`,
2058
+ `4. Review against the criteria below`,
2059
+ ``,
2060
+ `### Review Criteria`,
2061
+ `- **Correctness**: Does the code do what the plan says? Logic errors, off-by-one, race conditions?`,
2062
+ `- **Pattern Consistency**: Does the code follow existing patterns in the codebase? Check nearby files.`,
2063
+ `- **Security**: No hardcoded secrets, no injection vulnerabilities, proper input validation at boundaries.`,
2064
+ `- **Performance**: No unnecessary loops, no N+1 queries, no blocking in async contexts.`,
2065
+ `- **Error Handling**: Appropriate error handling at system boundaries. No swallowed errors.`,
2066
+ `- **Test Coverage**: Are new code paths tested? Edge cases covered?`,
2067
+ `- **TypeScript Best Practices**: Proper typing (no unnecessary \`any\`), correct React patterns, proper async/await.`,
2068
+ `- **Naming & Readability**: Clear names, no misleading comments, self-documenting code.`,
2069
+ ``,
2070
+ `## Output \u2014 You MUST do exactly ONE of:`,
2071
+ ``,
2072
+ `### If code passes review:`,
2073
+ `Use the \`approve_code_review\` tool with a brief summary of what looks good.`,
2074
+ ``,
2075
+ `### If changes are needed:`,
2076
+ `Use the \`request_code_changes\` tool with specific issues:`,
2077
+ `- Reference specific files and line numbers`,
2078
+ `- Explain what's wrong and suggest fixes`,
2079
+ `- Focus on substantive issues, not style nitpicks (linting handles that)`,
2080
+ ``,
2081
+ `## Rules`,
2082
+ `- You are READ-ONLY. Do NOT modify any files.`,
2083
+ `- Do NOT re-review things CI already validates (formatting, lint rules).`,
2084
+ `- Be concise \u2014 the task agent needs actionable feedback, not essays.`,
2085
+ `- Max 5-7 issues per review. Prioritize the most important ones.`
2086
+ ].join("\n");
2087
+ }
2042
2088
 
2043
2089
  // src/execution/system-prompt.ts
2044
2090
  function formatProjectAgentLine(pa) {
@@ -2410,6 +2456,18 @@ ${context.plan}`);
2410
2456
  }
2411
2457
  return parts;
2412
2458
  }
2459
+ function buildCodeReviewInstructions(context) {
2460
+ const parts = [
2461
+ `You are performing an automated code review for this task.`,
2462
+ `The PR branch is "${context.githubBranch}"${context.baseBranch ? ` based on "${context.baseBranch}"` : ""}.`,
2463
+ `Begin your code review by running \`git diff ${context.baseBranch ?? "dev"}..HEAD\` to see all changes.`,
2464
+ ``,
2465
+ `CRITICAL: You are in Code Review mode. You are READ-ONLY \u2014 do NOT modify any files.`,
2466
+ `After reviewing, you MUST call exactly one of: \`approve_code_review\` or \`request_code_changes\`.`,
2467
+ `Do NOT go idle, ask for confirmation, or wait for instructions \u2014 complete the review and exit.`
2468
+ ];
2469
+ return parts;
2470
+ }
2413
2471
  function buildFreshInstructions(isPm, isAutoMode, context, agentMode) {
2414
2472
  if (isPm && agentMode === "building") {
2415
2473
  return [
@@ -2507,6 +2565,10 @@ Address the requested changes directly. Do NOT re-investigate the codebase from
2507
2565
  function buildInstructions(mode, context, scenario, agentMode, isAuto) {
2508
2566
  const parts = [`
2509
2567
  ## Instructions`];
2568
+ if (agentMode === "code-review") {
2569
+ parts.push(...buildCodeReviewInstructions(context));
2570
+ return parts;
2571
+ }
2510
2572
  const isPm = mode === "pm";
2511
2573
  if (scenario === "fresh") {
2512
2574
  parts.push(...buildFreshInstructions(isPm, agentMode === "auto", context, agentMode));
@@ -2558,7 +2620,7 @@ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
2558
2620
  }
2559
2621
  async function buildInitialPrompt(mode, context, isAuto, agentMode) {
2560
2622
  const isPackRunner = mode === "pm" && !!isAuto && !!context.isParentTask;
2561
- if (!isPackRunner) {
2623
+ if (!isPackRunner && agentMode !== "code-review") {
2562
2624
  const sessionRelaunch = buildRelaunchWithSession(mode, context, agentMode, isAuto);
2563
2625
  if (sessionRelaunch) return sessionRelaunch;
2564
2626
  }
@@ -4365,6 +4427,73 @@ function buildDebugTools(manager) {
4365
4427
  ];
4366
4428
  }
4367
4429
 
4430
+ // src/tools/code-review-tools.ts
4431
+ import { tool as tool7 } from "@anthropic-ai/claude-agent-sdk";
4432
+ import { z as z7 } from "zod";
4433
+ function buildCodeReviewTools(connection) {
4434
+ return [
4435
+ tool7(
4436
+ "approve_code_review",
4437
+ "Approve the code review. Use this when the code passes all review criteria and is ready to merge.",
4438
+ {
4439
+ summary: z7.string().describe("Brief summary of what was reviewed and why it looks good")
4440
+ },
4441
+ // eslint-disable-next-line require-await -- SDK tool() API requires async handler
4442
+ async ({ summary }) => {
4443
+ connection.emitCodeReviewResult(
4444
+ `**Code Review: Approved** :white_check_mark:
4445
+
4446
+ ${summary}`,
4447
+ true
4448
+ );
4449
+ connection.sendEvent({
4450
+ type: "code_review_complete",
4451
+ result: "approved",
4452
+ summary
4453
+ });
4454
+ return textResult("Code review approved. Exiting.");
4455
+ }
4456
+ ),
4457
+ tool7(
4458
+ "request_code_changes",
4459
+ "Request changes during code review. Use this when substantive issues are found that need to be fixed before merge.",
4460
+ {
4461
+ issues: z7.array(
4462
+ z7.object({
4463
+ file: z7.string().describe("File path where the issue was found"),
4464
+ line: z7.number().optional().describe("Line number (if applicable)"),
4465
+ severity: z7.enum(["critical", "major", "minor"]).describe("Issue severity"),
4466
+ description: z7.string().describe("What is wrong and how to fix it")
4467
+ })
4468
+ ).describe("List of issues found during review"),
4469
+ summary: z7.string().describe("Brief overall summary of the review findings")
4470
+ },
4471
+ // eslint-disable-next-line require-await -- SDK tool() API requires async handler
4472
+ async ({ issues, summary }) => {
4473
+ const issueLines = issues.map((issue) => {
4474
+ const loc = issue.line ? `:${issue.line}` : "";
4475
+ return `- **[${issue.severity}]** \`${issue.file}${loc}\`: ${issue.description}`;
4476
+ }).join("\n");
4477
+ connection.emitCodeReviewResult(
4478
+ `**Code Review: Changes Requested** :warning:
4479
+
4480
+ ${summary}
4481
+
4482
+ ${issueLines}`,
4483
+ false
4484
+ );
4485
+ connection.sendEvent({
4486
+ type: "code_review_complete",
4487
+ result: "changes_requested",
4488
+ summary,
4489
+ issues
4490
+ });
4491
+ return textResult("Code review complete \u2014 changes requested. Exiting.");
4492
+ }
4493
+ )
4494
+ ];
4495
+ }
4496
+
4368
4497
  // src/tools/index.ts
4369
4498
  function textResult(text) {
4370
4499
  return { content: [{ type: "text", text }] };
@@ -4395,8 +4524,22 @@ function getModeTools(agentMode, connection, config, context) {
4395
4524
  }
4396
4525
  }
4397
4526
  function createConveyorMcpServer(connection, config, context, agentMode, debugManager) {
4398
- const commonTools = buildCommonTools(connection, config);
4399
4527
  const effectiveMode = agentMode ?? context?.agentMode ?? void 0;
4528
+ if (effectiveMode === "code-review") {
4529
+ return createSdkMcpServer({
4530
+ name: "conveyor",
4531
+ tools: [
4532
+ buildReadTaskChatTool(connection),
4533
+ buildGetTaskPlanTool(connection, config),
4534
+ buildGetTaskTool(connection),
4535
+ buildGetTaskCliTool(connection),
4536
+ buildListTaskFilesTool(connection),
4537
+ buildGetTaskFileTool(connection),
4538
+ ...buildCodeReviewTools(connection)
4539
+ ]
4540
+ });
4541
+ }
4542
+ const commonTools = buildCommonTools(connection, config);
4400
4543
  const modeTools = getModeTools(effectiveMode, connection, config, context);
4401
4544
  const discoveryTools = effectiveMode === "discovery" || effectiveMode === "auto" ? buildDiscoveryTools(connection, context) : [];
4402
4545
  const debugTools = debugManager && effectiveMode === "building" ? buildDebugTools(debugManager) : [];
@@ -4411,6 +4554,7 @@ function createConveyorMcpServer(connection, config, context, agentMode, debugMa
4411
4554
  import { randomUUID } from "crypto";
4412
4555
  var PM_PLAN_FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit"]);
4413
4556
  var DESTRUCTIVE_CMD_PATTERN = /git\s+push\s+--force(?!\s*-with-lease)|git\s+reset\s+--hard|rm\s+-rf\s+\//;
4557
+ var CODE_REVIEW_WRITE_CMD_PATTERN = /git\s+push|git\s+commit|git\s+add|rm\s+|mv\s+|cp\s+|mkdir\s+|touch\s+|chmod\s+|chown\s+/;
4414
4558
  function isPlanFile(input) {
4415
4559
  const filePath = String(input.file_path ?? input.path ?? "");
4416
4560
  return filePath.includes(".claude/plans/");
@@ -4460,6 +4604,24 @@ function handleReviewToolAccess(toolName, input, isParentTask) {
4460
4604
  }
4461
4605
  return { behavior: "allow", updatedInput: input };
4462
4606
  }
4607
+ function handleCodeReviewToolAccess(toolName, input) {
4608
+ if (PM_PLAN_FILE_TOOLS.has(toolName)) {
4609
+ return {
4610
+ behavior: "deny",
4611
+ message: "Code review mode is fully read-only. File writes are not permitted."
4612
+ };
4613
+ }
4614
+ if (toolName === "Bash") {
4615
+ const cmd = String(input.command ?? "");
4616
+ if (DESTRUCTIVE_CMD_PATTERN.test(cmd) || CODE_REVIEW_WRITE_CMD_PATTERN.test(cmd)) {
4617
+ return {
4618
+ behavior: "deny",
4619
+ message: "Code review mode is read-only. Write operations and destructive commands are blocked."
4620
+ };
4621
+ }
4622
+ }
4623
+ return { behavior: "allow", updatedInput: input };
4624
+ }
4463
4625
  function handleAutoToolAccess(toolName, input, hasExitedPlanMode, isParentTask) {
4464
4626
  if (hasExitedPlanMode) {
4465
4627
  return isParentTask ? handleReviewToolAccess(toolName, input, true) : handleBuildingToolAccess(toolName, input);
@@ -4564,6 +4726,9 @@ function buildCanUseTool(host) {
4564
4726
  case "auto":
4565
4727
  result = handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
4566
4728
  break;
4729
+ case "code-review":
4730
+ result = handleCodeReviewToolAccess(toolName, input);
4731
+ break;
4567
4732
  default:
4568
4733
  result = { behavior: "allow", updatedInput: input };
4569
4734
  }
@@ -4611,10 +4776,13 @@ function buildHooks(host) {
4611
4776
  };
4612
4777
  }
4613
4778
  function isReadOnlyMode(mode, hasExitedPlanMode) {
4614
- return mode === "discovery" || mode === "help" || mode === "auto" && !hasExitedPlanMode;
4779
+ return mode === "discovery" || mode === "help" || mode === "code-review" || mode === "auto" && !hasExitedPlanMode;
4615
4780
  }
4616
4781
  function buildDisallowedTools(settings, mode, hasExitedPlanMode) {
4617
4782
  const modeDisallowed = isReadOnlyMode(mode, hasExitedPlanMode) ? ["TodoWrite", "TodoRead", "NotebookEdit"] : [];
4783
+ if (mode === "code-review") {
4784
+ modeDisallowed.push("ExitPlanMode", "EnterPlanMode");
4785
+ }
4618
4786
  const configured = settings.disallowedTools ?? [];
4619
4787
  const combined = [...configured, ...modeDisallowed];
4620
4788
  return combined.length > 0 ? combined : void 0;
@@ -4647,11 +4815,11 @@ function buildQueryOptions(host, context) {
4647
4815
  tools: { type: "preset", preset: "claude_code" },
4648
4816
  mcpServers: { conveyor: createConveyorMcpServer(host.connection, host.config, context, mode) },
4649
4817
  hooks: buildHooks(host),
4650
- maxTurns: settings.maxTurns,
4818
+ maxTurns: mode === "code-review" ? Math.min(settings.maxTurns ?? 15, 15) : settings.maxTurns,
4651
4819
  effort: settings.effort,
4652
4820
  thinking: settings.thinking,
4653
4821
  betas: settings.betas,
4654
- maxBudgetUsd: settings.maxBudgetUsd ?? 50,
4822
+ maxBudgetUsd: mode === "code-review" ? Math.min(settings.maxBudgetUsd ?? 10, 10) : settings.maxBudgetUsd ?? 50,
4655
4823
  disallowedTools: buildDisallowedTools(settings, mode, host.hasExitedPlanMode),
4656
4824
  enableFileCheckpointing: settings.enableFileCheckpointing,
4657
4825
  stderr: (data) => {
@@ -5362,6 +5530,7 @@ var AgentRunner = class {
5362
5530
  agentMode = null;
5363
5531
  hasExitedPlanMode = false;
5364
5532
  pendingModeRestart = false;
5533
+ pendingModeAutoStart = false;
5365
5534
  sessionIds = /* @__PURE__ */ new Map();
5366
5535
  lastQueryModeRestart = false;
5367
5536
  startCommandStarted = false;
@@ -5565,6 +5734,12 @@ var AgentRunner = class {
5565
5734
  async executeInitialMode() {
5566
5735
  if (!this.taskContext) return;
5567
5736
  const mode = this.effectiveAgentMode;
5737
+ if (mode === "code-review") {
5738
+ await this.setState("running");
5739
+ await this.runQuerySafe(this.taskContext);
5740
+ this.stopped = true;
5741
+ return;
5742
+ }
5568
5743
  const shouldRun = mode === "building" || mode === "auto" || mode === "review" && !!this.taskContext.isParentTask;
5569
5744
  if (shouldRun) {
5570
5745
  await this.setState("running");
@@ -5621,6 +5796,12 @@ var AgentRunner = class {
5621
5796
  await this.handleModeRestartCycle();
5622
5797
  continue;
5623
5798
  }
5799
+ if (this.pendingModeAutoStart) {
5800
+ this.pendingModeAutoStart = false;
5801
+ this.interrupted = false;
5802
+ await this.handleModeRestartCycle();
5803
+ continue;
5804
+ }
5624
5805
  if (this._state === "idle") {
5625
5806
  const msg = await this.waitForUserContent();
5626
5807
  if (!msg) {
@@ -5772,15 +5953,32 @@ var AgentRunner = class {
5772
5953
  handleModeChange(newAgentMode) {
5773
5954
  if (this.config.mode !== "pm") return;
5774
5955
  if (newAgentMode) this.agentMode = newAgentMode;
5956
+ this.updateExitedPlanModeFlag(newAgentMode);
5775
5957
  const effectiveMode = this.effectiveAgentMode;
5958
+ const isBuildCapable = effectiveMode === "building" || effectiveMode === "auto" && this.hasExitedPlanMode;
5776
5959
  this.connection.emitModeChanged(effectiveMode);
5777
5960
  this.connection.postChatMessage(
5778
5961
  `Mode switched to **${effectiveMode}**${effectiveMode === "building" ? " \u2014 I now have direct coding access." : ""}`
5779
5962
  );
5780
- if (effectiveMode === "building" && this.taskContext?.status === "Open") {
5963
+ if (isBuildCapable && this.taskContext?.status === "Open") {
5781
5964
  this.connection.updateStatus("InProgress");
5782
5965
  this.taskContext.status = "InProgress";
5783
5966
  }
5967
+ if (isBuildCapable) {
5968
+ this.pendingModeAutoStart = true;
5969
+ this.softStop();
5970
+ }
5971
+ }
5972
+ updateExitedPlanModeFlag(newAgentMode) {
5973
+ if (newAgentMode === "building") {
5974
+ this.hasExitedPlanMode = true;
5975
+ return;
5976
+ }
5977
+ if (newAgentMode !== "auto") return;
5978
+ const pastPlanning = this.taskContext?.status !== "Planning" && this.taskContext?.status !== "Unidentified";
5979
+ if (pastPlanning) {
5980
+ this.hasExitedPlanMode = true;
5981
+ }
5784
5982
  }
5785
5983
  softStop() {
5786
5984
  this.interrupted = true;
@@ -5926,17 +6124,17 @@ import {
5926
6124
  } from "@anthropic-ai/claude-agent-sdk";
5927
6125
 
5928
6126
  // src/tools/project-tools.ts
5929
- import { tool as tool7 } from "@anthropic-ai/claude-agent-sdk";
5930
- import { z as z7 } from "zod";
6127
+ import { tool as tool8 } from "@anthropic-ai/claude-agent-sdk";
6128
+ import { z as z8 } from "zod";
5931
6129
  function buildReadTools(connection) {
5932
6130
  return [
5933
- tool7(
6131
+ tool8(
5934
6132
  "list_tasks",
5935
6133
  "List tasks in the project. Optionally filter by status or assignee.",
5936
6134
  {
5937
- status: z7.string().optional().describe("Filter by task status (e.g. Planning, Open, InProgress, ReviewPR, Complete)"),
5938
- assigneeId: z7.string().optional().describe("Filter by assigned user ID"),
5939
- limit: z7.number().optional().describe("Max number of tasks to return (default 50)")
6135
+ status: z8.string().optional().describe("Filter by task status (e.g. Planning, Open, InProgress, ReviewPR, Complete)"),
6136
+ assigneeId: z8.string().optional().describe("Filter by assigned user ID"),
6137
+ limit: z8.number().optional().describe("Max number of tasks to return (default 50)")
5940
6138
  },
5941
6139
  async (params) => {
5942
6140
  try {
@@ -5950,10 +6148,10 @@ function buildReadTools(connection) {
5950
6148
  },
5951
6149
  { annotations: { readOnlyHint: true } }
5952
6150
  ),
5953
- tool7(
6151
+ tool8(
5954
6152
  "get_task",
5955
6153
  "Get detailed information about a task including its chat messages, child tasks, and codespace status.",
5956
- { task_id: z7.string().describe("The task ID to look up") },
6154
+ { task_id: z8.string().describe("The task ID to look up") },
5957
6155
  async ({ task_id }) => {
5958
6156
  try {
5959
6157
  const task = await connection.requestGetTask(task_id);
@@ -5966,14 +6164,14 @@ function buildReadTools(connection) {
5966
6164
  },
5967
6165
  { annotations: { readOnlyHint: true } }
5968
6166
  ),
5969
- tool7(
6167
+ tool8(
5970
6168
  "search_tasks",
5971
6169
  "Search tasks by tags, text query, or status filters.",
5972
6170
  {
5973
- tagNames: z7.array(z7.string()).optional().describe("Filter by tag names"),
5974
- searchQuery: z7.string().optional().describe("Text search in title/description"),
5975
- statusFilters: z7.array(z7.string()).optional().describe("Filter by statuses"),
5976
- limit: z7.number().optional().describe("Max results (default 20)")
6171
+ tagNames: z8.array(z8.string()).optional().describe("Filter by tag names"),
6172
+ searchQuery: z8.string().optional().describe("Text search in title/description"),
6173
+ statusFilters: z8.array(z8.string()).optional().describe("Filter by statuses"),
6174
+ limit: z8.number().optional().describe("Max results (default 20)")
5977
6175
  },
5978
6176
  async (params) => {
5979
6177
  try {
@@ -5987,7 +6185,7 @@ function buildReadTools(connection) {
5987
6185
  },
5988
6186
  { annotations: { readOnlyHint: true } }
5989
6187
  ),
5990
- tool7(
6188
+ tool8(
5991
6189
  "list_tags",
5992
6190
  "List all tags available in the project.",
5993
6191
  {},
@@ -6003,7 +6201,7 @@ function buildReadTools(connection) {
6003
6201
  },
6004
6202
  { annotations: { readOnlyHint: true } }
6005
6203
  ),
6006
- tool7(
6204
+ tool8(
6007
6205
  "get_project_summary",
6008
6206
  "Get a summary of the project including task counts by status and active builds.",
6009
6207
  {},
@@ -6023,15 +6221,15 @@ function buildReadTools(connection) {
6023
6221
  }
6024
6222
  function buildMutationTools(connection) {
6025
6223
  return [
6026
- tool7(
6224
+ tool8(
6027
6225
  "create_task",
6028
6226
  "Create a new task in the project.",
6029
6227
  {
6030
- title: z7.string().describe("Task title"),
6031
- description: z7.string().optional().describe("Task description"),
6032
- plan: z7.string().optional().describe("Implementation plan in markdown"),
6033
- status: z7.string().optional().describe("Initial status (default: Planning)"),
6034
- isBug: z7.boolean().optional().describe("Whether this is a bug report")
6228
+ title: z8.string().describe("Task title"),
6229
+ description: z8.string().optional().describe("Task description"),
6230
+ plan: z8.string().optional().describe("Implementation plan in markdown"),
6231
+ status: z8.string().optional().describe("Initial status (default: Planning)"),
6232
+ isBug: z8.boolean().optional().describe("Whether this is a bug report")
6035
6233
  },
6036
6234
  async (params) => {
6037
6235
  try {
@@ -6044,16 +6242,16 @@ function buildMutationTools(connection) {
6044
6242
  }
6045
6243
  }
6046
6244
  ),
6047
- tool7(
6245
+ tool8(
6048
6246
  "update_task",
6049
6247
  "Update an existing task's title, description, plan, status, or assignee.",
6050
6248
  {
6051
- task_id: z7.string().describe("The task ID to update"),
6052
- title: z7.string().optional().describe("New title"),
6053
- description: z7.string().optional().describe("New description"),
6054
- plan: z7.string().optional().describe("New plan in markdown"),
6055
- status: z7.string().optional().describe("New status"),
6056
- assignedUserId: z7.string().nullable().optional().describe("Assign to user ID, or null to unassign")
6249
+ task_id: z8.string().describe("The task ID to update"),
6250
+ title: z8.string().optional().describe("New title"),
6251
+ description: z8.string().optional().describe("New description"),
6252
+ plan: z8.string().optional().describe("New plan in markdown"),
6253
+ status: z8.string().optional().describe("New status"),
6254
+ assignedUserId: z8.string().nullable().optional().describe("Assign to user ID, or null to unassign")
6057
6255
  },
6058
6256
  async ({ task_id, ...fields }) => {
6059
6257
  try {
@@ -6285,8 +6483,8 @@ import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
6285
6483
 
6286
6484
  // src/tools/audit-tools.ts
6287
6485
  import { randomUUID as randomUUID3 } from "crypto";
6288
- import { tool as tool8, createSdkMcpServer as createSdkMcpServer3 } from "@anthropic-ai/claude-agent-sdk";
6289
- import { z as z8 } from "zod";
6486
+ import { tool as tool9, createSdkMcpServer as createSdkMcpServer3 } from "@anthropic-ai/claude-agent-sdk";
6487
+ import { z as z9 } from "zod";
6290
6488
  function mapCreateTag(input) {
6291
6489
  return {
6292
6490
  type: "create_tag",
@@ -6368,14 +6566,14 @@ function collectRecommendation(toolName, input, collector, onRecommendation) {
6368
6566
  }
6369
6567
  function createAuditMcpServer(collector, onRecommendation) {
6370
6568
  const auditTools = [
6371
- tool8(
6569
+ tool9(
6372
6570
  "recommend_create_tag",
6373
6571
  "Recommend creating a new tag for an uncovered subsystem or area",
6374
6572
  {
6375
- name: z8.string().describe("Proposed tag name (lowercase, hyphenated)"),
6376
- color: z8.string().optional().describe("Hex color code"),
6377
- description: z8.string().describe("What this tag covers"),
6378
- reasoning: z8.string().describe("Why this tag should be created")
6573
+ name: z9.string().describe("Proposed tag name (lowercase, hyphenated)"),
6574
+ color: z9.string().optional().describe("Hex color code"),
6575
+ description: z9.string().describe("What this tag covers"),
6576
+ reasoning: z9.string().describe("Why this tag should be created")
6379
6577
  },
6380
6578
  async (args) => {
6381
6579
  const result = collectRecommendation(
@@ -6387,14 +6585,14 @@ function createAuditMcpServer(collector, onRecommendation) {
6387
6585
  return { content: [{ type: "text", text: result }] };
6388
6586
  }
6389
6587
  ),
6390
- tool8(
6588
+ tool9(
6391
6589
  "recommend_update_description",
6392
6590
  "Recommend updating a tag's description to better reflect its scope",
6393
6591
  {
6394
- tagId: z8.string(),
6395
- tagName: z8.string(),
6396
- description: z8.string().describe("Proposed new description"),
6397
- reasoning: z8.string()
6592
+ tagId: z9.string(),
6593
+ tagName: z9.string(),
6594
+ description: z9.string().describe("Proposed new description"),
6595
+ reasoning: z9.string()
6398
6596
  },
6399
6597
  async (args) => {
6400
6598
  const result = collectRecommendation(
@@ -6406,16 +6604,16 @@ function createAuditMcpServer(collector, onRecommendation) {
6406
6604
  return { content: [{ type: "text", text: result }] };
6407
6605
  }
6408
6606
  ),
6409
- tool8(
6607
+ tool9(
6410
6608
  "recommend_context_link",
6411
6609
  "Recommend linking a doc, rule, file, or folder to a tag's contextPaths",
6412
6610
  {
6413
- tagId: z8.string(),
6414
- tagName: z8.string(),
6415
- linkType: z8.enum(["rule", "doc", "file", "folder"]),
6416
- path: z8.string(),
6417
- label: z8.string().optional(),
6418
- reasoning: z8.string()
6611
+ tagId: z9.string(),
6612
+ tagName: z9.string(),
6613
+ linkType: z9.enum(["rule", "doc", "file", "folder"]),
6614
+ path: z9.string(),
6615
+ label: z9.string().optional(),
6616
+ reasoning: z9.string()
6419
6617
  },
6420
6618
  async (args) => {
6421
6619
  const result = collectRecommendation(
@@ -6427,16 +6625,16 @@ function createAuditMcpServer(collector, onRecommendation) {
6427
6625
  return { content: [{ type: "text", text: result }] };
6428
6626
  }
6429
6627
  ),
6430
- tool8(
6628
+ tool9(
6431
6629
  "flag_documentation_gap",
6432
6630
  "Flag a file that agents read heavily but has no tag documentation linked",
6433
6631
  {
6434
- tagName: z8.string().describe("Tag whose agents read this file"),
6435
- tagId: z8.string().optional(),
6436
- filePath: z8.string(),
6437
- readCount: z8.number(),
6438
- suggestedAction: z8.string().describe("What doc or rule should be created"),
6439
- reasoning: z8.string()
6632
+ tagName: z9.string().describe("Tag whose agents read this file"),
6633
+ tagId: z9.string().optional(),
6634
+ filePath: z9.string(),
6635
+ readCount: z9.number(),
6636
+ suggestedAction: z9.string().describe("What doc or rule should be created"),
6637
+ reasoning: z9.string()
6440
6638
  },
6441
6639
  async (args) => {
6442
6640
  const result = collectRecommendation(
@@ -6448,15 +6646,15 @@ function createAuditMcpServer(collector, onRecommendation) {
6448
6646
  return { content: [{ type: "text", text: result }] };
6449
6647
  }
6450
6648
  ),
6451
- tool8(
6649
+ tool9(
6452
6650
  "recommend_merge_tags",
6453
6651
  "Recommend merging one tag into another",
6454
6652
  {
6455
- tagId: z8.string().describe("Tag ID to be merged (removed after merge)"),
6456
- tagName: z8.string().describe("Name of the tag to be merged"),
6457
- mergeIntoTagId: z8.string().describe("Tag ID to merge into (kept)"),
6458
- mergeIntoTagName: z8.string(),
6459
- reasoning: z8.string()
6653
+ tagId: z9.string().describe("Tag ID to be merged (removed after merge)"),
6654
+ tagName: z9.string().describe("Name of the tag to be merged"),
6655
+ mergeIntoTagId: z9.string().describe("Tag ID to merge into (kept)"),
6656
+ mergeIntoTagName: z9.string(),
6657
+ reasoning: z9.string()
6460
6658
  },
6461
6659
  async (args) => {
6462
6660
  const result = collectRecommendation(
@@ -6468,14 +6666,14 @@ function createAuditMcpServer(collector, onRecommendation) {
6468
6666
  return { content: [{ type: "text", text: result }] };
6469
6667
  }
6470
6668
  ),
6471
- tool8(
6669
+ tool9(
6472
6670
  "recommend_rename_tag",
6473
6671
  "Recommend renaming a tag",
6474
6672
  {
6475
- tagId: z8.string(),
6476
- tagName: z8.string().describe("Current tag name"),
6477
- newName: z8.string().describe("Proposed new name"),
6478
- reasoning: z8.string()
6673
+ tagId: z9.string(),
6674
+ tagName: z9.string().describe("Current tag name"),
6675
+ newName: z9.string().describe("Proposed new name"),
6676
+ reasoning: z9.string()
6479
6677
  },
6480
6678
  async (args) => {
6481
6679
  const result = collectRecommendation(
@@ -6487,10 +6685,10 @@ function createAuditMcpServer(collector, onRecommendation) {
6487
6685
  return { content: [{ type: "text", text: result }] };
6488
6686
  }
6489
6687
  ),
6490
- tool8(
6688
+ tool9(
6491
6689
  "complete_audit",
6492
6690
  "Signal that the audit is complete with a summary of all findings",
6493
- { summary: z8.string().describe("Brief overview of all findings") },
6691
+ { summary: z9.string().describe("Brief overview of all findings") },
6494
6692
  async (args) => {
6495
6693
  collector.complete = true;
6496
6694
  collector.summary = args.summary ?? "Audit completed.";
@@ -6706,21 +6904,22 @@ function setupWorkDir(projectDir, assignment) {
6706
6904
  return { workDir, usesWorktree: shouldWorktree };
6707
6905
  }
6708
6906
  function spawnChildAgent(assignment, workDir) {
6709
- const { taskToken, apiUrl, taskId, mode, isAuto, useSandbox } = assignment;
6907
+ const { taskToken, apiUrl, taskId, mode, isAuto, useSandbox, agentMode } = assignment;
6710
6908
  const cliPath = path.resolve(__dirname, "cli.js");
6711
6909
  const childEnv = { ...process.env };
6712
6910
  delete childEnv.CONVEYOR_PROJECT_TOKEN;
6713
6911
  delete childEnv.CONVEYOR_PROJECT_ID;
6912
+ const effectiveAgentMode = agentMode ?? (isAuto ? "auto" : "");
6714
6913
  const child = fork(cliPath, [], {
6715
6914
  env: {
6716
6915
  ...childEnv,
6717
6916
  CONVEYOR_API_URL: apiUrl,
6718
6917
  CONVEYOR_TASK_TOKEN: taskToken,
6719
6918
  CONVEYOR_TASK_ID: taskId,
6720
- CONVEYOR_MODE: mode,
6919
+ CONVEYOR_MODE: agentMode === "code-review" ? "code-review" : mode,
6721
6920
  CONVEYOR_WORKSPACE: workDir,
6722
6921
  CONVEYOR_USE_WORKTREE: "false",
6723
- CONVEYOR_AGENT_MODE: isAuto ? "auto" : "",
6922
+ CONVEYOR_AGENT_MODE: effectiveAgentMode,
6724
6923
  CONVEYOR_IS_AUTO: isAuto ? "true" : "false",
6725
6924
  CONVEYOR_USE_SANDBOX: useSandbox === true ? "true" : "false",
6726
6925
  CONVEYOR_FROM_PROJECT_RUNNER: "true"
@@ -7155,8 +7354,9 @@ var ProjectRunner = class {
7155
7354
  handleAssignment(assignment) {
7156
7355
  const { taskId, mode } = assignment;
7157
7356
  const shortId = taskId.slice(0, 8);
7158
- if (this.activeAgents.has(taskId)) {
7159
- logger8.info("Task already running, skipping", { taskId: shortId });
7357
+ const agentKey = assignment.agentMode === "code-review" ? `${taskId}:code-review` : taskId;
7358
+ if (this.activeAgents.has(agentKey)) {
7359
+ logger8.info("Task already running, skipping", { taskId: shortId, agentKey });
7160
7360
  return;
7161
7361
  }
7162
7362
  if (this.activeAgents.size >= MAX_CONCURRENT) {
@@ -7175,16 +7375,16 @@ var ProjectRunner = class {
7175
7375
  }
7176
7376
  const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
7177
7377
  const child = spawnChildAgent(assignment, workDir);
7178
- this.activeAgents.set(taskId, {
7378
+ this.activeAgents.set(agentKey, {
7179
7379
  process: child,
7180
7380
  worktreePath: workDir,
7181
7381
  mode,
7182
7382
  usesWorktree
7183
7383
  });
7184
7384
  this.connection.emitTaskStarted(taskId);
7185
- logger8.info("Started task", { taskId: shortId, mode, workDir });
7385
+ logger8.info("Started task", { taskId: shortId, mode, agentKey, workDir });
7186
7386
  child.on("exit", (code) => {
7187
- this.activeAgents.delete(taskId);
7387
+ this.activeAgents.delete(agentKey);
7188
7388
  const reason = code === 0 ? "completed" : `exited with code ${code}`;
7189
7389
  this.connection.emitTaskStopped(taskId, reason);
7190
7390
  logger8.info("Task exited", { taskId: shortId, reason });
@@ -7344,4 +7544,4 @@ export {
7344
7544
  ProjectRunner,
7345
7545
  FileCache
7346
7546
  };
7347
- //# sourceMappingURL=chunk-ANYHEBDY.js.map
7547
+ //# sourceMappingURL=chunk-R6BFXUS7.js.map