@rallycry/conveyor-mcp 4.3.3 → 4.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConveyorConnection
4
- } from "./chunk-GNC7IHOT.js";
4
+ } from "./chunk-JSQLLIYJ.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { createRequire } from "module";
@@ -42,6 +42,17 @@ function registerProjectTools(server2, conn2) {
42
42
  return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
43
43
  }
44
44
  );
45
+ server2.tool(
46
+ "get_onboarding_step",
47
+ "Drive project setup one contextual step at a time (the choose-your-own-adventure onboarding flow). Returns { done, ok, step, remaining, checks }: `step` is the SINGLE next thing to configure (fails before warnings) with a human `title`, the `reason`, agent-facing `guidance` on how to resolve it, `autoFixable` (true = you can fix it via gh/git/tools, false = a browser-only step the user must do), an optional `fix` hint, and `connectUrls` (URLs to hand the user for browser-only steps). `remaining` lists the other unresolved checks. Prefer this over get_onboarding_status during setup: call it, resolve the returned step (do it yourself if autoFixable, otherwise give the user the connectUrl and wait), then call again \u2014 repeat until `done` is true. The repository and branches are already configured; never change them. Pass projectId to target a specific project; otherwise the configured default project is used.",
48
+ {
49
+ projectId: z.string().optional().describe("Target Conveyor project ID")
50
+ },
51
+ async (params) => {
52
+ const step = await conn2.getOnboardingStep(params.projectId);
53
+ return { content: [{ type: "text", text: JSON.stringify(step, null, 2) }] };
54
+ }
55
+ );
45
56
  server2.tool(
46
57
  "list_project_members",
47
58
  "List project members with user ID, name, email, and access level \u2014 use to resolve a person's name or email to a user ID for task assignment or review. Pass projectId to target a specific project; otherwise the configured default project is used.",
@@ -55,8 +66,158 @@ function registerProjectTools(server2, conn2) {
55
66
  );
56
67
  }
57
68
 
58
- // src/tools/tasks.ts
69
+ // src/tools/project-config.ts
59
70
  import { z as z2 } from "zod";
71
+ function jsonResult(data) {
72
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
73
+ }
74
+ function textResult(text) {
75
+ return { content: [{ type: "text", text }] };
76
+ }
77
+ var AGENT_DEFAULT_KEYS = [
78
+ "defaultPmAgentId",
79
+ "defaultTaskAgentId",
80
+ "defaultReviewerAgentId",
81
+ "helperAgentId"
82
+ ];
83
+ function registerGetConnectUrls(server2, conn2) {
84
+ server2.tool(
85
+ "get_connect_urls",
86
+ "Get browser-only setup URLs to hand to the user: the Google Cloud OAuth connect link (gcpConnect) plus Settings deep links (gcpSettings, memberSettings, projectSettings, setupWizard). Use these for setup steps an agent cannot perform (OAuth grants, secret entry). Pass projectId to target a specific project; otherwise the configured default project is used.",
87
+ {
88
+ projectId: z2.string().optional().describe("Target Conveyor project ID")
89
+ },
90
+ async (params) => jsonResult(await conn2.getConnectUrls(params.projectId))
91
+ );
92
+ }
93
+ function registerUpdateProjectSettings(server2, conn2) {
94
+ server2.tool(
95
+ "update_project_settings",
96
+ "Update project configuration: name, description, default agent assignments, or a shallow-merged patch of the project settings JSON (e.g. compute tier keys). Requires a Moderate project role. Does NOT touch repositories or branches \u2014 those are configured in the Conveyor UI.",
97
+ {
98
+ projectId: z2.string().optional().describe("Target Conveyor project ID"),
99
+ name: z2.string().optional().describe("New project name"),
100
+ description: z2.string().optional().describe("New project description"),
101
+ settings: z2.record(z2.unknown()).optional().describe("Shallow-merged patch of the project settings JSON (advanced)"),
102
+ defaultPmAgentId: z2.string().nullable().optional().describe("Default PM agent ID"),
103
+ defaultTaskAgentId: z2.string().nullable().optional().describe("Default task agent ID"),
104
+ defaultReviewerAgentId: z2.string().nullable().optional().describe("Default reviewer agent ID"),
105
+ helperAgentId: z2.string().nullable().optional().describe("Helper agent ID")
106
+ },
107
+ async (params) => {
108
+ const { projectId: projectId2, name, description, settings, ...agents } = params;
109
+ const results = [];
110
+ const config = {};
111
+ if (name !== void 0) config.name = name;
112
+ if (description !== void 0) config.description = description;
113
+ if (settings !== void 0) config.settings = settings;
114
+ if (Object.keys(config).length > 0) {
115
+ results.push(await conn2.updateProjectConfig({ projectId: projectId2, ...config }));
116
+ }
117
+ const defaults = {};
118
+ for (const key of AGENT_DEFAULT_KEYS) {
119
+ if (agents[key] !== void 0) defaults[key] = agents[key];
120
+ }
121
+ if (Object.keys(defaults).length > 0) {
122
+ results.push(await conn2.updateProjectAgentDefaults({ projectId: projectId2, ...defaults }));
123
+ }
124
+ if (results.length === 0) return textResult("Nothing to update \u2014 pass at least one field.");
125
+ return jsonResult(results.length === 1 ? results[0] : results);
126
+ }
127
+ );
128
+ }
129
+ function registerManageTags(server2, conn2) {
130
+ server2.tool(
131
+ "manage_tags",
132
+ "List, create, update, or delete project tags. create requires name (optional color/description); update/delete require the tag id. Mutations require a Moderate project role.",
133
+ {
134
+ action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
135
+ projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
136
+ id: z2.string().optional().describe("Tag ID (update/delete)"),
137
+ name: z2.string().optional().describe("Tag name"),
138
+ color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
139
+ description: z2.string().optional().describe("Tag description")
140
+ },
141
+ async (params) => {
142
+ const { action, projectId: projectId2, id, name, color, description } = params;
143
+ if (action === "list") return jsonResult(await conn2.listTagsDetailed(projectId2));
144
+ if (action === "create") {
145
+ if (!name) return textResult("name is required for create.");
146
+ return jsonResult(
147
+ await conn2.createTag({
148
+ projectId: projectId2,
149
+ name,
150
+ ...color !== void 0 && { color },
151
+ ...description !== void 0 && { description }
152
+ })
153
+ );
154
+ }
155
+ if (!id) return textResult(`id is required for ${action}.`);
156
+ if (action === "delete") return jsonResult(await conn2.deleteTag(id));
157
+ return jsonResult(
158
+ await conn2.updateTag({
159
+ id,
160
+ ...name !== void 0 && { name },
161
+ ...color !== void 0 && { color },
162
+ ...description !== void 0 && { description }
163
+ })
164
+ );
165
+ }
166
+ );
167
+ }
168
+ function registerManagePriorities(server2, conn2) {
169
+ server2.tool(
170
+ "manage_priorities",
171
+ "List, create, update, or delete project priorities. create requires value (1-100), name, and color; update/delete require the priority id. create/update require a Moderate role; delete requires Admin.",
172
+ {
173
+ action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
174
+ projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
175
+ id: z2.string().optional().describe("Priority ID (update/delete)"),
176
+ value: z2.number().int().min(1).max(100).optional().describe("Priority value (1 = highest urgency scale point)"),
177
+ name: z2.string().optional().describe("Priority name"),
178
+ color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
179
+ description: z2.string().optional().describe("Priority description")
180
+ },
181
+ async (params) => {
182
+ const { action, projectId: projectId2, id, value, name, color, description } = params;
183
+ if (action === "list") return jsonResult(await conn2.listPriorities(projectId2));
184
+ if (action === "create") {
185
+ if (value === void 0 || !name || !color) {
186
+ return textResult("value, name, and color are required for create.");
187
+ }
188
+ return jsonResult(
189
+ await conn2.createPriority({
190
+ projectId: projectId2,
191
+ value,
192
+ name,
193
+ color,
194
+ ...description !== void 0 && { description }
195
+ })
196
+ );
197
+ }
198
+ if (!id) return textResult(`id is required for ${action}.`);
199
+ if (action === "delete") return jsonResult(await conn2.deletePriority(id));
200
+ return jsonResult(
201
+ await conn2.updatePriority({
202
+ id,
203
+ ...value !== void 0 && { value },
204
+ ...name !== void 0 && { name },
205
+ ...color !== void 0 && { color },
206
+ ...description !== void 0 && { description }
207
+ })
208
+ );
209
+ }
210
+ );
211
+ }
212
+ function registerProjectConfigTools(server2, conn2) {
213
+ registerGetConnectUrls(server2, conn2);
214
+ registerUpdateProjectSettings(server2, conn2);
215
+ registerManageTags(server2, conn2);
216
+ registerManagePriorities(server2, conn2);
217
+ }
218
+
219
+ // src/tools/tasks.ts
220
+ import { z as z3 } from "zod";
60
221
  var CLI_EVENT_FORMATTERS = {
61
222
  thinking: (data) => String(data.message ?? ""),
62
223
  tool_use: (data) => `${data.tool}: ${String(data.input ?? "").slice(0, 1e3)}`,
@@ -129,16 +290,19 @@ var RISK_ENUM = ["critical", "high", "medium", "low"];
129
290
  function registerListTasks(server2, conn2) {
130
291
  server2.tool(
131
292
  "list_tasks",
132
- "List project tasks, optionally filtered by status or assignment (a specific assignee, or unassigned tasks). Pass projectId to target a specific project; otherwise the configured default project is used. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.",
293
+ "List project cards, optionally filtered by card type, status, or assignment (a specific assignee, or unassigned tasks). Defaults to type=task \u2014 pass typeFilters to list incidents/suggestions. Results are relevance-ordered: highest priority first then newest; suggestions-only queries rank by upvote score. Pass projectId to target a specific project; otherwise the configured default project is used. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.",
133
294
  {
134
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
135
- status: z2.enum(STATUS_ENUM).optional().describe("Filter by task status"),
136
- assigneeId: z2.string().optional().describe("Filter by assigned user ID"),
137
- unassigned: z2.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
138
- subProjectId: z2.string().optional().describe(
295
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
296
+ status: z3.enum(STATUS_ENUM).optional().describe("Filter by task status"),
297
+ typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
298
+ 'Card types to include, e.g. ["incident"] or ["task", "incident"]. Omit for tasks only.'
299
+ ),
300
+ assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
301
+ unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
302
+ subProjectId: z3.string().optional().describe(
139
303
  "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
140
304
  ),
141
- limit: z2.number().optional().describe("Max tasks to return (default 50)")
305
+ limit: z3.number().optional().describe("Max tasks to return (default 50)")
142
306
  },
143
307
  async (params) => {
144
308
  const tasks = await conn2.listTasks(params);
@@ -153,8 +317,8 @@ function registerGetTask(server2, conn2) {
153
317
  "get_task",
154
318
  "Get full task details including plan, chat history, PR info, subtasks, and build status. Pass projectId to target a specific project; otherwise the configured default project is used.",
155
319
  {
156
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
157
- taskId: z2.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
320
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
321
+ taskId: z3.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
158
322
  },
159
323
  async (params) => {
160
324
  const task = await conn2.getTask(params.taskId, params.projectId);
@@ -167,8 +331,8 @@ function registerGetCardBySlug(server2, conn2) {
167
331
  "get_card_by_slug",
168
332
  "Get full card details by the slug from a card URL (/cards/<slug>) instead of a task ID. Pass projectId to target a specific project; otherwise the configured default project is used.",
169
333
  {
170
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
171
- slug: z2.string().describe("The card slug from a Conveyor card URL, e.g. 'ship-it'")
334
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
335
+ slug: z3.string().describe("The card slug from a Conveyor card URL, e.g. 'ship-it'")
172
336
  },
173
337
  async (params) => {
174
338
  const task = await conn2.getCardBySlug(params.slug, params.projectId);
@@ -181,12 +345,12 @@ function registerCreateTask(server2, conn2) {
181
345
  "create_task",
182
346
  "Create a new task with title, description, and optional plan. Pass projectId to target a specific project; otherwise the configured default project is used. Icon, story points, and agent assignment are auto-filled when a task is created in (or later moved to) a status beyond Planning \u2014 don't spend turns on them.",
183
347
  {
184
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
185
- title: z2.string().describe("Task title"),
186
- description: z2.string().optional().describe("Task description"),
187
- plan: z2.string().optional().describe("Task implementation plan (markdown)"),
188
- status: z2.enum(["Planning", "Open"]).optional().describe("Initial status (default: Planning)"),
189
- subProjectId: z2.string().optional().describe(
348
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
349
+ title: z3.string().describe("Task title"),
350
+ description: z3.string().optional().describe("Task description"),
351
+ plan: z3.string().optional().describe("Task implementation plan (markdown)"),
352
+ status: z3.enum(["Planning", "Open"]).optional().describe("Initial status (default: Planning)"),
353
+ subProjectId: z3.string().optional().describe(
190
354
  "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
191
355
  )
192
356
  },
@@ -201,17 +365,19 @@ function registerCreateTask(server2, conn2) {
201
365
  function registerUpdateTask(server2, conn2) {
202
366
  server2.tool(
203
367
  "update_task",
204
- "Update task fields: title, plan, risk, or assignment. Status and description are NOT editable through MCP \u2014 use approve_task / request_changes for review status moves. Pass projectId to target a specific project; otherwise the configured default project is used.",
368
+ "Update task fields: title, description, plan, status, risk, or assignment. Set status to claim a card (InProgress), triage it (Open), or cancel it (Cancelled); for review approvals prefer approve_task / request_changes, which guard against stale-state races. Pass projectId to target a specific project; otherwise the configured default project is used. Moving a task beyond Planning auto-fills any missing icon, story points, and agent assignment \u2014 don't spend turns on them.",
205
369
  {
206
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
207
- taskId: z2.string().describe("The task ID"),
208
- title: z2.string().optional().describe("New title"),
209
- plan: z2.string().optional().describe("New plan (markdown)"),
210
- risk: z2.enum(RISK_ENUM).nullable().optional().describe(
370
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
371
+ taskId: z3.string().describe("The task ID"),
372
+ title: z3.string().optional().describe("New title"),
373
+ description: z3.string().optional().describe("New description"),
374
+ plan: z3.string().optional().describe("New plan (markdown)"),
375
+ status: z3.enum(STATUS_ENUM).optional().describe("New status"),
376
+ risk: z3.enum(RISK_ENUM).nullable().optional().describe(
211
377
  "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
212
378
  ),
213
- assignedUserId: z2.string().nullable().optional().describe("User ID to assign, or null"),
214
- subProjectId: z2.string().nullable().optional().describe(
379
+ assignedUserId: z3.string().nullable().optional().describe("User ID to assign, or null"),
380
+ subProjectId: z3.string().nullable().optional().describe(
215
381
  "Assign the task to a sub-project board; null moves it back to the parent board. Omit to leave unchanged."
216
382
  )
217
383
  },
@@ -228,9 +394,9 @@ function registerMoveCard(server2, conn2) {
228
394
  "move_card",
229
395
  "Move an eligible Planning or Open task, incident, or suggestion card to another project. Cards with identification or automation in progress, active compute, pull requests, deployments, releases, reviews, or delivered reporter email cannot move. Project-specific metadata is cleared instead of mapped by name. Pass projectId for the source project; otherwise the configured default project is used.",
230
396
  {
231
- projectId: z2.string().optional().describe("Source Conveyor project ID"),
232
- taskId: z2.string().describe("Card ID or slug"),
233
- destinationProjectId: z2.string().describe("Destination Conveyor project ID")
397
+ projectId: z3.string().optional().describe("Source Conveyor project ID"),
398
+ taskId: z3.string().describe("Card ID or slug"),
399
+ destinationProjectId: z3.string().describe("Destination Conveyor project ID")
234
400
  },
235
401
  async (params) => {
236
402
  const result = await conn2.moveCard(params);
@@ -251,9 +417,9 @@ function registerChatTools(server2, conn2) {
251
417
  "read_task_chat",
252
418
  "Read messages from a task's chat. Pass projectId to target a specific project; otherwise the configured default project is used. For agent execution logs use get_task_logs.",
253
419
  {
254
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
255
- taskId: z2.string().describe("The task ID"),
256
- limit: z2.number().optional().describe("Max messages to return (default 50)")
420
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
421
+ taskId: z3.string().describe("The task ID"),
422
+ limit: z3.number().optional().describe("Max messages to return (default 50)")
257
423
  },
258
424
  async (params) => {
259
425
  const messages = await conn2.getTaskChat(params.taskId, params.limit, params.projectId);
@@ -264,9 +430,9 @@ function registerChatTools(server2, conn2) {
264
430
  "post_to_chat",
265
431
  "Post a message to a task's chat. Pass projectId to target a specific project; otherwise the configured default project is used.",
266
432
  {
267
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
268
- taskId: z2.string().describe("The task ID"),
269
- content: z2.string().describe("Message content")
433
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
434
+ taskId: z3.string().describe("The task ID"),
435
+ content: z3.string().describe("Message content")
270
436
  },
271
437
  async (params) => {
272
438
  await conn2.postToTaskChat(params.taskId, params.content, params.projectId);
@@ -279,12 +445,12 @@ function registerGetTaskCli(server2, conn2) {
279
445
  "get_task_logs",
280
446
  "Read CLI execution logs from a task. Pass projectId to target a specific project; otherwise the configured default project is used. Returns agent reasoning, tool calls, setup output, and other execution events. For human chat use read_task_chat.",
281
447
  {
282
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
283
- taskId: z2.string().describe("The task ID or slug"),
284
- source: z2.enum(["agent", "application"]).optional().describe(
448
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
449
+ taskId: z3.string().describe("The task ID or slug"),
450
+ source: z3.enum(["agent", "application"]).optional().describe(
285
451
  "Filter by log source: 'agent' for reasoning/tool calls, 'application' for setup/dev-server output"
286
452
  ),
287
- limit: z2.number().optional().describe("Max entries to return (default 50, max 500)")
453
+ limit: z3.number().optional().describe("Max entries to return (default 50, max 500)")
288
454
  },
289
455
  async ({ taskId, source, limit, projectId: projectId2 }) => {
290
456
  const effectiveLimit = Math.min(limit ?? 50, 500);
@@ -303,8 +469,8 @@ function registerGetTaskSessions(server2, conn2) {
303
469
  "get_task_sessions",
304
470
  "Read compute-session state for a task: legacy CodespaceSession rows plus v3 workspaces, including purpose=review review workspaces. Shows pod identity, liveness, lifecycle, and code-review claim state. Use to diagnose stalled/dead agents or code-review runs. Pass projectId to target a specific project; otherwise the configured default project is used.",
305
471
  {
306
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
307
- taskId: z2.string().describe("The task ID or slug")
472
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
473
+ taskId: z3.string().describe("The task ID or slug")
308
474
  },
309
475
  async ({ taskId, projectId: projectId2 }) => {
310
476
  const tasks = await conn2.getTaskSessions(taskId, projectId2);
@@ -320,21 +486,21 @@ function registerGetTaskSessions(server2, conn2) {
320
486
  function registerSearchTasks(server2, conn2) {
321
487
  server2.tool(
322
488
  "search_tasks",
323
- "Search cards by tag name, text query, status, type, and/or assignment. Defaults to type=task \u2014 pass typeFilters to include incidents/suggestions. Pass projectId to target a specific project; otherwise the configured default project is used. Use tag names like 'agent-runner', not IDs. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.",
489
+ "Search cards by tag name, text query, status, type, and/or assignment. Defaults to type=task \u2014 pass typeFilters to include incidents/suggestions. Results are relevance-ordered: highest priority first then newest; suggestions-only queries rank by upvote score. Pass projectId to target a specific project; otherwise the configured default project is used. Use tag names like 'agent-runner', not IDs. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.",
324
490
  {
325
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
326
- tagNames: z2.array(z2.string()).optional().describe('Tag names to filter by (e.g., ["agent-runner", "chat"])'),
327
- searchQuery: z2.string().optional().describe("Text search on title and description"),
328
- statusFilters: z2.array(z2.enum(STATUS_ENUM)).optional().describe("Filter by one or more statuses"),
329
- typeFilters: z2.array(z2.enum(CARD_TYPE_ENUM)).optional().describe(
491
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
492
+ tagNames: z3.array(z3.string()).optional().describe('Tag names to filter by (e.g., ["agent-runner", "chat"])'),
493
+ searchQuery: z3.string().optional().describe("Text search on title and description"),
494
+ statusFilters: z3.array(z3.enum(STATUS_ENUM)).optional().describe("Filter by one or more statuses"),
495
+ typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
330
496
  'Card types to include (default ["task"]). Pass e.g. ["incident"] or list several to search across types.'
331
497
  ),
332
- assigneeId: z2.string().optional().describe("Filter by assigned user ID"),
333
- unassigned: z2.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
334
- subProjectId: z2.string().optional().describe(
498
+ assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
499
+ unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
500
+ subProjectId: z3.string().optional().describe(
335
501
  "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
336
502
  ),
337
- limit: z2.number().optional().describe("Max results to return (default 20)")
503
+ limit: z3.number().optional().describe("Max results to return (default 20)")
338
504
  },
339
505
  async (params) => {
340
506
  const tasks = await conn2.searchTasks(params);
@@ -349,7 +515,7 @@ function registerListTags(server2, conn2) {
349
515
  "list_tags",
350
516
  "List all project tags with their names, IDs, and colors. Pass projectId to target a specific project; otherwise the configured default project is used.",
351
517
  {
352
- projectId: z2.string().optional().describe("Target Conveyor project ID")
518
+ projectId: z3.string().optional().describe("Target Conveyor project ID")
353
519
  },
354
520
  async (params) => {
355
521
  const tags = await conn2.listTags(params.projectId);
@@ -362,9 +528,9 @@ function registerReviewTools(server2, conn2) {
362
528
  "approve_task",
363
529
  "Move a task forward in the review flow (ReviewPR -> ReviewDev, or -> Complete). Pass projectId to target a specific project; otherwise the configured default project is used.",
364
530
  {
365
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
366
- taskId: z2.string().describe("The task ID"),
367
- risk: z2.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'low' on approval.")
531
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
532
+ taskId: z3.string().describe("The task ID"),
533
+ risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'low' on approval.")
368
534
  },
369
535
  async (params) => {
370
536
  const result = await conn2.approveTask(params.taskId, params.projectId, params.risk);
@@ -377,8 +543,8 @@ function registerReviewTools(server2, conn2) {
377
543
  "approve_and_merge_pr",
378
544
  "Approve and merge a child task's pull request. Pass projectId to target a specific project; otherwise the configured default project is used. Only succeeds if all CI/CD checks are passing. The child task must be in ReviewPR status with a PR.",
379
545
  {
380
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
381
- childTaskId: z2.string().describe("The child task ID whose PR should be approved and merged")
546
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
547
+ childTaskId: z3.string().describe("The child task ID whose PR should be approved and merged")
382
548
  },
383
549
  async (params) => {
384
550
  const result = await conn2.approveAndMergePR(params.childTaskId, params.projectId);
@@ -396,10 +562,10 @@ function registerReviewTools(server2, conn2) {
396
562
  "request_changes",
397
563
  "Post feedback and send task back to InProgress for more work. Pass projectId to target a specific project; otherwise the configured default project is used.",
398
564
  {
399
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
400
- taskId: z2.string().describe("The task ID"),
401
- feedback: z2.string().describe("Feedback message describing requested changes"),
402
- risk: z2.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'medium' on changes.")
565
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
566
+ taskId: z3.string().describe("The task ID"),
567
+ feedback: z3.string().describe("Feedback message describing requested changes"),
568
+ risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'medium' on changes.")
403
569
  },
404
570
  async (params) => {
405
571
  await conn2.requestChanges(params.taskId, params.feedback, params.projectId, params.risk);
@@ -416,9 +582,9 @@ function registerReviewerTools(server2, conn2) {
416
582
  "add_reviewer",
417
583
  "Add a project member as a reviewer on a task. Pass projectId to target a specific project; otherwise the configured default project is used. Idempotent \u2014 adding an existing reviewer is a no-op.",
418
584
  {
419
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
420
- taskId: z2.string().describe("The task ID or slug"),
421
- userId: z2.string().describe("User ID of the reviewer (use list_project_members to resolve a name or email)")
585
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
586
+ taskId: z3.string().describe("The task ID or slug"),
587
+ userId: z3.string().describe("User ID of the reviewer (use list_project_members to resolve a name or email)")
422
588
  },
423
589
  async (params) => {
424
590
  const result = await conn2.addReviewer(params);
@@ -436,9 +602,9 @@ function registerReviewerTools(server2, conn2) {
436
602
  "remove_reviewer",
437
603
  "Remove a reviewer from a task. Pass projectId to target a specific project; otherwise the configured default project is used. Idempotent \u2014 removing a non-reviewer is a no-op.",
438
604
  {
439
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
440
- taskId: z2.string().describe("The task ID or slug"),
441
- userId: z2.string().describe("User ID of the reviewer to remove")
605
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
606
+ taskId: z3.string().describe("The task ID or slug"),
607
+ userId: z3.string().describe("User ID of the reviewer to remove")
442
608
  },
443
609
  async (params) => {
444
610
  const result = await conn2.removeReviewer(params);
@@ -470,8 +636,8 @@ function registerTaskTools(server2, conn2) {
470
636
  }
471
637
 
472
638
  // src/tools/builds.ts
473
- import { z as z3 } from "zod";
474
- function textResult(result) {
639
+ import { z as z4 } from "zod";
640
+ function textResult2(result) {
475
641
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
476
642
  }
477
643
  function registerTaskLifecycleTools(server2, conn2) {
@@ -479,37 +645,37 @@ function registerTaskLifecycleTools(server2, conn2) {
479
645
  "stop_task",
480
646
  "Compatibility alias for the legacy stop path. stop_task now performs the same durable sleep behavior as sleep_task, preserving task Claudespace state while stopping compute. Pass projectId to target a specific project; otherwise the configured default project is used.",
481
647
  {
482
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
483
- taskId: z3.string().describe("The task ID")
648
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
649
+ taskId: z4.string().describe("The task ID")
484
650
  },
485
- async (params) => textResult(await conn2.sleepTask(params.taskId, params.projectId))
651
+ async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
486
652
  );
487
653
  server2.tool(
488
654
  "sleep_task",
489
655
  "Sleep a task Claudespace, stopping compute while preserving durable state. Pass projectId to target a specific project; otherwise the configured default project is used.",
490
656
  {
491
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
492
- taskId: z3.string().describe("The task ID")
657
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
658
+ taskId: z4.string().describe("The task ID")
493
659
  },
494
- async (params) => textResult(await conn2.sleepTask(params.taskId, params.projectId))
660
+ async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
495
661
  );
496
662
  server2.tool(
497
663
  "resume_task",
498
664
  "Resume a sleeping task Claudespace. Pass projectId to target a specific project; otherwise the configured default project is used.",
499
665
  {
500
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
501
- taskId: z3.string().describe("The task ID")
666
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
667
+ taskId: z4.string().describe("The task ID")
502
668
  },
503
- async (params) => textResult(await conn2.resumeTask(params.taskId, params.projectId))
669
+ async (params) => textResult2(await conn2.resumeTask(params.taskId, params.projectId))
504
670
  );
505
671
  server2.tool(
506
672
  "delete_task_environment",
507
673
  "Delete a task environment, including durable Claudespace state. Pass projectId to target a specific project; otherwise the configured default project is used.",
508
674
  {
509
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
510
- taskId: z3.string().describe("The task ID")
675
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
676
+ taskId: z4.string().describe("The task ID")
511
677
  },
512
- async (params) => textResult(await conn2.deleteTaskEnvironment(params.taskId, params.projectId))
678
+ async (params) => textResult2(await conn2.deleteTaskEnvironment(params.taskId, params.projectId))
513
679
  );
514
680
  }
515
681
  function registerBuildTools(server2, conn2) {
@@ -517,12 +683,12 @@ function registerBuildTools(server2, conn2) {
517
683
  "start_task",
518
684
  "Start a cloud build (codespace) for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
519
685
  {
520
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
521
- taskId: z3.string().describe("The task ID")
686
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
687
+ taskId: z4.string().describe("The task ID")
522
688
  },
523
689
  async (params) => {
524
690
  const result = await conn2.startBuild(params.taskId, params.projectId);
525
- return textResult(result);
691
+ return textResult2(result);
526
692
  }
527
693
  );
528
694
  registerTaskLifecycleTools(server2, conn2);
@@ -530,26 +696,26 @@ function registerBuildTools(server2, conn2) {
530
696
  "create_release",
531
697
  "Create a release for the project \u2014 the same flow as the Release button in the web UI. Pass projectId to target a specific project; otherwise the configured default project is used. Creates a release task with a release/YYYY.MM.N branch and a PR from the dev branch to the default branch. Omit taskIds to release ALL cards currently in Review (Dev); pass a subset to cherry-pick \u2014 a cloud build agent then cherry-picks those changes and resolves conflicts. Fails if a release is already in progress or no cards are in Review (Dev).",
532
698
  {
533
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
534
- taskIds: z3.array(z3.string()).optional().describe(
699
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
700
+ taskIds: z4.array(z4.string()).optional().describe(
535
701
  "Task IDs in Review (Dev) to cherry-pick into the release. Omit to release all of them."
536
702
  )
537
703
  },
538
704
  async (params) => {
539
705
  const result = await conn2.createRelease(params.taskIds, params.projectId);
540
- return textResult(result);
706
+ return textResult2(result);
541
707
  }
542
708
  );
543
709
  server2.tool(
544
710
  "get_build_status",
545
711
  "Check codespace and agent status for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
546
712
  {
547
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
548
- taskId: z3.string().describe("The task ID")
713
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
714
+ taskId: z4.string().describe("The task ID")
549
715
  },
550
716
  async (params) => {
551
717
  const status = await conn2.getBuildStatus(params.taskId, params.projectId);
552
- return textResult(status);
718
+ return textResult2(status);
553
719
  }
554
720
  );
555
721
  }
@@ -557,7 +723,7 @@ function registerBuildTools(server2, conn2) {
557
723
  // src/tools/attachments.ts
558
724
  import { readFile, stat } from "fs/promises";
559
725
  import { basename, extname } from "path";
560
- import { z as z4 } from "zod";
726
+ import { z as z5 } from "zod";
561
727
  var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
562
728
  var MIME_BY_EXT = {
563
729
  ".png": "image/png",
@@ -594,8 +760,8 @@ function registerListTaskFiles(server2, conn2) {
594
760
  "list_task_files",
595
761
  "List all files attached to a task with metadata (no contents \u2014 fast and small). Pass projectId to target a specific project; otherwise the configured default project is used. Use before fetching a specific file to see what is available and how large each is. For file contents use get_attachment.",
596
762
  {
597
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
598
- taskId: z4.string().describe("The task ID or slug")
763
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
764
+ taskId: z5.string().describe("The task ID or slug")
599
765
  },
600
766
  async (params) => {
601
767
  const files = await conn2.listTaskFiles(params.taskId, params.projectId);
@@ -637,11 +803,11 @@ function registerGetAttachment(server2, conn2) {
637
803
  "get_attachment",
638
804
  "Fetch one task file's content plus metadata by file ID (accepts task id or slug). Pass projectId to target a specific project; otherwise the configured default project is used. Images are returned as viewable image blocks. Large text files (logs, JSON) are returned in pages \u2014 use `offset`/`maxBytes` to read more, or fetch `downloadUrl` for the whole file. Call list_task_files first to discover IDs and sizes.",
639
805
  {
640
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
641
- taskId: z4.string().describe("The task ID or slug"),
642
- fileId: z4.string().describe("The file ID to fetch"),
643
- offset: z4.number().int().nonnegative().optional().describe("Byte offset into text content (paging). Default 0."),
644
- maxBytes: z4.number().int().positive().optional().describe("Max bytes of text content to return from offset.")
806
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
807
+ taskId: z5.string().describe("The task ID or slug"),
808
+ fileId: z5.string().describe("The file ID to fetch"),
809
+ offset: z5.number().int().nonnegative().optional().describe("Byte offset into text content (paging). Default 0."),
810
+ maxBytes: z5.number().int().positive().optional().describe("Max bytes of text content to return from offset.")
645
811
  },
646
812
  async (params) => {
647
813
  const file = await conn2.getAttachment(params.taskId, params.fileId, {
@@ -658,11 +824,11 @@ function registerUploadAttachment(server2, conn2) {
658
824
  "upload_attachment",
659
825
  "Upload a local file as a task attachment (any file type, up to 25MB). Pass projectId to target a specific project; otherwise the configured default project is used. The file appears under the task's Files. Pass `comment` to also post it to the task chat in the same step.",
660
826
  {
661
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
662
- taskId: z4.string().describe("The task ID or slug"),
663
- path: z4.string().describe("Absolute path to the local file to upload"),
664
- comment: z4.string().optional().describe("When set, also posts the attachment to the task chat with this text"),
665
- mimeType: z4.string().optional().describe("Override the mime type inferred from the file extension")
827
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
828
+ taskId: z5.string().describe("The task ID or slug"),
829
+ path: z5.string().describe("Absolute path to the local file to upload"),
830
+ comment: z5.string().optional().describe("When set, also posts the attachment to the task chat with this text"),
831
+ mimeType: z5.string().optional().describe("Override the mime type inferred from the file extension")
666
832
  },
667
833
  async (params) => {
668
834
  const info = await stat(params.path).catch(() => null);
@@ -725,18 +891,18 @@ function registerAttachmentTools(server2, conn2) {
725
891
  }
726
892
 
727
893
  // src/tools/pull-request.ts
728
- import { z as z5 } from "zod";
894
+ import { z as z6 } from "zod";
729
895
  function registerPullRequestTools(server2, conn2) {
730
896
  server2.tool(
731
897
  "create_pull_request",
732
898
  "Open a GitHub pull request for a task's existing branch (the branch must already be pushed to origin). Pass projectId to target a specific project; otherwise the configured default project is used. Moves the task to ReviewPR. Returns the PR number and URL.",
733
899
  {
734
- projectId: z5.string().optional().describe("Target Conveyor project ID"),
735
- taskId: z5.string().describe("The task ID whose branch should be opened as a PR"),
736
- title: z5.string().describe("Pull request title"),
737
- body: z5.string().describe("Pull request body (markdown)"),
738
- head: z5.string().optional().describe("Source branch for the PR (defaults to the task's branch)"),
739
- base: z5.string().optional().describe("Target branch for the PR (defaults to the repo default)")
900
+ projectId: z6.string().optional().describe("Target Conveyor project ID"),
901
+ taskId: z6.string().describe("The task ID whose branch should be opened as a PR"),
902
+ title: z6.string().describe("Pull request title"),
903
+ body: z6.string().describe("Pull request body (markdown)"),
904
+ head: z6.string().optional().describe("Source branch for the PR (defaults to the task's branch)"),
905
+ base: z6.string().optional().describe("Target branch for the PR (defaults to the repo default)")
740
906
  },
741
907
  async (params) => {
742
908
  const result = await conn2.createPullRequest(params);
@@ -748,7 +914,7 @@ function registerPullRequestTools(server2, conn2) {
748
914
  }
749
915
 
750
916
  // src/tools/subtasks.ts
751
- import { z as z6 } from "zod";
917
+ import { z as z7 } from "zod";
752
918
  var STATUS_ENUM2 = [
753
919
  "Planning",
754
920
  "Open",
@@ -766,15 +932,15 @@ function registerCreateSubtask(server2, conn2) {
766
932
  "create_subtask",
767
933
  "Create a subtask under a parent task. Pass projectId to target a specific project; otherwise the configured default project is used. Subtasks break a larger task into independently buildable pieces. For children that instead ship on the parent's own branch/PR (e.g. per-theme tracking cards for one bundled PR), set followParentStatus so their status rides the parent's automatically.",
768
934
  {
769
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
770
- parentTaskId: z6.string().describe("The parent task ID"),
771
- title: z6.string().describe("Subtask title"),
772
- description: z6.string().optional().describe("Subtask description"),
773
- plan: z6.string().optional().describe("Subtask implementation plan (markdown)"),
774
- ordinal: z6.number().optional().describe("Ordering position among siblings"),
775
- storyPointValue: z6.number().optional().describe(SP_DESCRIPTION),
776
- followParentStatus: z6.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
777
- dependsOn: z6.array(z6.string()).optional().describe(
935
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
936
+ parentTaskId: z7.string().describe("The parent task ID"),
937
+ title: z7.string().describe("Subtask title"),
938
+ description: z7.string().optional().describe("Subtask description"),
939
+ plan: z7.string().optional().describe("Subtask implementation plan (markdown)"),
940
+ ordinal: z7.number().optional().describe("Ordering position among siblings"),
941
+ storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
942
+ followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
943
+ dependsOn: z7.array(z7.string()).optional().describe(
778
944
  "Sibling subtask ids or slugs this subtask blocks on (it won't start until they merge to dev). Set explicit dependency metadata here instead of describing order in the plan text. Omit / leave empty for independent children so they run in parallel."
779
945
  )
780
946
  },
@@ -789,17 +955,20 @@ function registerCreateSubtask(server2, conn2) {
789
955
  function registerUpdateSubtask(server2, conn2) {
790
956
  server2.tool(
791
957
  "update_subtask",
792
- "Update a subtask's fields: title, description, plan, status, ordering, or story points. Pass projectId to target a specific project; otherwise the configured default project is used. Moving a subtask beyond Planning auto-fills missing story points and agent assignment \u2014 don't spend turns on them.",
958
+ "Update a subtask's fields: title, description, plan, status, ordering, story points, or dependencies. Pass projectId to target a specific project; otherwise the configured default project is used. Moving a subtask beyond Planning auto-fills missing story points and agent assignment \u2014 don't spend turns on them.",
793
959
  {
794
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
795
- subtaskId: z6.string().describe("The subtask ID"),
796
- title: z6.string().optional().describe("New title"),
797
- description: z6.string().optional().describe("New description"),
798
- plan: z6.string().optional().describe("New plan (markdown)"),
799
- status: z6.enum(STATUS_ENUM2).optional().describe("New status"),
800
- ordinal: z6.number().optional().describe("New ordering position among siblings"),
801
- storyPointValue: z6.number().optional().describe(SP_DESCRIPTION),
802
- followParentStatus: z6.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION)
960
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
961
+ subtaskId: z7.string().describe("The subtask ID"),
962
+ title: z7.string().optional().describe("New title"),
963
+ description: z7.string().optional().describe("New description"),
964
+ plan: z7.string().optional().describe("New plan (markdown)"),
965
+ status: z7.enum(STATUS_ENUM2).optional().describe("New status"),
966
+ ordinal: z7.number().optional().describe("New ordering position among siblings"),
967
+ storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
968
+ followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
969
+ dependsOn: z7.array(z7.string()).optional().describe(
970
+ "Replace the sibling subtask ids/slugs this subtask blocks on (pass [] to clear). Omit to leave dependencies unchanged."
971
+ )
803
972
  },
804
973
  async (params) => {
805
974
  const result = await conn2.updateSubtask(params);
@@ -816,8 +985,8 @@ function registerListSubtasks(server2, conn2) {
816
985
  "list_subtasks",
817
986
  "List all subtasks of a parent task with their status and ordering. Pass projectId to target a specific project; otherwise the configured default project is used.",
818
987
  {
819
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
820
- taskId: z6.string().describe("The parent task ID")
988
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
989
+ taskId: z7.string().describe("The parent task ID")
821
990
  },
822
991
  async (params) => {
823
992
  const subtasks = await conn2.listSubtasks(params.taskId, params.projectId);
@@ -830,8 +999,8 @@ function registerDeleteSubtask(server2, conn2) {
830
999
  "delete_subtask",
831
1000
  "Delete a subtask by ID. Pass projectId to target a specific project; otherwise the configured default project is used. This is permanent \u2014 use update_subtask to set status to Cancelled if you only want to close it.",
832
1001
  {
833
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
834
- subtaskId: z6.string().describe("The subtask ID to delete")
1002
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
1003
+ subtaskId: z7.string().describe("The subtask ID to delete")
835
1004
  },
836
1005
  async (params) => {
837
1006
  const result = await conn2.deleteSubtask(params.subtaskId, params.projectId);
@@ -851,14 +1020,14 @@ function registerSubtaskTools(server2, conn2) {
851
1020
  }
852
1021
 
853
1022
  // src/tools/dependencies.ts
854
- import { z as z7 } from "zod";
1023
+ import { z as z8 } from "zod";
855
1024
  function registerGetDependencies(server2, conn2) {
856
1025
  server2.tool(
857
1026
  "get_dependencies",
858
1027
  "Get a task's dependencies and their met/unmet status (met = merged to dev). Pass projectId to target a specific project; otherwise the configured default project is used. Use to confirm blockers merged, or see why a task cannot start. For task state use get_task.",
859
1028
  {
860
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
861
- taskId: z7.string().describe("The task ID")
1029
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1030
+ taskId: z8.string().describe("The task ID")
862
1031
  },
863
1032
  async (params) => {
864
1033
  const deps = await conn2.getDependencies(params.taskId, params.projectId);
@@ -871,9 +1040,9 @@ function registerAddDependency(server2, conn2) {
871
1040
  "add_dependency",
872
1041
  "Add a blocking dependency \u2014 this task cannot start until the named task is merged to dev. Pass projectId to target a specific project; otherwise the configured default project is used.",
873
1042
  {
874
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
875
- taskId: z7.string().describe("The task ID that will be blocked"),
876
- dependsOnSlugOrId: z7.string().describe("Slug or ID of the task this one depends on")
1043
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1044
+ taskId: z8.string().describe("The task ID that will be blocked"),
1045
+ dependsOnSlugOrId: z8.string().describe("Slug or ID of the task this one depends on")
877
1046
  },
878
1047
  async (params) => {
879
1048
  await conn2.addDependency(params);
@@ -886,9 +1055,9 @@ function registerRemoveDependency(server2, conn2) {
886
1055
  "remove_dependency",
887
1056
  "Remove a previously added dependency from a task. Pass projectId to target a specific project; otherwise the configured default project is used. The task is no longer blocked by the named task. Returns: confirmation string.",
888
1057
  {
889
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
890
- taskId: z7.string().describe("The task ID to unblock"),
891
- dependsOnSlugOrId: z7.string().describe("Slug or ID of the dependency to remove")
1058
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1059
+ taskId: z8.string().describe("The task ID to unblock"),
1060
+ dependsOnSlugOrId: z8.string().describe("Slug or ID of the dependency to remove")
892
1061
  },
893
1062
  async (params) => {
894
1063
  await conn2.removeDependency(params);
@@ -903,16 +1072,16 @@ function registerDependencyTools(server2, conn2) {
903
1072
  }
904
1073
 
905
1074
  // src/tools/suggestions.ts
906
- import { z as z8 } from "zod";
1075
+ import { z as z9 } from "zod";
907
1076
  function registerSuggestionTools(server2, conn2) {
908
1077
  server2.tool(
909
1078
  "create_suggestion",
910
1079
  "Suggest a feature, improvement, rule, or idea for the project. Pass projectId to target a specific project; otherwise the configured default project is used. Duplicates are deduped and your upvote is recorded.",
911
1080
  {
912
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
913
- title: z8.string().describe("Suggestion title"),
914
- description: z8.string().optional().describe("Suggestion details (markdown)"),
915
- tagNames: z8.array(z8.string()).optional().describe('Tag names to categorize the suggestion (e.g., ["agent-runner"])')
1081
+ projectId: z9.string().optional().describe("Target Conveyor project ID"),
1082
+ title: z9.string().describe("Suggestion title"),
1083
+ description: z9.string().optional().describe("Suggestion details (markdown)"),
1084
+ tagNames: z9.array(z9.string()).optional().describe('Tag names to categorize the suggestion (e.g., ["agent-runner"])')
916
1085
  },
917
1086
  async (params) => {
918
1087
  const result = await conn2.createSuggestion(params);
@@ -923,7 +1092,7 @@ function registerSuggestionTools(server2, conn2) {
923
1092
  }
924
1093
 
925
1094
  // src/tools/checklists.ts
926
- import { z as z9 } from "zod";
1095
+ import { z as z10 } from "zod";
927
1096
  function renderManualTestGroups(groups) {
928
1097
  const lines = [];
929
1098
  for (const g of groups) {
@@ -932,7 +1101,7 @@ function renderManualTestGroups(groups) {
932
1101
  const mark = t.status === "approved" ? "\u2713" : t.status === "rejected" ? "\u2717" : "\u25CB";
933
1102
  lines.push(` ${mark} ${t.title} \u2014 ${t.status}`);
934
1103
  if (t.status === "rejected") {
935
- for (const f of t.failures) {
1104
+ for (const f of t.failures ?? []) {
936
1105
  lines.push(` \u26A0 ${f.userName ?? "Someone"}: ${f.reason ?? "(no message)"}`);
937
1106
  }
938
1107
  }
@@ -946,8 +1115,8 @@ function registerListManualTests(server2, conn2) {
946
1115
  "list_manual_tests",
947
1116
  "List the manual test checklist items for a task. Pass projectId to target a specific project; otherwise the configured default project is used. Use to see what manual verification steps have already been recorded.",
948
1117
  {
949
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
950
- taskId: z9.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
1118
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1119
+ taskId: z10.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
951
1120
  },
952
1121
  async (params) => {
953
1122
  const items = await conn2.listManualTests(params.taskId, params.projectId);
@@ -973,9 +1142,9 @@ function registerSetManualTests(server2, conn2) {
973
1142
  "set_manual_tests",
974
1143
  "Add manual test steps to a task's checklist. Pass projectId to target a specific project; otherwise the configured default project is used. Existing items with the same title are automatically skipped (deduplication). Use to record specific manual verification steps that reviewers should follow when testing the task's PR.",
975
1144
  {
976
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
977
- taskId: z9.string().describe("The task ID or slug"),
978
- items: z9.array(z9.object({ title: z9.string().min(1).describe("A concise, actionable test step") })).min(1).describe("List of manual test steps to add")
1145
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1146
+ taskId: z10.string().describe("The task ID or slug"),
1147
+ items: z10.array(z10.object({ title: z10.string().min(1).describe("A concise, actionable test step") })).min(1).describe("List of manual test steps to add")
979
1148
  },
980
1149
  async (params) => {
981
1150
  const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
@@ -990,10 +1159,10 @@ function registerEditManualTest(server2, conn2) {
990
1159
  "edit_manual_test",
991
1160
  "Rename an existing manual test step on a task. Pass projectId to target a specific project; otherwise the configured default project is used. Identify the test by its current title (case-insensitive) and pass the new title to replace it.",
992
1161
  {
993
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
994
- taskId: z9.string().describe("The task ID or slug"),
995
- title: z9.string().min(1).describe("The current title of the manual test to edit"),
996
- newTitle: z9.string().min(1).describe("The new title for the manual test")
1162
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1163
+ taskId: z10.string().describe("The task ID or slug"),
1164
+ title: z10.string().min(1).describe("The current title of the manual test to edit"),
1165
+ newTitle: z10.string().min(1).describe("The new title for the manual test")
997
1166
  },
998
1167
  async (params) => {
999
1168
  await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
@@ -1006,9 +1175,9 @@ function registerRemoveManualTest(server2, conn2) {
1006
1175
  "remove_manual_test",
1007
1176
  "Remove an existing manual test step from a task's checklist. Pass projectId to target a specific project; otherwise the configured default project is used. Identify the test by its title (case-insensitive).",
1008
1177
  {
1009
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
1010
- taskId: z9.string().describe("The task ID or slug"),
1011
- title: z9.string().min(1).describe("The title of the manual test to remove")
1178
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1179
+ taskId: z10.string().describe("The task ID or slug"),
1180
+ title: z10.string().min(1).describe("The title of the manual test to remove")
1012
1181
  },
1013
1182
  async (params) => {
1014
1183
  await conn2.removeManualTest(params.taskId, params.title, params.projectId);
@@ -1021,9 +1190,9 @@ function registerApproveManualTest(server2, conn2) {
1021
1190
  "approve_manual_test",
1022
1191
  "Sign off on (approve) a manual test step on a task on behalf of your authenticated user. Pass projectId to target a specific project; otherwise the configured default project is used. Identify the test by its title (case-insensitive). Use after you have verified the step passes.",
1023
1192
  {
1024
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
1025
- taskId: z9.string().describe("The task ID or slug"),
1026
- title: z9.string().min(1).describe("The title of the manual test to approve")
1193
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1194
+ taskId: z10.string().describe("The task ID or slug"),
1195
+ title: z10.string().min(1).describe("The title of the manual test to approve")
1027
1196
  },
1028
1197
  async (params) => {
1029
1198
  await conn2.approveManualTest(params.taskId, params.title, params.projectId);
@@ -1036,10 +1205,10 @@ function registerRejectManualTest(server2, conn2) {
1036
1205
  "reject_manual_test",
1037
1206
  "Flag an issue with (reject) a manual test step on a task on behalf of your authenticated user, recording the reason. Pass projectId to target a specific project; otherwise the configured default project is used. Identify the test by its title (case-insensitive). Use when the step fails verification.",
1038
1207
  {
1039
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
1040
- taskId: z9.string().describe("The task ID or slug"),
1041
- title: z9.string().min(1).describe("The title of the manual test to reject"),
1042
- reason: z9.string().min(1).max(2e3).describe("Why the test failed \u2014 what went wrong, shown to the team")
1208
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1209
+ taskId: z10.string().describe("The task ID or slug"),
1210
+ title: z10.string().min(1).describe("The title of the manual test to reject"),
1211
+ reason: z10.string().min(1).max(2e3).describe("Why the test failed \u2014 what went wrong, shown to the team")
1043
1212
  },
1044
1213
  async (params) => {
1045
1214
  await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
@@ -1059,9 +1228,9 @@ function registerQueryManualTests(server2, conn2) {
1059
1228
  "query_manual_tests",
1060
1229
  "Query manual tests across many tasks in a project, grouped by task. Filter by card status (e.g. ReviewDev, ReviewLive, Complete) and/or test status (open | approved | rejected). Use to answer questions like 'show all OPEN manual tests in ReviewDev' or 'show all REJECTED manual tests in ReviewDev/ReviewLive with the failing reason'. With no filters it defaults to the needs-attention view: open+rejected tests on ReviewDev/ReviewLive cards. Pass projectId to target a specific project; otherwise the configured default project is used.",
1061
1230
  {
1062
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
1063
- cardStatuses: z9.array(z9.string()).optional().describe('Filter tasks by card/column status, e.g. ["ReviewDev", "ReviewLive"]'),
1064
- testStatuses: z9.array(z9.enum(["open", "approved", "rejected"])).optional().describe("Filter tests by status: open | approved | rejected")
1231
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1232
+ cardStatuses: z10.array(z10.string()).optional().describe('Filter tasks by card/column status, e.g. ["ReviewDev", "ReviewLive"]'),
1233
+ testStatuses: z10.array(z10.enum(["open", "approved", "rejected"])).optional().describe("Filter tests by status: open | approved | rejected")
1065
1234
  },
1066
1235
  async (params) => {
1067
1236
  const groups = await conn2.queryManualTests({
@@ -1087,7 +1256,7 @@ function registerChecklistTools(server2, conn2) {
1087
1256
  }
1088
1257
 
1089
1258
  // src/tools/workspace.ts
1090
- import { z as z10 } from "zod";
1259
+ import { z as z11 } from "zod";
1091
1260
 
1092
1261
  // src/workspace-ssh-tunnel.ts
1093
1262
  import net from "net";
@@ -1206,8 +1375,8 @@ function registerAttachInfoTool(server2, conn2) {
1206
1375
  "workspace_attach_info",
1207
1376
  "Return SSH/SFTP attach metadata for a running task Claudespace, plus hosted preview URLs and configured preview ports. Optionally installs an OpenSSH public key for this attach session.",
1208
1377
  {
1209
- taskId: z10.string().describe("The task ID"),
1210
- sshPublicKey: z10.string().optional().describe("Optional OpenSSH public key to install into the workspace")
1378
+ taskId: z11.string().describe("The task ID"),
1379
+ sshPublicKey: z11.string().optional().describe("Optional OpenSSH public key to install into the workspace")
1211
1380
  },
1212
1381
  async ({ taskId, sshPublicKey }) => {
1213
1382
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1220,7 +1389,7 @@ function registerPreviewUrlsTool(server2, conn2) {
1220
1389
  "workspace_preview_urls",
1221
1390
  "Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
1222
1391
  {
1223
- taskId: z10.string().describe("The task ID")
1392
+ taskId: z11.string().describe("The task ID")
1224
1393
  },
1225
1394
  async ({ taskId }) => {
1226
1395
  const info = await conn2.getWorkspaceAttachInfo(taskId);
@@ -1249,10 +1418,10 @@ function registerStartTunnelTool(server2, conn2, startTunnel) {
1249
1418
  "workspace_start_tunnel",
1250
1419
  "Start a local loopback tunnel through the MCP server to a running task Claudespace port. Use port 2222 for SSH/SFTP, or one of previewPorts for app access.",
1251
1420
  {
1252
- taskId: z10.string().describe("The task ID"),
1253
- port: z10.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
1254
- preferredLocalPort: z10.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
1255
- sshPublicKey: z10.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
1421
+ taskId: z11.string().describe("The task ID"),
1422
+ port: z11.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
1423
+ preferredLocalPort: z11.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
1424
+ sshPublicKey: z11.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
1256
1425
  },
1257
1426
  async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
1258
1427
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1305,7 +1474,7 @@ function registerStopTunnelTool(server2) {
1305
1474
  "workspace_stop_tunnel",
1306
1475
  "Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
1307
1476
  {
1308
- tunnelId: z10.string().describe("Tunnel id returned by workspace_start_tunnel")
1477
+ tunnelId: z11.string().describe("Tunnel id returned by workspace_start_tunnel")
1309
1478
  },
1310
1479
  async ({ tunnelId }) => {
1311
1480
  const tunnel = activeTunnels.get(tunnelId);
@@ -1324,7 +1493,7 @@ function registerWorkspaceTools(server2, conn2, deps = {}) {
1324
1493
  }
1325
1494
 
1326
1495
  // src/tools/logs.ts
1327
- import { z as z11 } from "zod";
1496
+ import { z as z12 } from "zod";
1328
1497
  var SEVERITY_ENUM = [
1329
1498
  "DEBUG",
1330
1499
  "INFO",
@@ -1428,25 +1597,25 @@ function registerLogTools(server2, conn2) {
1428
1597
  "query_gcp_logs",
1429
1598
  "Query Google Cloud Logging for a project's linked GCP environments \u2014 use this to investigate production or dev issues directly ('something broke on prod'). Envs: 'prod' and 'dev' are the project's Cloud Run apps + Cloud SQL databases (scoped by default to the resources linked in project settings); 'claudespace' is the project's GKE agent-pod namespace. Start broad (severity=ERROR, sinceMinutes=60), then narrow with services/search. Returns compact lines: '<time> <SEVERITY> [<source>] <message> | key=value \u2026' (the key=value tail is the entry's structured payload \u2014 error details, service/method, actor and entity ids). When the response ends with a pageToken line, pass that token back as pageToken for the next page. Pass projectId to target a specific project; otherwise the configured default project is used.",
1430
1599
  {
1431
- projectId: z11.string().optional().describe("Target Conveyor project ID"),
1432
- env: z11.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
1433
- sinceMinutes: z11.number().int().min(1).max(10080).optional().describe(
1600
+ projectId: z12.string().optional().describe("Target Conveyor project ID"),
1601
+ env: z12.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
1602
+ sinceMinutes: z12.number().int().min(1).max(10080).optional().describe(
1434
1603
  "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
1435
1604
  ),
1436
- startTime: z11.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1437
- endTime: z11.string().optional().describe("ISO 8601 upper bound (default now)"),
1438
- severity: z11.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
1439
- services: z11.array(z11.string()).optional().describe(
1605
+ startTime: z12.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1606
+ endTime: z12.string().optional().describe("ISO 8601 upper bound (default now)"),
1607
+ severity: z12.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
1608
+ services: z12.array(z12.string()).optional().describe(
1440
1609
  "Restrict to these Cloud Run service names (prod/dev only). Defaults to all services linked in project settings."
1441
1610
  ),
1442
- sqlInstances: z11.array(z11.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
1443
- allServices: z11.boolean().optional().describe(
1611
+ sqlInstances: z12.array(z12.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
1612
+ allServices: z12.boolean().optional().describe(
1444
1613
  "Set true to search ALL logs in the GCP project, ignoring the linked-resource scope"
1445
1614
  ),
1446
- search: z11.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
1447
- filter: z11.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
1448
- limit: z11.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
1449
- pageToken: z11.string().optional().describe("Opaque token from a previous response to fetch the next page")
1615
+ search: z12.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
1616
+ filter: z12.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
1617
+ limit: z12.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
1618
+ pageToken: z12.string().optional().describe("Opaque token from a previous response to fetch the next page")
1450
1619
  },
1451
1620
  async (params) => {
1452
1621
  const text = await runQueryGcpLogs(conn2, params);
@@ -1458,6 +1627,7 @@ function registerLogTools(server2, conn2) {
1458
1627
  // src/tools/index.ts
1459
1628
  function registerAllTools(server2, conn2) {
1460
1629
  registerProjectTools(server2, conn2);
1630
+ registerProjectConfigTools(server2, conn2);
1461
1631
  registerTaskTools(server2, conn2);
1462
1632
  registerBuildTools(server2, conn2);
1463
1633
  registerAttachmentTools(server2, conn2);