@rallycry/conveyor-agent 8.0.1 → 8.2.0

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.
@@ -1233,8 +1233,8 @@ var PlanSync = class {
1233
1233
  for (const file of readdirSync(plansDir).filter((f) => f.endsWith(".md"))) {
1234
1234
  try {
1235
1235
  const fullPath = join(plansDir, file);
1236
- const stat2 = statSync(fullPath);
1237
- this.planFileSnapshot.set(fullPath, stat2.mtimeMs);
1236
+ const stat3 = statSync(fullPath);
1237
+ this.planFileSnapshot.set(fullPath, stat3.mtimeMs);
1238
1238
  } catch {
1239
1239
  continue;
1240
1240
  }
@@ -1255,11 +1255,11 @@ var PlanSync = class {
1255
1255
  for (const file of files) {
1256
1256
  const fullPath = join(plansDir, file);
1257
1257
  try {
1258
- const stat2 = statSync(fullPath);
1258
+ const stat3 = statSync(fullPath);
1259
1259
  const prevMtime = this.planFileSnapshot.get(fullPath);
1260
- const isNew = prevMtime === void 0 || stat2.mtimeMs > prevMtime;
1261
- if (isNew && (!newest || stat2.mtimeMs > newest.mtime)) {
1262
- newest = { path: fullPath, mtime: stat2.mtimeMs };
1260
+ const isNew = prevMtime === void 0 || stat3.mtimeMs > prevMtime;
1261
+ if (isNew && (!newest || stat3.mtimeMs > newest.mtime)) {
1262
+ newest = { path: fullPath, mtime: stat3.mtimeMs };
1263
1263
  }
1264
1264
  } catch {
1265
1265
  continue;
@@ -1304,7 +1304,7 @@ var PlanSync = class {
1304
1304
  import { createHash } from "crypto";
1305
1305
  import { existsSync } from "fs";
1306
1306
  import { homedir } from "os";
1307
- import { join as join2 } from "path";
1307
+ import { join as join3 } from "path";
1308
1308
 
1309
1309
  // ../shared/dist/index.js
1310
1310
  import { z } from "zod";
@@ -1365,7 +1365,21 @@ var GetSuggestionsRequestSchema = z.object({
1365
1365
  status: z.string().optional(),
1366
1366
  limit: z.number().int().min(1).max(100).optional()
1367
1367
  });
1368
+ var ListManualTestsRequestSchema = z.object({
1369
+ sessionId: z.string()
1370
+ });
1368
1371
  var CreatePullRequestRequestSchema = CreatePRInputSchema.extend({ sessionId: z.string() });
1372
+ var RequestFileUploadRequestSchema = z.object({
1373
+ sessionId: z.string(),
1374
+ fileName: z.string().min(1).max(255),
1375
+ mimeType: z.string().min(1).max(128),
1376
+ fileSize: z.number().int().positive().max(MAX_FILE_SIZE_BYTES)
1377
+ });
1378
+ var ConfirmFileUploadRequestSchema = z.object({
1379
+ sessionId: z.string(),
1380
+ fileId: z.string(),
1381
+ title: z.string().max(500).optional()
1382
+ });
1369
1383
  var UpdateTaskStatusRequestSchema = z.object({
1370
1384
  sessionId: z.string(),
1371
1385
  status: z.string(),
@@ -1375,6 +1389,10 @@ var StoreSessionIdRequestSchema = z.object({
1375
1389
  sessionId: z.string(),
1376
1390
  sdkSessionId: z.string()
1377
1391
  });
1392
+ var SetManualTestsRequestSchema = z.object({
1393
+ sessionId: z.string(),
1394
+ items: z.array(z.object({ title: z.string().min(1) })).min(1)
1395
+ });
1378
1396
  var TrackSpendingRequestSchema = z.object({
1379
1397
  sessionId: z.string(),
1380
1398
  inputTokens: z.number().int().nonnegative(),
@@ -1700,7 +1718,10 @@ var ListProjectTasksRequestSchema = z2.object({
1700
1718
  projectId: z2.string(),
1701
1719
  status: z2.string().optional(),
1702
1720
  assigneeId: z2.string().optional(),
1721
+ unassigned: z2.boolean().optional(),
1703
1722
  limit: z2.number().int().positive().optional().default(50)
1723
+ }).refine((p) => !(p.unassigned && p.assigneeId), {
1724
+ message: "Pass either assigneeId or unassigned, not both"
1704
1725
  });
1705
1726
  var GetProjectTaskRequestSchema = z2.object({
1706
1727
  projectId: z2.string(),
@@ -1711,7 +1732,11 @@ var SearchProjectTasksRequestSchema = z2.object({
1711
1732
  tagNames: z2.array(z2.string()).optional(),
1712
1733
  searchQuery: z2.string().optional(),
1713
1734
  statusFilters: z2.array(z2.string()).optional(),
1735
+ assigneeId: z2.string().optional(),
1736
+ unassigned: z2.boolean().optional(),
1714
1737
  limit: z2.number().int().positive().optional().default(20)
1738
+ }).refine((p) => !(p.unassigned && p.assigneeId), {
1739
+ message: "Pass either assigneeId or unassigned, not both"
1715
1740
  });
1716
1741
  var ListProjectTagsRequestSchema = z2.object({
1717
1742
  projectId: z2.string()
@@ -1901,6 +1926,21 @@ var CreateProjectPullRequestRequestSchema = z2.object({
1901
1926
  base: z2.string().optional(),
1902
1927
  requestingUserId: z2.string().optional()
1903
1928
  });
1929
+ var ListProjectMembersRequestSchema = z2.object({
1930
+ projectId: z2.string()
1931
+ });
1932
+ var AddProjectTaskReviewerRequestSchema = z2.object({
1933
+ projectId: z2.string(),
1934
+ taskId: z2.string(),
1935
+ userId: z2.string(),
1936
+ requestingUserId: z2.string().optional()
1937
+ });
1938
+ var RemoveProjectTaskReviewerRequestSchema = z2.object({
1939
+ projectId: z2.string(),
1940
+ taskId: z2.string(),
1941
+ userId: z2.string(),
1942
+ requestingUserId: z2.string().optional()
1943
+ });
1904
1944
  var StartTaskAuditRequestSchema = z3.object({
1905
1945
  projectId: z3.string(),
1906
1946
  taskIds: z3.array(z3.string()).min(1)
@@ -3845,26 +3885,150 @@ function buildMutationTools(connection, config) {
3845
3885
  ];
3846
3886
  }
3847
3887
 
3888
+ // src/tools/attachment-tools.ts
3889
+ import { readFile as readFile2, stat as stat2 } from "fs/promises";
3890
+ import { basename, extname, isAbsolute, join as join2 } from "path";
3891
+ import { z as z7 } from "zod";
3892
+ var IMAGE_MIME_BY_EXT = {
3893
+ ".png": "image/png",
3894
+ ".jpg": "image/jpeg",
3895
+ ".jpeg": "image/jpeg",
3896
+ ".gif": "image/gif",
3897
+ ".webp": "image/webp"
3898
+ };
3899
+ function buildUploadAttachmentTool(connection, config) {
3900
+ return defineTool(
3901
+ "upload_attachment",
3902
+ "Upload an image file (e.g. a Playwright screenshot) as a task attachment AND post it to the task chat in one step \u2014 no follow-up post_to_chat call needed. Supports png/jpg/gif/webp.",
3903
+ {
3904
+ path: z7.string().describe("Path to the image file \u2014 absolute, or relative to the workspace root"),
3905
+ title: z7.string().optional().describe("Short caption posted with the image (defaults to the file name)")
3906
+ },
3907
+ async ({ path: path2, title }) => {
3908
+ try {
3909
+ const filePath = isAbsolute(path2) ? path2 : join2(config.workspaceDir, path2);
3910
+ const mimeType = IMAGE_MIME_BY_EXT[extname(filePath).toLowerCase()];
3911
+ if (!mimeType) {
3912
+ return textResult(
3913
+ `Unsupported file type "${extname(filePath) || "(none)"}". Supported: ${Object.keys(IMAGE_MIME_BY_EXT).join(", ")}`
3914
+ );
3915
+ }
3916
+ const info = await stat2(filePath).catch(() => null);
3917
+ if (!info?.isFile()) {
3918
+ return textResult(`File not found: ${filePath}`);
3919
+ }
3920
+ if (info.size > MAX_FILE_SIZE_BYTES) {
3921
+ return textResult(
3922
+ `File is ${info.size} bytes \u2014 exceeds the ${MAX_FILE_SIZE_BYTES} byte upload limit. Resize or crop the image first.`
3923
+ );
3924
+ }
3925
+ const fileName = basename(filePath);
3926
+ const { fileId, uploadUrl } = await connection.call("requestFileUpload", {
3927
+ sessionId: connection.sessionId,
3928
+ fileName,
3929
+ mimeType,
3930
+ fileSize: info.size
3931
+ });
3932
+ const body = await readFile2(filePath);
3933
+ const res = await fetch(uploadUrl, {
3934
+ method: "PUT",
3935
+ headers: { "Content-Type": mimeType },
3936
+ body
3937
+ });
3938
+ if (!res.ok) {
3939
+ return textResult(
3940
+ `Upload to storage failed: HTTP ${res.status} ${await res.text().catch(() => "")}`
3941
+ );
3942
+ }
3943
+ const result = await connection.call("confirmFileUpload", {
3944
+ sessionId: connection.sessionId,
3945
+ fileId,
3946
+ title
3947
+ });
3948
+ return textResult(
3949
+ `Uploaded ${fileName} (${info.size} bytes) and posted it to the task chat${title ? ` with caption "${title}"` : ""}. File ID: ${result.fileId}`
3950
+ );
3951
+ } catch (error) {
3952
+ return textResult(
3953
+ `Failed to upload attachment: ${error instanceof Error ? error.message : "Unknown error"}`
3954
+ );
3955
+ }
3956
+ }
3957
+ );
3958
+ }
3959
+
3960
+ // src/tools/checklist-tools.ts
3961
+ import { z as z8 } from "zod";
3962
+ function buildListManualTestsTool(connection) {
3963
+ return defineTool(
3964
+ "list_manual_tests",
3965
+ "List the manual test checklist items for the current task. Use to see what manual verification steps have already been recorded.",
3966
+ {},
3967
+ async () => {
3968
+ try {
3969
+ const items = await connection.call("listManualTests", {
3970
+ sessionId: connection.sessionId
3971
+ });
3972
+ if (items.length === 0) return textResult("No manual tests recorded for this task.");
3973
+ const lines = items.map((item, i) => {
3974
+ const checked = item.checked ? "[x]" : "[ ]";
3975
+ return `${i + 1}. ${checked} ${item.title}`;
3976
+ });
3977
+ return textResult(lines.join("\n"));
3978
+ } catch {
3979
+ return textResult("Failed to list manual tests.");
3980
+ }
3981
+ },
3982
+ { annotations: { readOnlyHint: true } }
3983
+ );
3984
+ }
3985
+ function buildSetManualTestsTool(connection) {
3986
+ return defineTool(
3987
+ "set_manual_tests",
3988
+ "Add manual test steps to the task checklist. Existing items with the same title are automatically skipped (deduplication). Use to record specific manual verification steps that reviewers should follow when testing this PR.",
3989
+ {
3990
+ items: z8.array(z8.object({ title: z8.string().min(1).describe("A concise, actionable test step") })).min(1).describe("List of manual test steps to add")
3991
+ },
3992
+ async ({ items }) => {
3993
+ try {
3994
+ const result = await connection.call("setManualTests", {
3995
+ sessionId: connection.sessionId,
3996
+ items
3997
+ });
3998
+ const parts = [`Created ${result.created} manual test item(s).`];
3999
+ if (result.skipped > 0) parts.push(`Skipped ${result.skipped} duplicate(s).`);
4000
+ return textResult(parts.join(" "));
4001
+ } catch (error) {
4002
+ const msg = error instanceof Error ? error.message : "Unknown error";
4003
+ return textResult(`Failed to set manual tests: ${msg}`);
4004
+ }
4005
+ }
4006
+ );
4007
+ }
4008
+
3848
4009
  // src/tools/common-tools.ts
3849
4010
  function buildCommonTools(connection, config) {
3850
4011
  return [
3851
4012
  ...buildTaskContextTools(connection),
3852
4013
  buildGetDependenciesTool(connection),
3853
4014
  buildGetSuggestionsTool(connection),
3854
- ...buildMutationTools(connection, config)
4015
+ buildListManualTestsTool(connection),
4016
+ buildSetManualTestsTool(connection),
4017
+ ...buildMutationTools(connection, config),
4018
+ buildUploadAttachmentTool(connection, config)
3855
4019
  ];
3856
4020
  }
3857
4021
 
3858
4022
  // src/tools/pm-tools.ts
3859
- import { z as z7 } from "zod";
4023
+ import { z as z9 } from "zod";
3860
4024
  var SP_DESCRIPTION = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
3861
4025
  function buildUpdateTaskTool(connection) {
3862
4026
  return defineTool(
3863
4027
  "update_task_plan",
3864
4028
  "Save the finalized plan and/or description to the current task. Use in Plan mode after alignment. For children use update_subtask; for title/tags/PR use update_task_properties.",
3865
4029
  {
3866
- plan: z7.string().optional().describe("The task plan in markdown"),
3867
- description: z7.string().optional().describe("Updated task description")
4030
+ plan: z9.string().optional().describe("The task plan in markdown"),
4031
+ description: z9.string().optional().describe("Updated task description")
3868
4032
  },
3869
4033
  async ({ plan, description }) => {
3870
4034
  try {
@@ -3885,11 +4049,11 @@ function buildCreateSubtaskTool(connection) {
3885
4049
  "create_subtask",
3886
4050
  "Create a subtask under the current parent task. Use when breaking a complex parent into smaller pieces during planning. For post-task follow-ups use create_follow_up_task.",
3887
4051
  {
3888
- title: z7.string().describe("Subtask title"),
3889
- description: z7.string().optional().describe("Brief description"),
3890
- plan: z7.string().optional().describe("Implementation plan in markdown"),
3891
- ordinal: z7.number().optional().describe("Step/order number (0-based)"),
3892
- storyPointValue: z7.number().optional().describe(SP_DESCRIPTION)
4052
+ title: z9.string().describe("Subtask title"),
4053
+ description: z9.string().optional().describe("Brief description"),
4054
+ plan: z9.string().optional().describe("Implementation plan in markdown"),
4055
+ ordinal: z9.number().optional().describe("Step/order number (0-based)"),
4056
+ storyPointValue: z9.number().optional().describe(SP_DESCRIPTION)
3893
4057
  },
3894
4058
  async ({ title, description, plan, ordinal, storyPointValue }) => {
3895
4059
  try {
@@ -3915,12 +4079,12 @@ function buildUpdateSubtaskTool(connection) {
3915
4079
  "update_subtask",
3916
4080
  "Update an existing subtask's fields (title, description, plan, ordinal, storyPointValue). Use when refining a child's plan or reordering. For the current task use update_task_plan.",
3917
4081
  {
3918
- subtaskId: z7.string().describe("The subtask ID to update"),
3919
- title: z7.string().optional(),
3920
- description: z7.string().optional(),
3921
- plan: z7.string().optional(),
3922
- ordinal: z7.number().optional(),
3923
- storyPointValue: z7.number().optional().describe(SP_DESCRIPTION)
4082
+ subtaskId: z9.string().describe("The subtask ID to update"),
4083
+ title: z9.string().optional(),
4084
+ description: z9.string().optional(),
4085
+ plan: z9.string().optional(),
4086
+ ordinal: z9.number().optional(),
4087
+ storyPointValue: z9.number().optional().describe(SP_DESCRIPTION)
3924
4088
  },
3925
4089
  async ({ subtaskId, title, description, plan, storyPointValue }) => {
3926
4090
  try {
@@ -3943,7 +4107,7 @@ function buildDeleteSubtaskTool(connection) {
3943
4107
  return defineTool(
3944
4108
  "delete_subtask",
3945
4109
  "Delete a subtask by id. When to use: a subtask was created in error or is no longer needed. Returns: confirmation string.",
3946
- { subtaskId: z7.string().describe("The subtask ID to delete") },
4110
+ { subtaskId: z9.string().describe("The subtask ID to delete") },
3947
4111
  async ({ subtaskId }) => {
3948
4112
  try {
3949
4113
  await connection.call("deleteSubtask", {
@@ -3981,7 +4145,7 @@ function buildPackTools(connection) {
3981
4145
  "start_child_cloud_build",
3982
4146
  "Start a cloud build (codespace) for a child task. Preconditions: child is in Open status, has a story point value, and has an agent assigned.",
3983
4147
  {
3984
- childTaskId: z7.string().describe("The child task ID to start a cloud build for")
4148
+ childTaskId: z9.string().describe("The child task ID to start a cloud build for")
3985
4149
  },
3986
4150
  async ({ childTaskId }) => {
3987
4151
  try {
@@ -4001,7 +4165,7 @@ function buildPackTools(connection) {
4001
4165
  "stop_child_build",
4002
4166
  "Send a graceful stop signal to a running child build's agent. Not a force-kill \u2014 the agent may take a moment to wind down.",
4003
4167
  {
4004
- childTaskId: z7.string().describe("The child task ID whose build should be stopped")
4168
+ childTaskId: z9.string().describe("The child task ID whose build should be stopped")
4005
4169
  },
4006
4170
  async ({ childTaskId }) => {
4007
4171
  try {
@@ -4021,7 +4185,7 @@ function buildPackTools(connection) {
4021
4185
  "approve_and_merge_pr",
4022
4186
  "Approve and merge a child task's PR. Preconditions: child in ReviewPR. Returns { merged }: true = merged (status\u2192ReviewDev); false = automerge queued, wait for ReviewDev.",
4023
4187
  {
4024
- childTaskId: z7.string().describe("The child task ID whose PR should be approved and merged")
4188
+ childTaskId: z9.string().describe("The child task ID whose PR should be approved and merged")
4025
4189
  },
4026
4190
  async ({ childTaskId }) => {
4027
4191
  try {
@@ -4059,7 +4223,7 @@ function buildPmTools(connection, options) {
4059
4223
  }
4060
4224
 
4061
4225
  // src/tools/discovery-tools.ts
4062
- import { z as z8 } from "zod";
4226
+ import { z as z10 } from "zod";
4063
4227
  var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
4064
4228
  function buildDiscoveryTools(connection) {
4065
4229
  return [
@@ -4067,11 +4231,11 @@ function buildDiscoveryTools(connection) {
4067
4231
  "update_task_properties",
4068
4232
  "Set one or more task properties in a single call. All fields are optional \u2014 only include the fields you want to update.",
4069
4233
  {
4070
- title: z8.string().optional().describe("The new task title"),
4071
- storyPointValue: z8.number().optional().describe(SP_DESCRIPTION2),
4072
- tagNames: z8.array(z8.string()).optional().describe("Array of tag names to assign"),
4073
- githubPRUrl: z8.string().url().optional().describe("GitHub pull request URL to link to this task"),
4074
- githubBranch: z8.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')")
4234
+ title: z10.string().optional().describe("The new task title"),
4235
+ storyPointValue: z10.number().optional().describe(SP_DESCRIPTION2),
4236
+ tagNames: z10.array(z10.string()).optional().describe("Array of tag names to assign"),
4237
+ githubPRUrl: z10.string().url().optional().describe("GitHub pull request URL to link to this task"),
4238
+ githubBranch: z10.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')")
4075
4239
  },
4076
4240
  async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch }) => {
4077
4241
  try {
@@ -4102,14 +4266,14 @@ function buildDiscoveryTools(connection) {
4102
4266
  }
4103
4267
 
4104
4268
  // src/tools/code-review-tools.ts
4105
- import { z as z9 } from "zod";
4269
+ import { z as z11 } from "zod";
4106
4270
  function buildCodeReviewTools(connection) {
4107
4271
  return [
4108
4272
  defineTool(
4109
4273
  "approve_code_review",
4110
4274
  "Approve the code review and exit. Use when the diff passes all review criteria. Takes only a summary \u2014 for changes, use request_code_changes with a structured issues[] list.",
4111
4275
  {
4112
- summary: z9.string().describe("Brief summary of what was reviewed and why it looks good")
4276
+ summary: z11.string().describe("Brief summary of what was reviewed and why it looks good")
4113
4277
  },
4114
4278
  async ({ summary }) => {
4115
4279
  const content = `**Code Review: Approved** :white_check_mark:
@@ -4132,15 +4296,15 @@ ${summary}`;
4132
4296
  "request_code_changes",
4133
4297
  "Request changes during code review and exit. Use when substantive issues must be fixed before merge. Each issue: { file, line?, severity: critical|major|minor, description }.",
4134
4298
  {
4135
- issues: z9.array(
4136
- z9.object({
4137
- file: z9.string().describe("File path where the issue was found"),
4138
- line: z9.number().optional().describe("Line number (if applicable)"),
4139
- severity: z9.enum(["critical", "major", "minor"]).describe("Issue severity"),
4140
- description: z9.string().describe("What is wrong and how to fix it")
4299
+ issues: z11.array(
4300
+ z11.object({
4301
+ file: z11.string().describe("File path where the issue was found"),
4302
+ line: z11.number().optional().describe("Line number (if applicable)"),
4303
+ severity: z11.enum(["critical", "major", "minor"]).describe("Issue severity"),
4304
+ description: z11.string().describe("What is wrong and how to fix it")
4141
4305
  })
4142
4306
  ).describe("List of issues found during review"),
4143
- summary: z9.string().describe("Brief overall summary of the review findings")
4307
+ summary: z11.string().describe("Brief overall summary of the review findings")
4144
4308
  },
4145
4309
  async ({ issues, summary }) => {
4146
4310
  const issueLines = issues.map((issue) => {
@@ -4170,10 +4334,10 @@ ${issueLines}`;
4170
4334
  }
4171
4335
 
4172
4336
  // src/tools/debug-tools.ts
4173
- import { z as z12 } from "zod";
4337
+ import { z as z14 } from "zod";
4174
4338
 
4175
4339
  // src/tools/telemetry-tools.ts
4176
- import { z as z10 } from "zod";
4340
+ import { z as z12 } from "zod";
4177
4341
 
4178
4342
  // src/debug/telemetry-injector.ts
4179
4343
  var BUFFER_SIZE = 200;
@@ -4568,12 +4732,12 @@ function buildGetTelemetryTool(manager) {
4568
4732
  "debug_get_telemetry",
4569
4733
  "Query structured telemetry events (HTTP, DB, Socket.IO, errors) captured from the dev server. Returns filtered structured data instead of raw logs.",
4570
4734
  {
4571
- type: z10.enum(["http", "db", "socket", "error"]).optional().describe("Filter by event type"),
4572
- urlPattern: z10.string().optional().describe("Regex pattern to filter HTTP events by URL"),
4573
- minDuration: z10.number().optional().describe("Minimum duration in ms \u2014 only return events slower than this"),
4574
- errorOnly: z10.boolean().optional().describe("Only return error events and HTTP 4xx/5xx responses"),
4575
- since: z10.number().optional().describe("Only return events after this timestamp (ms since epoch)"),
4576
- limit: z10.number().optional().describe("Max events to return (default: 20, from most recent)")
4735
+ type: z12.enum(["http", "db", "socket", "error"]).optional().describe("Filter by event type"),
4736
+ urlPattern: z12.string().optional().describe("Regex pattern to filter HTTP events by URL"),
4737
+ minDuration: z12.number().optional().describe("Minimum duration in ms \u2014 only return events slower than this"),
4738
+ errorOnly: z12.boolean().optional().describe("Only return error events and HTTP 4xx/5xx responses"),
4739
+ since: z12.number().optional().describe("Only return events after this timestamp (ms since epoch)"),
4740
+ limit: z12.number().optional().describe("Max events to return (default: 20, from most recent)")
4577
4741
  },
4578
4742
  async ({ type, urlPattern, minDuration, errorOnly, since, limit }) => {
4579
4743
  const clientOrErr = requireDebugClient(manager);
@@ -4643,7 +4807,7 @@ function buildTelemetryTools(manager) {
4643
4807
  }
4644
4808
 
4645
4809
  // src/tools/client-debug-tools.ts
4646
- import { z as z11 } from "zod";
4810
+ import { z as z13 } from "zod";
4647
4811
  function requirePlaywrightClient(manager) {
4648
4812
  if (!manager.isClientDebugMode()) {
4649
4813
  return "Client debug mode is not active. Use debug_enter_mode with clientSide: true first.";
@@ -4663,11 +4827,11 @@ function buildClientBreakpointTools(manager) {
4663
4827
  "debug_set_client_breakpoint",
4664
4828
  "Set a breakpoint in client-side code running in headless Chromium. V8 resolves source maps automatically \u2014 use original .tsx/.ts file paths.",
4665
4829
  {
4666
- file: z11.string().describe(
4830
+ file: z13.string().describe(
4667
4831
  "Original source file path (e.g., src/components/App.tsx) \u2014 source maps resolve automatically"
4668
4832
  ),
4669
- line: z11.number().describe("Line number (1-based) in the original source file"),
4670
- condition: z11.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
4833
+ line: z13.number().describe("Line number (1-based) in the original source file"),
4834
+ condition: z13.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
4671
4835
  },
4672
4836
  async ({ file, line, condition }) => {
4673
4837
  const clientOrErr = requirePlaywrightClient(manager);
@@ -4689,7 +4853,7 @@ Breakpoint ID: ${breakpointId}${sourceMapNote}`
4689
4853
  "debug_remove_client_breakpoint",
4690
4854
  "Remove a previously set client-side breakpoint by its ID.",
4691
4855
  {
4692
- breakpointId: z11.string().describe("The breakpoint ID returned by debug_set_client_breakpoint")
4856
+ breakpointId: z13.string().describe("The breakpoint ID returned by debug_set_client_breakpoint")
4693
4857
  },
4694
4858
  async ({ breakpointId }) => {
4695
4859
  const clientOrErr = requirePlaywrightClient(manager);
@@ -4769,8 +4933,8 @@ ${JSON.stringify(queuedHits, null, 2)}`
4769
4933
  "debug_evaluate_client",
4770
4934
  "Evaluate a JavaScript expression in the browser context. When paused, runs in the paused frame's scope; otherwise the page's global scope. Side effects execute \u2014 prefer read-only.",
4771
4935
  {
4772
- expression: z11.string().describe("JavaScript expression to evaluate in the browser context"),
4773
- frameIndex: z11.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
4936
+ expression: z13.string().describe("JavaScript expression to evaluate in the browser context"),
4937
+ frameIndex: z13.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
4774
4938
  },
4775
4939
  async ({ expression, frameIndex }) => {
4776
4940
  const clientOrErr = requirePlaywrightClient(manager);
@@ -4843,7 +5007,7 @@ function buildClientInteractionTools(manager) {
4843
5007
  "debug_navigate_client",
4844
5008
  "Navigate the headless browser to a URL. Waits for domcontentloaded (Playwright's default ~30s timeout applies).",
4845
5009
  {
4846
- url: z11.string().describe("URL to navigate to (e.g., http://localhost:3000/dashboard)")
5010
+ url: z13.string().describe("URL to navigate to (e.g., http://localhost:3000/dashboard)")
4847
5011
  },
4848
5012
  async ({ url }) => {
4849
5013
  const clientOrErr = requirePlaywrightClient(manager);
@@ -4860,7 +5024,7 @@ function buildClientInteractionTools(manager) {
4860
5024
  "debug_click_client",
4861
5025
  "Click an element in the headless browser by CSS selector. Playwright auto-waits for visibility/stability/enabled up to 10s \u2014 a miss throws with a failure message.",
4862
5026
  {
4863
- selector: z11.string().describe(
5027
+ selector: z13.string().describe(
4864
5028
  "CSS selector of the element to click (e.g., 'button.submit', '#login-form input[type=submit]')"
4865
5029
  )
4866
5030
  },
@@ -4882,8 +5046,8 @@ function buildClientConsoleTool(manager) {
4882
5046
  "debug_get_client_console",
4883
5047
  "Get console messages captured from the headless browser. Includes console.log, warn, error, etc.",
4884
5048
  {
4885
- level: z11.string().optional().describe("Filter by console level: log, warn, error, info, debug"),
4886
- limit: z11.number().optional().describe("Maximum number of recent messages to return (default: all)")
5049
+ level: z13.string().optional().describe("Filter by console level: log, warn, error, info, debug"),
5050
+ limit: z13.number().optional().describe("Maximum number of recent messages to return (default: all)")
4887
5051
  },
4888
5052
  // oxlint-disable-next-line require-await
4889
5053
  async ({ level, limit }) => {
@@ -4910,8 +5074,8 @@ function buildClientNetworkTool(manager) {
4910
5074
  "debug_get_client_network",
4911
5075
  "Get network requests captured from the headless browser. Shows URLs, methods, status codes, and timing.",
4912
5076
  {
4913
- filter: z11.string().optional().describe("Regex pattern to filter requests by URL"),
4914
- limit: z11.number().optional().describe("Maximum number of recent requests to return (default: all)")
5077
+ filter: z13.string().optional().describe("Regex pattern to filter requests by URL"),
5078
+ limit: z13.number().optional().describe("Maximum number of recent requests to return (default: all)")
4915
5079
  },
4916
5080
  // oxlint-disable-next-line require-await
4917
5081
  async ({ filter, limit }) => {
@@ -4939,7 +5103,7 @@ function buildClientErrorsTool(manager) {
4939
5103
  "debug_get_client_errors",
4940
5104
  "Get uncaught errors captured from the headless browser. Includes error messages and source-mapped stack traces.",
4941
5105
  {
4942
- limit: z11.number().optional().describe("Maximum number of recent errors to return (default: all)")
5106
+ limit: z13.number().optional().describe("Maximum number of recent errors to return (default: all)")
4943
5107
  },
4944
5108
  // oxlint-disable-next-line require-await
4945
5109
  async ({ limit }) => {
@@ -5033,12 +5197,12 @@ function buildDebugLifecycleTools(manager) {
5033
5197
  "debug_enter_mode",
5034
5198
  "Activate debug mode. Default: server-only (Node --inspect via CDP). Set clientSide=true (previewUrl required) or both flags for full-stack (adds headless Chromium via Playwright).",
5035
5199
  {
5036
- hypothesis: z12.string().optional().describe("Your hypothesis about the bug \u2014 helps track debugging intent"),
5037
- serverSide: z12.boolean().optional().describe(
5200
+ hypothesis: z14.string().optional().describe("Your hypothesis about the bug \u2014 helps track debugging intent"),
5201
+ serverSide: z14.boolean().optional().describe(
5038
5202
  "Enable server-side Node.js debugging (default: true if clientSide is not set)"
5039
5203
  ),
5040
- clientSide: z12.boolean().optional().describe("Enable client-side browser debugging via headless Chromium + Playwright"),
5041
- previewUrl: z12.string().optional().describe(
5204
+ clientSide: z14.boolean().optional().describe("Enable client-side browser debugging via headless Chromium + Playwright"),
5205
+ previewUrl: z14.string().optional().describe(
5042
5206
  "Preview URL for client-side debugging (e.g., http://localhost:3000). Required when clientSide is true."
5043
5207
  )
5044
5208
  },
@@ -5074,9 +5238,9 @@ function buildBreakpointTools(manager) {
5074
5238
  "debug_set_breakpoint",
5075
5239
  "Set a breakpoint at the specified file and line number. Optionally provide a condition expression that must evaluate to true for the breakpoint to pause execution.",
5076
5240
  {
5077
- file: z12.string().describe("Absolute or relative file path to set the breakpoint in"),
5078
- line: z12.number().describe("Line number (1-based) to set the breakpoint on"),
5079
- condition: z12.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
5241
+ file: z14.string().describe("Absolute or relative file path to set the breakpoint in"),
5242
+ line: z14.number().describe("Line number (1-based) to set the breakpoint on"),
5243
+ condition: z14.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
5080
5244
  },
5081
5245
  async ({ file, line, condition }) => {
5082
5246
  const clientOrErr = requireDebugClient2(manager);
@@ -5098,7 +5262,7 @@ Breakpoint ID: ${breakpointId}`
5098
5262
  "debug_remove_breakpoint",
5099
5263
  "Remove a previously set breakpoint by its ID.",
5100
5264
  {
5101
- breakpointId: z12.string().describe("The breakpoint ID returned by debug_set_breakpoint")
5265
+ breakpointId: z14.string().describe("The breakpoint ID returned by debug_set_breakpoint")
5102
5266
  },
5103
5267
  async ({ breakpointId }) => {
5104
5268
  const clientOrErr = requireDebugClient2(manager);
@@ -5179,8 +5343,8 @@ ${JSON.stringify(queuedHits, null, 2)}`
5179
5343
  "debug_evaluate",
5180
5344
  "Evaluate a JavaScript expression server-side in the Node process. When paused, runs in the frame's scope (frameIndex selects frame). Side effects execute \u2014 prefer read-only.",
5181
5345
  {
5182
- expression: z12.string().describe("The JavaScript expression to evaluate"),
5183
- frameIndex: z12.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
5346
+ expression: z14.string().describe("The JavaScript expression to evaluate"),
5347
+ frameIndex: z14.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
5184
5348
  },
5185
5349
  async ({ expression, frameIndex }) => {
5186
5350
  const clientOrErr = requireDebugClient2(manager);
@@ -5208,12 +5372,12 @@ function buildProbeManagementTools(manager) {
5208
5372
  "debug_add_probe",
5209
5373
  "Add a debug probe at a code location. Captures expression values each time the line executes, without pausing or modifying source. Auto-cleaned on debug exit.",
5210
5374
  {
5211
- file: z12.string().describe("File path to probe"),
5212
- line: z12.number().describe("Line number (1-based) to probe"),
5213
- expressions: z12.array(z12.string()).describe(
5375
+ file: z14.string().describe("File path to probe"),
5376
+ line: z14.number().describe("Line number (1-based) to probe"),
5377
+ expressions: z14.array(z14.string()).describe(
5214
5378
  'JavaScript expressions to capture when the line executes (e.g., ["req.params.id", "user.role"])'
5215
5379
  ),
5216
- label: z12.string().optional().describe("Optional label for this probe (defaults to file:line)")
5380
+ label: z14.string().optional().describe("Optional label for this probe (defaults to file:line)")
5217
5381
  },
5218
5382
  async ({ file, line, expressions, label }) => {
5219
5383
  const clientOrErr = requireDebugClient2(manager);
@@ -5238,7 +5402,7 @@ Trigger the code path, then use debug_get_probe_results to see captured values.`
5238
5402
  "debug_remove_probe",
5239
5403
  "Remove a previously set debug probe by its ID.",
5240
5404
  {
5241
- probeId: z12.string().describe("The probe ID returned by debug_add_probe")
5405
+ probeId: z14.string().describe("The probe ID returned by debug_add_probe")
5242
5406
  },
5243
5407
  async ({ probeId }) => {
5244
5408
  const clientOrErr = requireDebugClient2(manager);
@@ -5278,9 +5442,9 @@ function buildProbeResultTools(manager) {
5278
5442
  "debug_get_probe_results",
5279
5443
  "Fetch captured probe hit data. Filter by label (wins) or probeId. Returns grouped text with per-probe hit count, timestamps, and captured expression values.",
5280
5444
  {
5281
- probeId: z12.string().optional().describe("Filter results by probe ID (resolves to its label)"),
5282
- label: z12.string().optional().describe("Filter results by probe label"),
5283
- limit: z12.number().optional().describe("Maximum number of recent hits to return (default: all)")
5445
+ probeId: z14.string().optional().describe("Filter results by probe ID (resolves to its label)"),
5446
+ label: z14.string().optional().describe("Filter results by probe label"),
5447
+ limit: z14.number().optional().describe("Maximum number of recent hits to return (default: all)")
5284
5448
  },
5285
5449
  async ({ probeId, label, limit }) => {
5286
5450
  const clientOrErr = requireDebugClient2(manager);
@@ -5503,9 +5667,6 @@ async function processAssistantEvent(event, host, turnToolCalls) {
5503
5667
  await host.callbacks.onEvent({ type: "tool_use", tool: block.name, input: inputStr });
5504
5668
  }
5505
5669
  }
5506
- if (turnTextParts.length > 0) {
5507
- host.connection.postChatMessage(turnTextParts.join("\n\n"));
5508
- }
5509
5670
  }
5510
5671
  var API_ERROR_PATTERN = /API Error: (?:[45]\d\d|terminated)/;
5511
5672
  var IMAGE_ERROR_PATTERN = /Could not process image/i;
@@ -6235,7 +6396,7 @@ function taskIdToSessionUuid(taskId) {
6235
6396
  function sessionFileExists(sessionUuid, cwd) {
6236
6397
  try {
6237
6398
  const cwdSlug = cwd.replace(/\//g, "-");
6238
- const sessionFile = join2(homedir(), ".claude", "projects", cwdSlug, `${sessionUuid}.jsonl`);
6399
+ const sessionFile = join3(homedir(), ".claude", "projects", cwdSlug, `${sessionUuid}.jsonl`);
6239
6400
  return existsSync(sessionFile);
6240
6401
  } catch {
6241
6402
  return false;
@@ -6901,7 +7062,7 @@ var QueryBridge = class {
6901
7062
 
6902
7063
  // src/runner/session-runner-helpers.ts
6903
7064
  import { readFileSync as readFileSync2 } from "fs";
6904
- import { dirname, join as join3 } from "path";
7065
+ import { dirname, join as join4 } from "path";
6905
7066
  import { fileURLToPath } from "url";
6906
7067
  function mapChatHistory(messages) {
6907
7068
  if (!messages) return [];
@@ -6930,7 +7091,7 @@ function readAgentVersion() {
6930
7091
  const here = dirname(fileURLToPath(import.meta.url));
6931
7092
  for (const rel of ["../package.json", "../../package.json"]) {
6932
7093
  try {
6933
- const pkg = JSON.parse(readFileSync2(join3(here, rel), "utf-8"));
7094
+ const pkg = JSON.parse(readFileSync2(join4(here, rel), "utf-8"));
6934
7095
  if (pkg.version) return pkg.version;
6935
7096
  } catch {
6936
7097
  }
@@ -7943,13 +8104,13 @@ var CommitWatcher = class {
7943
8104
  // src/runner/worktree.ts
7944
8105
  import { execSync as execSync4 } from "child_process";
7945
8106
  import { existsSync as existsSync2 } from "fs";
7946
- import { join as join4 } from "path";
8107
+ import { join as join5 } from "path";
7947
8108
  var WORKTREE_DIR = ".worktrees";
7948
8109
  function ensureWorktree(projectDir, taskId, branch) {
7949
8110
  if (projectDir.includes(`/${WORKTREE_DIR}/`)) {
7950
8111
  return projectDir;
7951
8112
  }
7952
- const worktreePath = join4(projectDir, WORKTREE_DIR, taskId);
8113
+ const worktreePath = join5(projectDir, WORKTREE_DIR, taskId);
7953
8114
  if (existsSync2(worktreePath)) {
7954
8115
  if (branch) {
7955
8116
  if (hasUncommittedChanges(worktreePath)) {
@@ -7995,7 +8156,7 @@ function detachWorktreeBranch(projectDir, branch) {
7995
8156
  }
7996
8157
  }
7997
8158
  function removeWorktree(projectDir, taskId) {
7998
- const worktreePath = join4(projectDir, WORKTREE_DIR, taskId);
8159
+ const worktreePath = join5(projectDir, WORKTREE_DIR, taskId);
7999
8160
  if (!existsSync2(worktreePath)) return;
8000
8161
  try {
8001
8162
  execSync4(`git worktree remove "${worktreePath}" --force`, {
@@ -8066,10 +8227,10 @@ function runStartCommand(cmd, cwd, onOutput) {
8066
8227
  }
8067
8228
 
8068
8229
  // src/tools/project-tools.ts
8069
- import { z as z14 } from "zod";
8230
+ import { z as z17 } from "zod";
8070
8231
 
8071
8232
  // src/tools/project-action-tools.ts
8072
- import { z as z13 } from "zod";
8233
+ import { z as z15 } from "zod";
8073
8234
  var SP_DESCRIPTION3 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
8074
8235
  function withRequestingUser(payload, getRequestingUserId) {
8075
8236
  const requestingUserId = getRequestingUserId();
@@ -8081,10 +8242,10 @@ function buildCreateTaskTool(connection, getRequestingUserId) {
8081
8242
  "create_task",
8082
8243
  "Create a new task in the project.",
8083
8244
  {
8084
- title: z13.string().describe("Task title"),
8085
- description: z13.string().optional().describe("Task description"),
8086
- plan: z13.string().optional().describe("Implementation plan in markdown"),
8087
- status: z13.string().optional().describe("Initial status (default: Planning)")
8245
+ title: z15.string().describe("Task title"),
8246
+ description: z15.string().optional().describe("Task description"),
8247
+ plan: z15.string().optional().describe("Implementation plan in markdown"),
8248
+ status: z15.string().optional().describe("Initial status (default: Planning)")
8088
8249
  },
8089
8250
  async (params) => {
8090
8251
  try {
@@ -8105,12 +8266,12 @@ function buildUpdateProjectTaskTool(connection, getRequestingUserId) {
8105
8266
  "update_project_task",
8106
8267
  "Update an existing task's title, description, plan, status, or assignee. Project-runner scope.",
8107
8268
  {
8108
- task_id: z13.string().describe("The task ID to update"),
8109
- title: z13.string().optional().describe("New title"),
8110
- description: z13.string().optional().describe("New description"),
8111
- plan: z13.string().optional().describe("New plan in markdown"),
8112
- status: z13.string().optional().describe("New status"),
8113
- assignedUserId: z13.string().nullable().optional().describe("Assign to user ID, or null to unassign")
8269
+ task_id: z15.string().describe("The task ID to update"),
8270
+ title: z15.string().optional().describe("New title"),
8271
+ description: z15.string().optional().describe("New description"),
8272
+ plan: z15.string().optional().describe("New plan in markdown"),
8273
+ status: z15.string().optional().describe("New status"),
8274
+ assignedUserId: z15.string().nullable().optional().describe("Assign to user ID, or null to unassign")
8114
8275
  },
8115
8276
  async ({ task_id, ...fields }) => {
8116
8277
  try {
@@ -8131,9 +8292,9 @@ function buildUpdateTaskPlanTool(connection, getRequestingUserId) {
8131
8292
  "update_task_plan",
8132
8293
  "Save a plan or description to a task. Convenience wrapper around update_project_task for the common Plan-mode case.",
8133
8294
  {
8134
- task_id: z13.string().describe("The task ID to update"),
8135
- plan: z13.string().optional().describe("The task plan in markdown"),
8136
- description: z13.string().optional().describe("Updated task description")
8295
+ task_id: z15.string().describe("The task ID to update"),
8296
+ plan: z15.string().optional().describe("The task plan in markdown"),
8297
+ description: z15.string().optional().describe("Updated task description")
8137
8298
  },
8138
8299
  async ({ task_id, plan, description }) => {
8139
8300
  try {
@@ -8162,8 +8323,8 @@ function buildForceUpdateStatusTool(connection, getRequestingUserId) {
8162
8323
  "force_update_task_status",
8163
8324
  "EMERGENCY ONLY: force-override a task's Kanban status. Use when an automatic transition failed and the task is wedged. Normal flow transitions status automatically.",
8164
8325
  {
8165
- task_id: z13.string().describe("The task ID to update"),
8166
- status: z13.enum(["Planning", "Open", "InProgress", "ReviewPR", "ReviewDev", "ReviewLive", "Complete"]).describe("The new status for the task")
8326
+ task_id: z15.string().describe("The task ID to update"),
8327
+ status: z15.enum(["Planning", "Open", "InProgress", "ReviewPR", "ReviewDev", "ReviewLive", "Complete"]).describe("The new status for the task")
8167
8328
  },
8168
8329
  async ({ task_id, status }) => {
8169
8330
  try {
@@ -8178,18 +8339,70 @@ function buildForceUpdateStatusTool(connection, getRequestingUserId) {
8178
8339
  }
8179
8340
  );
8180
8341
  }
8342
+ function formatReviewerList(reviewers) {
8343
+ if (reviewers.length === 0) return "none";
8344
+ return reviewers.map((r) => `${r.name ?? "unknown"} (${r.userId})`).join(", ");
8345
+ }
8346
+ function buildAddReviewerTool(connection, getRequestingUserId) {
8347
+ const projectId = connection.projectId;
8348
+ return defineTool(
8349
+ "add_reviewer",
8350
+ "Add a project member as a reviewer on a task. Idempotent \u2014 adding an existing reviewer is a no-op.",
8351
+ {
8352
+ task_id: z15.string().describe("The task ID or slug"),
8353
+ user_id: z15.string().describe("User ID of the reviewer (use list_project_members to resolve a name or email)")
8354
+ },
8355
+ async ({ task_id, user_id }) => {
8356
+ try {
8357
+ const result = await connection.call(
8358
+ "addProjectTaskReviewer",
8359
+ withRequestingUser({ projectId, taskId: task_id, userId: user_id }, getRequestingUserId)
8360
+ );
8361
+ return textResult(
8362
+ `Reviewer added to task ${result.taskId}. Reviewers: ${formatReviewerList(result.reviewers)}`
8363
+ );
8364
+ } catch (error) {
8365
+ return textResult(`Failed to add reviewer: ${errorMessage(error)}`);
8366
+ }
8367
+ }
8368
+ );
8369
+ }
8370
+ function buildRemoveReviewerTool(connection, getRequestingUserId) {
8371
+ const projectId = connection.projectId;
8372
+ return defineTool(
8373
+ "remove_reviewer",
8374
+ "Remove a reviewer from a task. Idempotent \u2014 removing a non-reviewer is a no-op.",
8375
+ {
8376
+ task_id: z15.string().describe("The task ID or slug"),
8377
+ user_id: z15.string().describe("User ID of the reviewer to remove")
8378
+ },
8379
+ async ({ task_id, user_id }) => {
8380
+ try {
8381
+ const result = await connection.call(
8382
+ "removeProjectTaskReviewer",
8383
+ withRequestingUser({ projectId, taskId: task_id, userId: user_id }, getRequestingUserId)
8384
+ );
8385
+ return textResult(
8386
+ `Reviewer removed from task ${result.taskId}. Reviewers: ${formatReviewerList(result.reviewers)}`
8387
+ );
8388
+ } catch (error) {
8389
+ return textResult(`Failed to remove reviewer: ${errorMessage(error)}`);
8390
+ }
8391
+ }
8392
+ );
8393
+ }
8181
8394
  function buildCreateSubtaskTool2(connection, getRequestingUserId) {
8182
8395
  const projectId = connection.projectId;
8183
8396
  return defineTool(
8184
8397
  "create_subtask",
8185
8398
  "Create a subtask under a parent task in this project. Use for breaking parent tasks into smaller pieces during planning.",
8186
8399
  {
8187
- parent_task_id: z13.string().describe("The parent task ID"),
8188
- title: z13.string().describe("Subtask title"),
8189
- description: z13.string().optional().describe("Brief description"),
8190
- plan: z13.string().optional().describe("Implementation plan in markdown"),
8191
- ordinal: z13.number().optional().describe("Step/order number (0-based)"),
8192
- storyPointValue: z13.number().optional().describe(SP_DESCRIPTION3)
8400
+ parent_task_id: z15.string().describe("The parent task ID"),
8401
+ title: z15.string().describe("Subtask title"),
8402
+ description: z15.string().optional().describe("Brief description"),
8403
+ plan: z15.string().optional().describe("Implementation plan in markdown"),
8404
+ ordinal: z15.number().optional().describe("Step/order number (0-based)"),
8405
+ storyPointValue: z15.number().optional().describe(SP_DESCRIPTION3)
8193
8406
  },
8194
8407
  async ({ parent_task_id, title, description, plan, ordinal, storyPointValue }) => {
8195
8408
  try {
@@ -8221,13 +8434,13 @@ function buildUpdateSubtaskTool2(connection, getRequestingUserId) {
8221
8434
  "update_subtask",
8222
8435
  "Update an existing subtask's fields (title, description, plan, status, ordinal, storyPointValue).",
8223
8436
  {
8224
- subtask_id: z13.string().describe("The subtask ID to update"),
8225
- title: z13.string().optional(),
8226
- description: z13.string().optional(),
8227
- plan: z13.string().optional(),
8228
- status: z13.string().optional(),
8229
- ordinal: z13.number().optional(),
8230
- storyPointValue: z13.number().optional().describe(SP_DESCRIPTION3)
8437
+ subtask_id: z15.string().describe("The subtask ID to update"),
8438
+ title: z15.string().optional(),
8439
+ description: z15.string().optional(),
8440
+ plan: z15.string().optional(),
8441
+ status: z15.string().optional(),
8442
+ ordinal: z15.number().optional(),
8443
+ storyPointValue: z15.number().optional().describe(SP_DESCRIPTION3)
8231
8444
  },
8232
8445
  async ({ subtask_id, ...fields }) => {
8233
8446
  try {
@@ -8247,7 +8460,7 @@ function buildDeleteSubtaskTool2(connection, getRequestingUserId) {
8247
8460
  return defineTool(
8248
8461
  "delete_subtask",
8249
8462
  "Delete a subtask by id. Use when a subtask was created in error or is no longer needed.",
8250
- { subtask_id: z13.string().describe("The subtask ID to delete") },
8463
+ { subtask_id: z15.string().describe("The subtask ID to delete") },
8251
8464
  async ({ subtask_id }) => {
8252
8465
  try {
8253
8466
  await connection.call(
@@ -8266,7 +8479,7 @@ function buildStartTaskTool(connection, getRequestingUserId) {
8266
8479
  return defineTool(
8267
8480
  "start_task",
8268
8481
  "Start a cloud build (codespace) for a task. Preconditions: task has a story point value and an agent assigned.",
8269
- { task_id: z13.string().describe("The task ID to start a build for") },
8482
+ { task_id: z15.string().describe("The task ID to start a build for") },
8270
8483
  async ({ task_id }) => {
8271
8484
  try {
8272
8485
  const result = await connection.call(
@@ -8287,7 +8500,7 @@ function buildStopTaskTool(connection, getRequestingUserId) {
8287
8500
  return defineTool(
8288
8501
  "stop_task",
8289
8502
  "Send a stop signal to a running task's cloud build. Not a force-kill \u2014 the agent may take a moment to wind down.",
8290
- { task_id: z13.string().describe("The task ID whose build should be stopped") },
8503
+ { task_id: z15.string().describe("The task ID whose build should be stopped") },
8291
8504
  async ({ task_id }) => {
8292
8505
  try {
8293
8506
  await connection.call(
@@ -8306,7 +8519,7 @@ function buildMergePRTool(connection, getRequestingUserId) {
8306
8519
  return defineTool(
8307
8520
  "merge_pr",
8308
8521
  "Approve and merge a task's PR. Preconditions: task in ReviewPR with an open PR. Returns { merged }: false means automerge queued \u2014 wait for status to flip to ReviewDev.",
8309
- { task_id: z13.string().describe("The task ID whose PR should be approved and merged") },
8522
+ { task_id: z15.string().describe("The task ID whose PR should be approved and merged") },
8310
8523
  async ({ task_id }) => {
8311
8524
  try {
8312
8525
  const result = await connection.call(
@@ -8327,15 +8540,34 @@ function buildMergePRTool(connection, getRequestingUserId) {
8327
8540
  }
8328
8541
  );
8329
8542
  }
8543
+ function buildProjectActionTools(connection, getRequestingUserId = () => void 0) {
8544
+ return [
8545
+ buildCreateTaskTool(connection, getRequestingUserId),
8546
+ buildUpdateProjectTaskTool(connection, getRequestingUserId),
8547
+ buildUpdateTaskPlanTool(connection, getRequestingUserId),
8548
+ buildForceUpdateStatusTool(connection, getRequestingUserId),
8549
+ buildAddReviewerTool(connection, getRequestingUserId),
8550
+ buildRemoveReviewerTool(connection, getRequestingUserId),
8551
+ buildCreateSubtaskTool2(connection, getRequestingUserId),
8552
+ buildUpdateSubtaskTool2(connection, getRequestingUserId),
8553
+ buildDeleteSubtaskTool2(connection, getRequestingUserId),
8554
+ buildStartTaskTool(connection, getRequestingUserId),
8555
+ buildStopTaskTool(connection, getRequestingUserId),
8556
+ buildMergePRTool(connection, getRequestingUserId)
8557
+ ];
8558
+ }
8559
+
8560
+ // src/tools/project-suggestion-dependency-tools.ts
8561
+ import { z as z16 } from "zod";
8330
8562
  function buildCreateSuggestionTool2(connection, getRequestingUserId) {
8331
8563
  const projectId = connection.projectId;
8332
8564
  return defineTool(
8333
8565
  "create_suggestion",
8334
8566
  "Suggest a feature, improvement, rule, or idea for the project. Duplicates are deduped and your upvote is recorded.",
8335
8567
  {
8336
- title: z13.string().describe("Short title for the suggestion"),
8337
- description: z13.string().optional().describe("1-2 sentence description of what should change and why."),
8338
- tag_names: z13.array(z13.string()).optional().describe("Tag names to categorize the suggestion")
8568
+ title: z16.string().describe("Short title for the suggestion"),
8569
+ description: z16.string().optional().describe("1-2 sentence description of what should change and why."),
8570
+ tag_names: z16.array(z16.string()).optional().describe("Tag names to categorize the suggestion")
8339
8571
  },
8340
8572
  async ({ title, description, tag_names }) => {
8341
8573
  try {
@@ -8369,8 +8601,8 @@ function buildVoteSuggestionTool2(connection, getRequestingUserId) {
8369
8601
  "vote_suggestion",
8370
8602
  "Vote +1 or -1 on a project suggestion.",
8371
8603
  {
8372
- suggestion_id: z13.string().describe("The suggestion ID to vote on"),
8373
- value: z13.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
8604
+ suggestion_id: z16.string().describe("The suggestion ID to vote on"),
8605
+ value: z16.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
8374
8606
  },
8375
8607
  async ({ suggestion_id, value }) => {
8376
8608
  try {
@@ -8398,8 +8630,8 @@ function buildAddDependencyTool2(connection, getRequestingUserId) {
8398
8630
  "add_dependency",
8399
8631
  "Add a blocking dependency \u2014 a task cannot start until the named task is merged to dev.",
8400
8632
  {
8401
- task_id: z13.string().describe("The task that should be blocked"),
8402
- depends_on_slug_or_id: z13.string().describe("Slug or ID of the task this task depends on")
8633
+ task_id: z16.string().describe("The task that should be blocked"),
8634
+ depends_on_slug_or_id: z16.string().describe("Slug or ID of the task this task depends on")
8403
8635
  },
8404
8636
  async ({ task_id, depends_on_slug_or_id }) => {
8405
8637
  try {
@@ -8429,8 +8661,8 @@ function buildRemoveDependencyTool2(connection, getRequestingUserId) {
8429
8661
  "remove_dependency",
8430
8662
  "Remove a previously added dependency from a task.",
8431
8663
  {
8432
- task_id: z13.string().describe("The task to update"),
8433
- depends_on_slug_or_id: z13.string().describe("Slug or ID of the task to remove as dependency")
8664
+ task_id: z16.string().describe("The task to update"),
8665
+ depends_on_slug_or_id: z16.string().describe("Slug or ID of the task to remove as dependency")
8434
8666
  },
8435
8667
  async ({ task_id, depends_on_slug_or_id }) => {
8436
8668
  try {
@@ -8452,18 +8684,8 @@ function buildRemoveDependencyTool2(connection, getRequestingUserId) {
8452
8684
  }
8453
8685
  );
8454
8686
  }
8455
- function buildProjectActionTools(connection, getRequestingUserId = () => void 0) {
8687
+ function buildProjectSuggestionDependencyTools(connection, getRequestingUserId = () => void 0) {
8456
8688
  return [
8457
- buildCreateTaskTool(connection, getRequestingUserId),
8458
- buildUpdateProjectTaskTool(connection, getRequestingUserId),
8459
- buildUpdateTaskPlanTool(connection, getRequestingUserId),
8460
- buildForceUpdateStatusTool(connection, getRequestingUserId),
8461
- buildCreateSubtaskTool2(connection, getRequestingUserId),
8462
- buildUpdateSubtaskTool2(connection, getRequestingUserId),
8463
- buildDeleteSubtaskTool2(connection, getRequestingUserId),
8464
- buildStartTaskTool(connection, getRequestingUserId),
8465
- buildStopTaskTool(connection, getRequestingUserId),
8466
- buildMergePRTool(connection, getRequestingUserId),
8467
8689
  buildCreateSuggestionTool2(connection, getRequestingUserId),
8468
8690
  buildVoteSuggestionTool2(connection, getRequestingUserId),
8469
8691
  buildAddDependencyTool2(connection, getRequestingUserId),
@@ -8479,11 +8701,12 @@ function buildListTasksTool(connection) {
8479
8701
  const projectId = connection.projectId;
8480
8702
  return defineTool(
8481
8703
  "list_tasks",
8482
- "List tasks in the project. Optionally filter by status or assignee.",
8704
+ "List tasks in the project. Optionally filter by status or assignment (a specific assignee, or unassigned tasks).",
8483
8705
  {
8484
- status: z14.string().optional().describe("Filter by task status"),
8485
- assigneeId: z14.string().optional().describe("Filter by assigned user ID"),
8486
- limit: z14.number().optional().describe("Max number of tasks to return (default 50)")
8706
+ status: z17.string().optional().describe("Filter by task status"),
8707
+ assigneeId: z17.string().optional().describe("Filter by assigned user ID"),
8708
+ unassigned: z17.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
8709
+ limit: z17.number().optional().describe("Max number of tasks to return (default 50)")
8487
8710
  },
8488
8711
  async (params) => {
8489
8712
  try {
@@ -8501,7 +8724,7 @@ function buildGetProjectTaskTool(connection) {
8501
8724
  return defineTool(
8502
8725
  "get_project_task",
8503
8726
  "Get detailed information about a task in this project (chat messages, child tasks, session). Project-runner scope.",
8504
- { task_id: z14.string().describe("The task ID to look up") },
8727
+ { task_id: z17.string().describe("The task ID to look up") },
8505
8728
  async ({ task_id }) => {
8506
8729
  try {
8507
8730
  const task = await connection.call("getProjectTask", { projectId, taskId: task_id });
@@ -8517,12 +8740,14 @@ function buildSearchTasksTool(connection) {
8517
8740
  const projectId = connection.projectId;
8518
8741
  return defineTool(
8519
8742
  "search_tasks",
8520
- "Search tasks by tags, text query, or status filters.",
8743
+ "Search tasks by tags, text query, status filters, or assignment.",
8521
8744
  {
8522
- tagNames: z14.array(z14.string()).optional().describe("Filter by tag names"),
8523
- searchQuery: z14.string().optional().describe("Text search in title/description"),
8524
- statusFilters: z14.array(z14.string()).optional().describe("Filter by statuses"),
8525
- limit: z14.number().optional().describe("Max results (default 20)")
8745
+ tagNames: z17.array(z17.string()).optional().describe("Filter by tag names"),
8746
+ searchQuery: z17.string().optional().describe("Text search in title/description"),
8747
+ statusFilters: z17.array(z17.string()).optional().describe("Filter by statuses"),
8748
+ assigneeId: z17.string().optional().describe("Filter by assigned user ID"),
8749
+ unassigned: z17.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
8750
+ limit: z17.number().optional().describe("Max results (default 20)")
8526
8751
  },
8527
8752
  async (params) => {
8528
8753
  try {
@@ -8540,7 +8765,7 @@ function buildListSubtasksTool2(connection) {
8540
8765
  return defineTool(
8541
8766
  "list_subtasks",
8542
8767
  "List the immediate child tasks under a parent task. Use to coordinate subtask work \u2014 see status, ordering, and PR state.",
8543
- { task_id: z14.string().describe("Parent task ID") },
8768
+ { task_id: z17.string().describe("Parent task ID") },
8544
8769
  async ({ task_id }) => {
8545
8770
  try {
8546
8771
  const subtasks = await connection.call("listProjectSubtasks", {
@@ -8572,6 +8797,23 @@ function buildListTagsTool(connection) {
8572
8797
  { annotations: { readOnlyHint: true } }
8573
8798
  );
8574
8799
  }
8800
+ function buildListProjectMembersTool(connection) {
8801
+ const projectId = connection.projectId;
8802
+ return defineTool(
8803
+ "list_project_members",
8804
+ "List project members with user ID, name, email, and access level. Use to resolve a person's name or email to a user ID for task assignment or review.",
8805
+ {},
8806
+ async () => {
8807
+ try {
8808
+ const members = await connection.call("listProjectMembers", { projectId });
8809
+ return textResult(JSON.stringify(members, null, 2));
8810
+ } catch (error) {
8811
+ return textResult(`Failed to list project members: ${errorMessage(error)}`);
8812
+ }
8813
+ },
8814
+ { annotations: { readOnlyHint: true } }
8815
+ );
8816
+ }
8575
8817
  function buildGetProjectSummaryTool(connection) {
8576
8818
  const projectId = connection.projectId;
8577
8819
  return defineTool(
@@ -8595,8 +8837,8 @@ function buildPostToChatTool2(connection, getRequestingUserId) {
8595
8837
  "post_to_chat",
8596
8838
  "Post an out-of-band message into a chat. Omit task_id to post into the project chat; pass task_id to post into a specific task's chat. Normal replies already appear in chat automatically.",
8597
8839
  {
8598
- message: z14.string().describe("The message content to post"),
8599
- task_id: z14.string().optional().describe("Task ID to post into a specific task's chat. Omit for the project chat.")
8840
+ message: z17.string().describe("The message content to post"),
8841
+ task_id: z17.string().optional().describe("Task ID to post into a specific task's chat. Omit for the project chat.")
8600
8842
  },
8601
8843
  async ({ message, task_id }) => {
8602
8844
  try {
@@ -8624,8 +8866,8 @@ function buildReadChatHistoryTool(connection) {
8624
8866
  "read_chat_history",
8625
8867
  "Read recent messages from a chat. Omit chat_id to read the project chat; pass a chat_id to read a specific chat.",
8626
8868
  {
8627
- chat_id: z14.string().optional().describe("Chat ID to read. Omit for the project chat."),
8628
- limit: z14.number().optional().describe("Number of recent messages to fetch (default 50)")
8869
+ chat_id: z17.string().optional().describe("Chat ID to read. Omit for the project chat."),
8870
+ limit: z17.number().optional().describe("Number of recent messages to fetch (default 50)")
8629
8871
  },
8630
8872
  async ({ chat_id, limit }) => {
8631
8873
  try {
@@ -8648,8 +8890,8 @@ function buildReadTaskChatTool2(connection) {
8648
8890
  "read_task_chat",
8649
8891
  "Read recent human/user chat messages for a specific task in this project. For agent execution logs use get_task_logs.",
8650
8892
  {
8651
- task_id: z14.string().describe("The task ID whose chat to read"),
8652
- limit: z14.number().optional().describe("Number of recent messages to fetch (default 20)")
8893
+ task_id: z17.string().describe("The task ID whose chat to read"),
8894
+ limit: z17.number().optional().describe("Number of recent messages to fetch (default 20)")
8653
8895
  },
8654
8896
  async ({ task_id, limit }) => {
8655
8897
  try {
@@ -8672,9 +8914,9 @@ function buildGetTaskLogsTool(connection) {
8672
8914
  "get_task_logs",
8673
8915
  "Read CLI execution logs for a task \u2014 agent reasoning, tool calls, and setup/dev-server output. For human chat use read_task_chat.",
8674
8916
  {
8675
- task_id: z14.string().describe("Task ID to read logs from"),
8676
- source: z14.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
8677
- limit: z14.number().optional().describe("Max number of log entries to return (default 50, max 500).")
8917
+ task_id: z17.string().describe("Task ID to read logs from"),
8918
+ source: z17.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
8919
+ limit: z17.number().optional().describe("Max number of log entries to return (default 50, max 500).")
8678
8920
  },
8679
8921
  async ({ task_id, source, limit }) => {
8680
8922
  try {
@@ -8699,12 +8941,14 @@ function buildProjectTools(connection, getRequestingUserId = () => void 0) {
8699
8941
  buildSearchTasksTool(connection),
8700
8942
  buildListSubtasksTool2(connection),
8701
8943
  buildListTagsTool(connection),
8944
+ buildListProjectMembersTool(connection),
8702
8945
  buildGetProjectSummaryTool(connection),
8703
8946
  buildPostToChatTool2(connection, getRequestingUserId),
8704
8947
  buildReadChatHistoryTool(connection),
8705
8948
  buildReadTaskChatTool2(connection),
8706
8949
  buildGetTaskLogsTool(connection),
8707
- ...buildProjectActionTools(connection, getRequestingUserId)
8950
+ ...buildProjectActionTools(connection, getRequestingUserId),
8951
+ ...buildProjectSuggestionDependencyTools(connection, getRequestingUserId)
8708
8952
  ];
8709
8953
  }
8710
8954
 
@@ -9665,13 +9909,13 @@ var ProjectRunner = class {
9665
9909
  };
9666
9910
 
9667
9911
  // src/setup/config.ts
9668
- import { readFile as readFile2 } from "fs/promises";
9669
- import { join as join5 } from "path";
9912
+ import { readFile as readFile3 } from "fs/promises";
9913
+ import { join as join6 } from "path";
9670
9914
  var DEVCONTAINER_PATH = ".devcontainer/conveyor/devcontainer.json";
9671
9915
  var DEVCONTAINER_PORT_DENY_LIST = /* @__PURE__ */ new Set([5432, 6379, 9200]);
9672
9916
  async function loadForwardPorts(workspaceDir) {
9673
9917
  try {
9674
- const raw = await readFile2(join5(workspaceDir, DEVCONTAINER_PATH), "utf-8");
9918
+ const raw = await readFile3(join6(workspaceDir, DEVCONTAINER_PATH), "utf-8");
9675
9919
  const parsed = JSON.parse(raw);
9676
9920
  const ports = (parsed.forwardPorts ?? []).filter(
9677
9921
  (p) => typeof p === "number" && !DEVCONTAINER_PORT_DENY_LIST.has(p)
@@ -9758,4 +10002,4 @@ export {
9758
10002
  loadConveyorConfig,
9759
10003
  unshallowRepo
9760
10004
  };
9761
- //# sourceMappingURL=chunk-2L7ROQE6.js.map
10005
+ //# sourceMappingURL=chunk-W4NCD23G.js.map