@rallycry/conveyor-mcp 4.3.2 → 4.3.6

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-OAHDLTC6.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { createRequire } from "module";
@@ -55,8 +55,158 @@ function registerProjectTools(server2, conn2) {
55
55
  );
56
56
  }
57
57
 
58
- // src/tools/tasks.ts
58
+ // src/tools/project-config.ts
59
59
  import { z as z2 } from "zod";
60
+ function jsonResult(data) {
61
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
62
+ }
63
+ function textResult(text) {
64
+ return { content: [{ type: "text", text }] };
65
+ }
66
+ var AGENT_DEFAULT_KEYS = [
67
+ "defaultPmAgentId",
68
+ "defaultTaskAgentId",
69
+ "defaultReviewerAgentId",
70
+ "helperAgentId"
71
+ ];
72
+ function registerGetConnectUrls(server2, conn2) {
73
+ server2.tool(
74
+ "get_connect_urls",
75
+ "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.",
76
+ {
77
+ projectId: z2.string().optional().describe("Target Conveyor project ID")
78
+ },
79
+ async (params) => jsonResult(await conn2.getConnectUrls(params.projectId))
80
+ );
81
+ }
82
+ function registerUpdateProjectSettings(server2, conn2) {
83
+ server2.tool(
84
+ "update_project_settings",
85
+ "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.",
86
+ {
87
+ projectId: z2.string().optional().describe("Target Conveyor project ID"),
88
+ name: z2.string().optional().describe("New project name"),
89
+ description: z2.string().optional().describe("New project description"),
90
+ settings: z2.record(z2.unknown()).optional().describe("Shallow-merged patch of the project settings JSON (advanced)"),
91
+ defaultPmAgentId: z2.string().nullable().optional().describe("Default PM agent ID"),
92
+ defaultTaskAgentId: z2.string().nullable().optional().describe("Default task agent ID"),
93
+ defaultReviewerAgentId: z2.string().nullable().optional().describe("Default reviewer agent ID"),
94
+ helperAgentId: z2.string().nullable().optional().describe("Helper agent ID")
95
+ },
96
+ async (params) => {
97
+ const { projectId: projectId2, name, description, settings, ...agents } = params;
98
+ const results = [];
99
+ const config = {};
100
+ if (name !== void 0) config.name = name;
101
+ if (description !== void 0) config.description = description;
102
+ if (settings !== void 0) config.settings = settings;
103
+ if (Object.keys(config).length > 0) {
104
+ results.push(await conn2.updateProjectConfig({ projectId: projectId2, ...config }));
105
+ }
106
+ const defaults = {};
107
+ for (const key of AGENT_DEFAULT_KEYS) {
108
+ if (agents[key] !== void 0) defaults[key] = agents[key];
109
+ }
110
+ if (Object.keys(defaults).length > 0) {
111
+ results.push(await conn2.updateProjectAgentDefaults({ projectId: projectId2, ...defaults }));
112
+ }
113
+ if (results.length === 0) return textResult("Nothing to update \u2014 pass at least one field.");
114
+ return jsonResult(results.length === 1 ? results[0] : results);
115
+ }
116
+ );
117
+ }
118
+ function registerManageTags(server2, conn2) {
119
+ server2.tool(
120
+ "manage_tags",
121
+ "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.",
122
+ {
123
+ action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
124
+ projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
125
+ id: z2.string().optional().describe("Tag ID (update/delete)"),
126
+ name: z2.string().optional().describe("Tag name"),
127
+ color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
128
+ description: z2.string().optional().describe("Tag description")
129
+ },
130
+ async (params) => {
131
+ const { action, projectId: projectId2, id, name, color, description } = params;
132
+ if (action === "list") return jsonResult(await conn2.listTagsDetailed(projectId2));
133
+ if (action === "create") {
134
+ if (!name) return textResult("name is required for create.");
135
+ return jsonResult(
136
+ await conn2.createTag({
137
+ projectId: projectId2,
138
+ name,
139
+ ...color !== void 0 && { color },
140
+ ...description !== void 0 && { description }
141
+ })
142
+ );
143
+ }
144
+ if (!id) return textResult(`id is required for ${action}.`);
145
+ if (action === "delete") return jsonResult(await conn2.deleteTag(id));
146
+ return jsonResult(
147
+ await conn2.updateTag({
148
+ id,
149
+ ...name !== void 0 && { name },
150
+ ...color !== void 0 && { color },
151
+ ...description !== void 0 && { description }
152
+ })
153
+ );
154
+ }
155
+ );
156
+ }
157
+ function registerManagePriorities(server2, conn2) {
158
+ server2.tool(
159
+ "manage_priorities",
160
+ "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.",
161
+ {
162
+ action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
163
+ projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
164
+ id: z2.string().optional().describe("Priority ID (update/delete)"),
165
+ value: z2.number().int().min(1).max(100).optional().describe("Priority value (1 = highest urgency scale point)"),
166
+ name: z2.string().optional().describe("Priority name"),
167
+ color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
168
+ description: z2.string().optional().describe("Priority description")
169
+ },
170
+ async (params) => {
171
+ const { action, projectId: projectId2, id, value, name, color, description } = params;
172
+ if (action === "list") return jsonResult(await conn2.listPriorities(projectId2));
173
+ if (action === "create") {
174
+ if (value === void 0 || !name || !color) {
175
+ return textResult("value, name, and color are required for create.");
176
+ }
177
+ return jsonResult(
178
+ await conn2.createPriority({
179
+ projectId: projectId2,
180
+ value,
181
+ name,
182
+ color,
183
+ ...description !== void 0 && { description }
184
+ })
185
+ );
186
+ }
187
+ if (!id) return textResult(`id is required for ${action}.`);
188
+ if (action === "delete") return jsonResult(await conn2.deletePriority(id));
189
+ return jsonResult(
190
+ await conn2.updatePriority({
191
+ id,
192
+ ...value !== void 0 && { value },
193
+ ...name !== void 0 && { name },
194
+ ...color !== void 0 && { color },
195
+ ...description !== void 0 && { description }
196
+ })
197
+ );
198
+ }
199
+ );
200
+ }
201
+ function registerProjectConfigTools(server2, conn2) {
202
+ registerGetConnectUrls(server2, conn2);
203
+ registerUpdateProjectSettings(server2, conn2);
204
+ registerManageTags(server2, conn2);
205
+ registerManagePriorities(server2, conn2);
206
+ }
207
+
208
+ // src/tools/tasks.ts
209
+ import { z as z3 } from "zod";
60
210
  var CLI_EVENT_FORMATTERS = {
61
211
  thinking: (data) => String(data.message ?? ""),
62
212
  tool_use: (data) => `${data.tool}: ${String(data.input ?? "").slice(0, 1e3)}`,
@@ -129,16 +279,19 @@ var RISK_ENUM = ["critical", "high", "medium", "low"];
129
279
  function registerListTasks(server2, conn2) {
130
280
  server2.tool(
131
281
  "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.",
282
+ "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
283
  {
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(
284
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
285
+ status: z3.enum(STATUS_ENUM).optional().describe("Filter by task status"),
286
+ typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
287
+ 'Card types to include, e.g. ["incident"] or ["task", "incident"]. Omit for tasks only.'
288
+ ),
289
+ assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
290
+ unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
291
+ subProjectId: z3.string().optional().describe(
139
292
  "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
140
293
  ),
141
- limit: z2.number().optional().describe("Max tasks to return (default 50)")
294
+ limit: z3.number().optional().describe("Max tasks to return (default 50)")
142
295
  },
143
296
  async (params) => {
144
297
  const tasks = await conn2.listTasks(params);
@@ -153,8 +306,8 @@ function registerGetTask(server2, conn2) {
153
306
  "get_task",
154
307
  "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
308
  {
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>)")
309
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
310
+ taskId: z3.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
158
311
  },
159
312
  async (params) => {
160
313
  const task = await conn2.getTask(params.taskId, params.projectId);
@@ -167,8 +320,8 @@ function registerGetCardBySlug(server2, conn2) {
167
320
  "get_card_by_slug",
168
321
  "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
322
  {
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'")
323
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
324
+ slug: z3.string().describe("The card slug from a Conveyor card URL, e.g. 'ship-it'")
172
325
  },
173
326
  async (params) => {
174
327
  const task = await conn2.getCardBySlug(params.slug, params.projectId);
@@ -181,12 +334,12 @@ function registerCreateTask(server2, conn2) {
181
334
  "create_task",
182
335
  "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
336
  {
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(
337
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
338
+ title: z3.string().describe("Task title"),
339
+ description: z3.string().optional().describe("Task description"),
340
+ plan: z3.string().optional().describe("Task implementation plan (markdown)"),
341
+ status: z3.enum(["Planning", "Open"]).optional().describe("Initial status (default: Planning)"),
342
+ subProjectId: z3.string().optional().describe(
190
343
  "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
191
344
  )
192
345
  },
@@ -201,17 +354,19 @@ function registerCreateTask(server2, conn2) {
201
354
  function registerUpdateTask(server2, conn2) {
202
355
  server2.tool(
203
356
  "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.",
357
+ "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
358
  {
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(
359
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
360
+ taskId: z3.string().describe("The task ID"),
361
+ title: z3.string().optional().describe("New title"),
362
+ description: z3.string().optional().describe("New description"),
363
+ plan: z3.string().optional().describe("New plan (markdown)"),
364
+ status: z3.enum(STATUS_ENUM).optional().describe("New status"),
365
+ risk: z3.enum(RISK_ENUM).nullable().optional().describe(
211
366
  "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
212
367
  ),
213
- assignedUserId: z2.string().nullable().optional().describe("User ID to assign, or null"),
214
- subProjectId: z2.string().nullable().optional().describe(
368
+ assignedUserId: z3.string().nullable().optional().describe("User ID to assign, or null"),
369
+ subProjectId: z3.string().nullable().optional().describe(
215
370
  "Assign the task to a sub-project board; null moves it back to the parent board. Omit to leave unchanged."
216
371
  )
217
372
  },
@@ -228,9 +383,9 @@ function registerMoveCard(server2, conn2) {
228
383
  "move_card",
229
384
  "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
385
  {
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")
386
+ projectId: z3.string().optional().describe("Source Conveyor project ID"),
387
+ taskId: z3.string().describe("Card ID or slug"),
388
+ destinationProjectId: z3.string().describe("Destination Conveyor project ID")
234
389
  },
235
390
  async (params) => {
236
391
  const result = await conn2.moveCard(params);
@@ -251,9 +406,9 @@ function registerChatTools(server2, conn2) {
251
406
  "read_task_chat",
252
407
  "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
408
  {
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)")
409
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
410
+ taskId: z3.string().describe("The task ID"),
411
+ limit: z3.number().optional().describe("Max messages to return (default 50)")
257
412
  },
258
413
  async (params) => {
259
414
  const messages = await conn2.getTaskChat(params.taskId, params.limit, params.projectId);
@@ -264,9 +419,9 @@ function registerChatTools(server2, conn2) {
264
419
  "post_to_chat",
265
420
  "Post a message to a task's chat. Pass projectId to target a specific project; otherwise the configured default project is used.",
266
421
  {
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")
422
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
423
+ taskId: z3.string().describe("The task ID"),
424
+ content: z3.string().describe("Message content")
270
425
  },
271
426
  async (params) => {
272
427
  await conn2.postToTaskChat(params.taskId, params.content, params.projectId);
@@ -279,12 +434,12 @@ function registerGetTaskCli(server2, conn2) {
279
434
  "get_task_logs",
280
435
  "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
436
  {
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(
437
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
438
+ taskId: z3.string().describe("The task ID or slug"),
439
+ source: z3.enum(["agent", "application"]).optional().describe(
285
440
  "Filter by log source: 'agent' for reasoning/tool calls, 'application' for setup/dev-server output"
286
441
  ),
287
- limit: z2.number().optional().describe("Max entries to return (default 50, max 500)")
442
+ limit: z3.number().optional().describe("Max entries to return (default 50, max 500)")
288
443
  },
289
444
  async ({ taskId, source, limit, projectId: projectId2 }) => {
290
445
  const effectiveLimit = Math.min(limit ?? 50, 500);
@@ -303,8 +458,8 @@ function registerGetTaskSessions(server2, conn2) {
303
458
  "get_task_sessions",
304
459
  "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
460
  {
306
- projectId: z2.string().optional().describe("Target Conveyor project ID"),
307
- taskId: z2.string().describe("The task ID or slug")
461
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
462
+ taskId: z3.string().describe("The task ID or slug")
308
463
  },
309
464
  async ({ taskId, projectId: projectId2 }) => {
310
465
  const tasks = await conn2.getTaskSessions(taskId, projectId2);
@@ -320,21 +475,21 @@ function registerGetTaskSessions(server2, conn2) {
320
475
  function registerSearchTasks(server2, conn2) {
321
476
  server2.tool(
322
477
  "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.",
478
+ "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
479
  {
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(
480
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
481
+ tagNames: z3.array(z3.string()).optional().describe('Tag names to filter by (e.g., ["agent-runner", "chat"])'),
482
+ searchQuery: z3.string().optional().describe("Text search on title and description"),
483
+ statusFilters: z3.array(z3.enum(STATUS_ENUM)).optional().describe("Filter by one or more statuses"),
484
+ typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
330
485
  'Card types to include (default ["task"]). Pass e.g. ["incident"] or list several to search across types.'
331
486
  ),
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(
487
+ assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
488
+ unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
489
+ subProjectId: z3.string().optional().describe(
335
490
  "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
336
491
  ),
337
- limit: z2.number().optional().describe("Max results to return (default 20)")
492
+ limit: z3.number().optional().describe("Max results to return (default 20)")
338
493
  },
339
494
  async (params) => {
340
495
  const tasks = await conn2.searchTasks(params);
@@ -349,7 +504,7 @@ function registerListTags(server2, conn2) {
349
504
  "list_tags",
350
505
  "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
506
  {
352
- projectId: z2.string().optional().describe("Target Conveyor project ID")
507
+ projectId: z3.string().optional().describe("Target Conveyor project ID")
353
508
  },
354
509
  async (params) => {
355
510
  const tags = await conn2.listTags(params.projectId);
@@ -362,9 +517,9 @@ function registerReviewTools(server2, conn2) {
362
517
  "approve_task",
363
518
  "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
519
  {
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.")
520
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
521
+ taskId: z3.string().describe("The task ID"),
522
+ risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'low' on approval.")
368
523
  },
369
524
  async (params) => {
370
525
  const result = await conn2.approveTask(params.taskId, params.projectId, params.risk);
@@ -377,8 +532,8 @@ function registerReviewTools(server2, conn2) {
377
532
  "approve_and_merge_pr",
378
533
  "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
534
  {
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")
535
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
536
+ childTaskId: z3.string().describe("The child task ID whose PR should be approved and merged")
382
537
  },
383
538
  async (params) => {
384
539
  const result = await conn2.approveAndMergePR(params.childTaskId, params.projectId);
@@ -396,10 +551,10 @@ function registerReviewTools(server2, conn2) {
396
551
  "request_changes",
397
552
  "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
553
  {
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.")
554
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
555
+ taskId: z3.string().describe("The task ID"),
556
+ feedback: z3.string().describe("Feedback message describing requested changes"),
557
+ risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'medium' on changes.")
403
558
  },
404
559
  async (params) => {
405
560
  await conn2.requestChanges(params.taskId, params.feedback, params.projectId, params.risk);
@@ -416,9 +571,9 @@ function registerReviewerTools(server2, conn2) {
416
571
  "add_reviewer",
417
572
  "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
573
  {
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)")
574
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
575
+ taskId: z3.string().describe("The task ID or slug"),
576
+ userId: z3.string().describe("User ID of the reviewer (use list_project_members to resolve a name or email)")
422
577
  },
423
578
  async (params) => {
424
579
  const result = await conn2.addReviewer(params);
@@ -436,9 +591,9 @@ function registerReviewerTools(server2, conn2) {
436
591
  "remove_reviewer",
437
592
  "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
593
  {
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")
594
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
595
+ taskId: z3.string().describe("The task ID or slug"),
596
+ userId: z3.string().describe("User ID of the reviewer to remove")
442
597
  },
443
598
  async (params) => {
444
599
  const result = await conn2.removeReviewer(params);
@@ -470,8 +625,8 @@ function registerTaskTools(server2, conn2) {
470
625
  }
471
626
 
472
627
  // src/tools/builds.ts
473
- import { z as z3 } from "zod";
474
- function textResult(result) {
628
+ import { z as z4 } from "zod";
629
+ function textResult2(result) {
475
630
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
476
631
  }
477
632
  function registerTaskLifecycleTools(server2, conn2) {
@@ -479,37 +634,37 @@ function registerTaskLifecycleTools(server2, conn2) {
479
634
  "stop_task",
480
635
  "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
636
  {
482
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
483
- taskId: z3.string().describe("The task ID")
637
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
638
+ taskId: z4.string().describe("The task ID")
484
639
  },
485
- async (params) => textResult(await conn2.sleepTask(params.taskId, params.projectId))
640
+ async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
486
641
  );
487
642
  server2.tool(
488
643
  "sleep_task",
489
644
  "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
645
  {
491
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
492
- taskId: z3.string().describe("The task ID")
646
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
647
+ taskId: z4.string().describe("The task ID")
493
648
  },
494
- async (params) => textResult(await conn2.sleepTask(params.taskId, params.projectId))
649
+ async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
495
650
  );
496
651
  server2.tool(
497
652
  "resume_task",
498
653
  "Resume a sleeping task Claudespace. Pass projectId to target a specific project; otherwise the configured default project is used.",
499
654
  {
500
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
501
- taskId: z3.string().describe("The task ID")
655
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
656
+ taskId: z4.string().describe("The task ID")
502
657
  },
503
- async (params) => textResult(await conn2.resumeTask(params.taskId, params.projectId))
658
+ async (params) => textResult2(await conn2.resumeTask(params.taskId, params.projectId))
504
659
  );
505
660
  server2.tool(
506
661
  "delete_task_environment",
507
662
  "Delete a task environment, including durable Claudespace state. Pass projectId to target a specific project; otherwise the configured default project is used.",
508
663
  {
509
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
510
- taskId: z3.string().describe("The task ID")
664
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
665
+ taskId: z4.string().describe("The task ID")
511
666
  },
512
- async (params) => textResult(await conn2.deleteTaskEnvironment(params.taskId, params.projectId))
667
+ async (params) => textResult2(await conn2.deleteTaskEnvironment(params.taskId, params.projectId))
513
668
  );
514
669
  }
515
670
  function registerBuildTools(server2, conn2) {
@@ -517,12 +672,12 @@ function registerBuildTools(server2, conn2) {
517
672
  "start_task",
518
673
  "Start a cloud build (codespace) for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
519
674
  {
520
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
521
- 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")
522
677
  },
523
678
  async (params) => {
524
679
  const result = await conn2.startBuild(params.taskId, params.projectId);
525
- return textResult(result);
680
+ return textResult2(result);
526
681
  }
527
682
  );
528
683
  registerTaskLifecycleTools(server2, conn2);
@@ -530,26 +685,26 @@ function registerBuildTools(server2, conn2) {
530
685
  "create_release",
531
686
  "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
687
  {
533
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
534
- taskIds: z3.array(z3.string()).optional().describe(
688
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
689
+ taskIds: z4.array(z4.string()).optional().describe(
535
690
  "Task IDs in Review (Dev) to cherry-pick into the release. Omit to release all of them."
536
691
  )
537
692
  },
538
693
  async (params) => {
539
694
  const result = await conn2.createRelease(params.taskIds, params.projectId);
540
- return textResult(result);
695
+ return textResult2(result);
541
696
  }
542
697
  );
543
698
  server2.tool(
544
699
  "get_build_status",
545
700
  "Check codespace and agent status for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
546
701
  {
547
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
548
- taskId: z3.string().describe("The task ID")
702
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
703
+ taskId: z4.string().describe("The task ID")
549
704
  },
550
705
  async (params) => {
551
706
  const status = await conn2.getBuildStatus(params.taskId, params.projectId);
552
- return textResult(status);
707
+ return textResult2(status);
553
708
  }
554
709
  );
555
710
  }
@@ -557,7 +712,7 @@ function registerBuildTools(server2, conn2) {
557
712
  // src/tools/attachments.ts
558
713
  import { readFile, stat } from "fs/promises";
559
714
  import { basename, extname } from "path";
560
- import { z as z4 } from "zod";
715
+ import { z as z5 } from "zod";
561
716
  var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
562
717
  var MIME_BY_EXT = {
563
718
  ".png": "image/png",
@@ -594,8 +749,8 @@ function registerListTaskFiles(server2, conn2) {
594
749
  "list_task_files",
595
750
  "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
751
  {
597
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
598
- taskId: z4.string().describe("The task ID or slug")
752
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
753
+ taskId: z5.string().describe("The task ID or slug")
599
754
  },
600
755
  async (params) => {
601
756
  const files = await conn2.listTaskFiles(params.taskId, params.projectId);
@@ -637,11 +792,11 @@ function registerGetAttachment(server2, conn2) {
637
792
  "get_attachment",
638
793
  "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
794
  {
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.")
795
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
796
+ taskId: z5.string().describe("The task ID or slug"),
797
+ fileId: z5.string().describe("The file ID to fetch"),
798
+ offset: z5.number().int().nonnegative().optional().describe("Byte offset into text content (paging). Default 0."),
799
+ maxBytes: z5.number().int().positive().optional().describe("Max bytes of text content to return from offset.")
645
800
  },
646
801
  async (params) => {
647
802
  const file = await conn2.getAttachment(params.taskId, params.fileId, {
@@ -658,11 +813,11 @@ function registerUploadAttachment(server2, conn2) {
658
813
  "upload_attachment",
659
814
  "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
815
  {
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")
816
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
817
+ taskId: z5.string().describe("The task ID or slug"),
818
+ path: z5.string().describe("Absolute path to the local file to upload"),
819
+ comment: z5.string().optional().describe("When set, also posts the attachment to the task chat with this text"),
820
+ mimeType: z5.string().optional().describe("Override the mime type inferred from the file extension")
666
821
  },
667
822
  async (params) => {
668
823
  const info = await stat(params.path).catch(() => null);
@@ -725,18 +880,18 @@ function registerAttachmentTools(server2, conn2) {
725
880
  }
726
881
 
727
882
  // src/tools/pull-request.ts
728
- import { z as z5 } from "zod";
883
+ import { z as z6 } from "zod";
729
884
  function registerPullRequestTools(server2, conn2) {
730
885
  server2.tool(
731
886
  "create_pull_request",
732
887
  "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
888
  {
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)")
889
+ projectId: z6.string().optional().describe("Target Conveyor project ID"),
890
+ taskId: z6.string().describe("The task ID whose branch should be opened as a PR"),
891
+ title: z6.string().describe("Pull request title"),
892
+ body: z6.string().describe("Pull request body (markdown)"),
893
+ head: z6.string().optional().describe("Source branch for the PR (defaults to the task's branch)"),
894
+ base: z6.string().optional().describe("Target branch for the PR (defaults to the repo default)")
740
895
  },
741
896
  async (params) => {
742
897
  const result = await conn2.createPullRequest(params);
@@ -748,7 +903,7 @@ function registerPullRequestTools(server2, conn2) {
748
903
  }
749
904
 
750
905
  // src/tools/subtasks.ts
751
- import { z as z6 } from "zod";
906
+ import { z as z7 } from "zod";
752
907
  var STATUS_ENUM2 = [
753
908
  "Planning",
754
909
  "Open",
@@ -766,15 +921,15 @@ function registerCreateSubtask(server2, conn2) {
766
921
  "create_subtask",
767
922
  "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
923
  {
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(
924
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
925
+ parentTaskId: z7.string().describe("The parent task ID"),
926
+ title: z7.string().describe("Subtask title"),
927
+ description: z7.string().optional().describe("Subtask description"),
928
+ plan: z7.string().optional().describe("Subtask implementation plan (markdown)"),
929
+ ordinal: z7.number().optional().describe("Ordering position among siblings"),
930
+ storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
931
+ followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
932
+ dependsOn: z7.array(z7.string()).optional().describe(
778
933
  "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
934
  )
780
935
  },
@@ -789,17 +944,20 @@ function registerCreateSubtask(server2, conn2) {
789
944
  function registerUpdateSubtask(server2, conn2) {
790
945
  server2.tool(
791
946
  "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.",
947
+ "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
948
  {
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)
949
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
950
+ subtaskId: z7.string().describe("The subtask ID"),
951
+ title: z7.string().optional().describe("New title"),
952
+ description: z7.string().optional().describe("New description"),
953
+ plan: z7.string().optional().describe("New plan (markdown)"),
954
+ status: z7.enum(STATUS_ENUM2).optional().describe("New status"),
955
+ ordinal: z7.number().optional().describe("New ordering position among siblings"),
956
+ storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
957
+ followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
958
+ dependsOn: z7.array(z7.string()).optional().describe(
959
+ "Replace the sibling subtask ids/slugs this subtask blocks on (pass [] to clear). Omit to leave dependencies unchanged."
960
+ )
803
961
  },
804
962
  async (params) => {
805
963
  const result = await conn2.updateSubtask(params);
@@ -816,8 +974,8 @@ function registerListSubtasks(server2, conn2) {
816
974
  "list_subtasks",
817
975
  "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
976
  {
819
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
820
- taskId: z6.string().describe("The parent task ID")
977
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
978
+ taskId: z7.string().describe("The parent task ID")
821
979
  },
822
980
  async (params) => {
823
981
  const subtasks = await conn2.listSubtasks(params.taskId, params.projectId);
@@ -830,8 +988,8 @@ function registerDeleteSubtask(server2, conn2) {
830
988
  "delete_subtask",
831
989
  "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
990
  {
833
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
834
- subtaskId: z6.string().describe("The subtask ID to delete")
991
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
992
+ subtaskId: z7.string().describe("The subtask ID to delete")
835
993
  },
836
994
  async (params) => {
837
995
  const result = await conn2.deleteSubtask(params.subtaskId, params.projectId);
@@ -851,14 +1009,14 @@ function registerSubtaskTools(server2, conn2) {
851
1009
  }
852
1010
 
853
1011
  // src/tools/dependencies.ts
854
- import { z as z7 } from "zod";
1012
+ import { z as z8 } from "zod";
855
1013
  function registerGetDependencies(server2, conn2) {
856
1014
  server2.tool(
857
1015
  "get_dependencies",
858
1016
  "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
1017
  {
860
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
861
- taskId: z7.string().describe("The task ID")
1018
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1019
+ taskId: z8.string().describe("The task ID")
862
1020
  },
863
1021
  async (params) => {
864
1022
  const deps = await conn2.getDependencies(params.taskId, params.projectId);
@@ -871,9 +1029,9 @@ function registerAddDependency(server2, conn2) {
871
1029
  "add_dependency",
872
1030
  "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
1031
  {
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")
1032
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1033
+ taskId: z8.string().describe("The task ID that will be blocked"),
1034
+ dependsOnSlugOrId: z8.string().describe("Slug or ID of the task this one depends on")
877
1035
  },
878
1036
  async (params) => {
879
1037
  await conn2.addDependency(params);
@@ -886,9 +1044,9 @@ function registerRemoveDependency(server2, conn2) {
886
1044
  "remove_dependency",
887
1045
  "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
1046
  {
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")
1047
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1048
+ taskId: z8.string().describe("The task ID to unblock"),
1049
+ dependsOnSlugOrId: z8.string().describe("Slug or ID of the dependency to remove")
892
1050
  },
893
1051
  async (params) => {
894
1052
  await conn2.removeDependency(params);
@@ -903,16 +1061,16 @@ function registerDependencyTools(server2, conn2) {
903
1061
  }
904
1062
 
905
1063
  // src/tools/suggestions.ts
906
- import { z as z8 } from "zod";
1064
+ import { z as z9 } from "zod";
907
1065
  function registerSuggestionTools(server2, conn2) {
908
1066
  server2.tool(
909
1067
  "create_suggestion",
910
1068
  "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
1069
  {
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"])')
1070
+ projectId: z9.string().optional().describe("Target Conveyor project ID"),
1071
+ title: z9.string().describe("Suggestion title"),
1072
+ description: z9.string().optional().describe("Suggestion details (markdown)"),
1073
+ tagNames: z9.array(z9.string()).optional().describe('Tag names to categorize the suggestion (e.g., ["agent-runner"])')
916
1074
  },
917
1075
  async (params) => {
918
1076
  const result = await conn2.createSuggestion(params);
@@ -923,7 +1081,7 @@ function registerSuggestionTools(server2, conn2) {
923
1081
  }
924
1082
 
925
1083
  // src/tools/checklists.ts
926
- import { z as z9 } from "zod";
1084
+ import { z as z10 } from "zod";
927
1085
  function renderManualTestGroups(groups) {
928
1086
  const lines = [];
929
1087
  for (const g of groups) {
@@ -932,7 +1090,7 @@ function renderManualTestGroups(groups) {
932
1090
  const mark = t.status === "approved" ? "\u2713" : t.status === "rejected" ? "\u2717" : "\u25CB";
933
1091
  lines.push(` ${mark} ${t.title} \u2014 ${t.status}`);
934
1092
  if (t.status === "rejected") {
935
- for (const f of t.failures) {
1093
+ for (const f of t.failures ?? []) {
936
1094
  lines.push(` \u26A0 ${f.userName ?? "Someone"}: ${f.reason ?? "(no message)"}`);
937
1095
  }
938
1096
  }
@@ -946,8 +1104,8 @@ function registerListManualTests(server2, conn2) {
946
1104
  "list_manual_tests",
947
1105
  "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
1106
  {
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>)")
1107
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1108
+ taskId: z10.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
951
1109
  },
952
1110
  async (params) => {
953
1111
  const items = await conn2.listManualTests(params.taskId, params.projectId);
@@ -973,9 +1131,9 @@ function registerSetManualTests(server2, conn2) {
973
1131
  "set_manual_tests",
974
1132
  "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
1133
  {
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")
1134
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1135
+ taskId: z10.string().describe("The task ID or slug"),
1136
+ 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
1137
  },
980
1138
  async (params) => {
981
1139
  const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
@@ -990,10 +1148,10 @@ function registerEditManualTest(server2, conn2) {
990
1148
  "edit_manual_test",
991
1149
  "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
1150
  {
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")
1151
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1152
+ taskId: z10.string().describe("The task ID or slug"),
1153
+ title: z10.string().min(1).describe("The current title of the manual test to edit"),
1154
+ newTitle: z10.string().min(1).describe("The new title for the manual test")
997
1155
  },
998
1156
  async (params) => {
999
1157
  await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
@@ -1006,9 +1164,9 @@ function registerRemoveManualTest(server2, conn2) {
1006
1164
  "remove_manual_test",
1007
1165
  "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
1166
  {
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")
1167
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1168
+ taskId: z10.string().describe("The task ID or slug"),
1169
+ title: z10.string().min(1).describe("The title of the manual test to remove")
1012
1170
  },
1013
1171
  async (params) => {
1014
1172
  await conn2.removeManualTest(params.taskId, params.title, params.projectId);
@@ -1021,9 +1179,9 @@ function registerApproveManualTest(server2, conn2) {
1021
1179
  "approve_manual_test",
1022
1180
  "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
1181
  {
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")
1182
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1183
+ taskId: z10.string().describe("The task ID or slug"),
1184
+ title: z10.string().min(1).describe("The title of the manual test to approve")
1027
1185
  },
1028
1186
  async (params) => {
1029
1187
  await conn2.approveManualTest(params.taskId, params.title, params.projectId);
@@ -1036,10 +1194,10 @@ function registerRejectManualTest(server2, conn2) {
1036
1194
  "reject_manual_test",
1037
1195
  "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
1196
  {
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")
1197
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1198
+ taskId: z10.string().describe("The task ID or slug"),
1199
+ title: z10.string().min(1).describe("The title of the manual test to reject"),
1200
+ reason: z10.string().min(1).max(2e3).describe("Why the test failed \u2014 what went wrong, shown to the team")
1043
1201
  },
1044
1202
  async (params) => {
1045
1203
  await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
@@ -1059,9 +1217,9 @@ function registerQueryManualTests(server2, conn2) {
1059
1217
  "query_manual_tests",
1060
1218
  "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
1219
  {
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")
1220
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1221
+ cardStatuses: z10.array(z10.string()).optional().describe('Filter tasks by card/column status, e.g. ["ReviewDev", "ReviewLive"]'),
1222
+ testStatuses: z10.array(z10.enum(["open", "approved", "rejected"])).optional().describe("Filter tests by status: open | approved | rejected")
1065
1223
  },
1066
1224
  async (params) => {
1067
1225
  const groups = await conn2.queryManualTests({
@@ -1087,7 +1245,7 @@ function registerChecklistTools(server2, conn2) {
1087
1245
  }
1088
1246
 
1089
1247
  // src/tools/workspace.ts
1090
- import { z as z10 } from "zod";
1248
+ import { z as z11 } from "zod";
1091
1249
 
1092
1250
  // src/workspace-ssh-tunnel.ts
1093
1251
  import net from "net";
@@ -1206,8 +1364,8 @@ function registerAttachInfoTool(server2, conn2) {
1206
1364
  "workspace_attach_info",
1207
1365
  "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
1366
  {
1209
- taskId: z10.string().describe("The task ID"),
1210
- sshPublicKey: z10.string().optional().describe("Optional OpenSSH public key to install into the workspace")
1367
+ taskId: z11.string().describe("The task ID"),
1368
+ sshPublicKey: z11.string().optional().describe("Optional OpenSSH public key to install into the workspace")
1211
1369
  },
1212
1370
  async ({ taskId, sshPublicKey }) => {
1213
1371
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1220,7 +1378,7 @@ function registerPreviewUrlsTool(server2, conn2) {
1220
1378
  "workspace_preview_urls",
1221
1379
  "Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
1222
1380
  {
1223
- taskId: z10.string().describe("The task ID")
1381
+ taskId: z11.string().describe("The task ID")
1224
1382
  },
1225
1383
  async ({ taskId }) => {
1226
1384
  const info = await conn2.getWorkspaceAttachInfo(taskId);
@@ -1249,10 +1407,10 @@ function registerStartTunnelTool(server2, conn2, startTunnel) {
1249
1407
  "workspace_start_tunnel",
1250
1408
  "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
1409
  {
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")
1410
+ taskId: z11.string().describe("The task ID"),
1411
+ port: z11.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
1412
+ preferredLocalPort: z11.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
1413
+ sshPublicKey: z11.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
1256
1414
  },
1257
1415
  async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
1258
1416
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1305,7 +1463,7 @@ function registerStopTunnelTool(server2) {
1305
1463
  "workspace_stop_tunnel",
1306
1464
  "Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
1307
1465
  {
1308
- tunnelId: z10.string().describe("Tunnel id returned by workspace_start_tunnel")
1466
+ tunnelId: z11.string().describe("Tunnel id returned by workspace_start_tunnel")
1309
1467
  },
1310
1468
  async ({ tunnelId }) => {
1311
1469
  const tunnel = activeTunnels.get(tunnelId);
@@ -1324,7 +1482,7 @@ function registerWorkspaceTools(server2, conn2, deps = {}) {
1324
1482
  }
1325
1483
 
1326
1484
  // src/tools/logs.ts
1327
- import { z as z11 } from "zod";
1485
+ import { z as z12 } from "zod";
1328
1486
  var SEVERITY_ENUM = [
1329
1487
  "DEBUG",
1330
1488
  "INFO",
@@ -1346,10 +1504,47 @@ function truncateLine(text) {
1346
1504
  function entrySource(entry) {
1347
1505
  return entry.resource.service_name ?? entry.resource.pod_name ?? entry.resource.database_id ?? entry.resourceType ?? "-";
1348
1506
  }
1507
+ var PAYLOAD_SKIP_KEYS = /* @__PURE__ */ new Set(["message", "severity", "timestamp", "level", "stack"]);
1508
+ var PAYLOAD_PRIORITY = [
1509
+ "error",
1510
+ "outcome",
1511
+ "serviceName",
1512
+ "methodName",
1513
+ "userId",
1514
+ "taskId",
1515
+ "sessionId",
1516
+ "workspaceId",
1517
+ "projectId",
1518
+ "durationMs"
1519
+ ];
1520
+ var PAYLOAD_VALUE_MAX_CHARS = 160;
1521
+ function compactPayloadValue(value) {
1522
+ const raw = typeof value === "string" ? value : JSON.stringify(value);
1523
+ const flat = (raw ?? "undefined").replace(/\s+/g, " ");
1524
+ const quoted = typeof value === "string" && /[\s"]/.test(flat) ? JSON.stringify(flat) : flat;
1525
+ return quoted.length > PAYLOAD_VALUE_MAX_CHARS ? `${quoted.slice(0, PAYLOAD_VALUE_MAX_CHARS)}\u2026` : quoted;
1526
+ }
1527
+ function formatPayloadSuffix(payloadJson) {
1528
+ if (!payloadJson) return "";
1529
+ let parsed;
1530
+ try {
1531
+ parsed = JSON.parse(payloadJson);
1532
+ } catch {
1533
+ return "";
1534
+ }
1535
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return "";
1536
+ const obj = parsed;
1537
+ const rank = (key) => {
1538
+ const i = PAYLOAD_PRIORITY.indexOf(key);
1539
+ return i === -1 ? PAYLOAD_PRIORITY.length : i;
1540
+ };
1541
+ const parts = Object.keys(obj).filter((k) => !PAYLOAD_SKIP_KEYS.has(k) && obj[k] !== void 0 && obj[k] !== null).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b)).map((k) => `${k}=${compactPayloadValue(obj[k])}`);
1542
+ return parts.length > 0 ? ` | ${parts.join(" ")}` : "";
1543
+ }
1349
1544
  function formatLogEntryLine(entry) {
1350
1545
  const httpPrefix = entry.httpRequest?.status ? `http ${entry.httpRequest.status} ${entry.httpRequest.method ?? ""} ${entry.httpRequest.url ?? ""}`.trim() + " \u2014 " : "";
1351
1546
  return `${entry.timestamp} ${entry.severity.padEnd(7)} [${entrySource(entry)}] ${truncateLine(
1352
- `${httpPrefix}${entry.message}`
1547
+ `${httpPrefix}${entry.message}${formatPayloadSuffix(entry.payload)}`
1353
1548
  )}`;
1354
1549
  }
1355
1550
  async function runQueryGcpLogs(conn2, params, now = Date.now) {
@@ -1389,27 +1584,27 @@ async function runQueryGcpLogs(conn2, params, now = Date.now) {
1389
1584
  function registerLogTools(server2, conn2) {
1390
1585
  server2.tool(
1391
1586
  "query_gcp_logs",
1392
- "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>'. 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.",
1587
+ "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.",
1393
1588
  {
1394
- projectId: z11.string().optional().describe("Target Conveyor project ID"),
1395
- env: z11.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
1396
- sinceMinutes: z11.number().int().min(1).max(10080).optional().describe(
1589
+ projectId: z12.string().optional().describe("Target Conveyor project ID"),
1590
+ env: z12.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
1591
+ sinceMinutes: z12.number().int().min(1).max(10080).optional().describe(
1397
1592
  "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
1398
1593
  ),
1399
- startTime: z11.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1400
- endTime: z11.string().optional().describe("ISO 8601 upper bound (default now)"),
1401
- severity: z11.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
1402
- services: z11.array(z11.string()).optional().describe(
1594
+ startTime: z12.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1595
+ endTime: z12.string().optional().describe("ISO 8601 upper bound (default now)"),
1596
+ severity: z12.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
1597
+ services: z12.array(z12.string()).optional().describe(
1403
1598
  "Restrict to these Cloud Run service names (prod/dev only). Defaults to all services linked in project settings."
1404
1599
  ),
1405
- sqlInstances: z11.array(z11.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
1406
- allServices: z11.boolean().optional().describe(
1600
+ sqlInstances: z12.array(z12.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
1601
+ allServices: z12.boolean().optional().describe(
1407
1602
  "Set true to search ALL logs in the GCP project, ignoring the linked-resource scope"
1408
1603
  ),
1409
- search: z11.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
1410
- filter: z11.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
1411
- limit: z11.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
1412
- pageToken: z11.string().optional().describe("Opaque token from a previous response to fetch the next page")
1604
+ search: z12.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
1605
+ filter: z12.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
1606
+ limit: z12.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
1607
+ pageToken: z12.string().optional().describe("Opaque token from a previous response to fetch the next page")
1413
1608
  },
1414
1609
  async (params) => {
1415
1610
  const text = await runQueryGcpLogs(conn2, params);
@@ -1421,6 +1616,7 @@ function registerLogTools(server2, conn2) {
1421
1616
  // src/tools/index.ts
1422
1617
  function registerAllTools(server2, conn2) {
1423
1618
  registerProjectTools(server2, conn2);
1619
+ registerProjectConfigTools(server2, conn2);
1424
1620
  registerTaskTools(server2, conn2);
1425
1621
  registerBuildTools(server2, conn2);
1426
1622
  registerAttachmentTools(server2, conn2);