@rallycry/conveyor-mcp 4.3.8 → 4.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConveyorConnection
4
- } from "./chunk-NQWURJDR.js";
4
+ } from "./chunk-KOYKRWVY.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { createRequire } from "module";
@@ -66,8 +66,50 @@ function registerProjectTools(server2, conn2) {
66
66
  );
67
67
  }
68
68
 
69
- // src/tools/project-config.ts
69
+ // src/tools/connection.ts
70
70
  import { z as z2 } from "zod";
71
+ var CAPABILITY_ENUM = ["read", "create", "update", "chat", "files", "build"];
72
+ function registerConnectionTools(server2, conn2) {
73
+ server2.tool(
74
+ "get_connection_context",
75
+ "Resolve WHO this connection is and WHAT project/board it points at \u2014 call this FIRST, before inferring anything from names. Returns the effective account, project, and board (sub-project) each with BOTH its immutable ID and its human-readable name/slug, the granted capabilities (read/create/update/chat/files/build), management URLs, and a one-line summary. Removes the ambiguity between a project's canonical name and a board label. 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) => {
80
+ const ctx = await conn2.getConnectionContext(params.projectId);
81
+ return { content: [{ type: "text", text: JSON.stringify(ctx, null, 2) }] };
82
+ }
83
+ );
84
+ server2.tool(
85
+ "verify_connection",
86
+ "Prove the connection is correct AND that you can actually write to the intended board \u2014 not just that auth works. Runs layered checks (auth \u2192 account \u2192 project \u2192 target board \u2192 capabilities \u2192 read) and returns a plain pass/fail with, on failure, the exact failing layer and ONE next action. Use this instead of get_project_summary to confirm setup: a summary that returns data proves auth, not scope. Pass intendedActions to verify specific capabilities (defaults to read+create+update). Pass projectId to target a specific project; otherwise the configured default project is used. The board scope comes from CONVEYOR_SUBPROJECT_ID.",
87
+ {
88
+ projectId: z2.string().optional().describe("Target Conveyor project ID"),
89
+ intendedActions: z2.array(z2.enum(CAPABILITY_ENUM)).optional().describe(
90
+ "Capabilities to verify the connection can perform (default: read, create, update)."
91
+ )
92
+ },
93
+ async (params) => {
94
+ const result = await conn2.verifyConnection(params);
95
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
96
+ }
97
+ );
98
+ server2.tool(
99
+ "list_accessible_subprojects",
100
+ "List the boards (sub-projects) under the connected project \u2014 each with its ID, name, slug, board URL, owned root path, and the role/capabilities this token has on it. Use this to discover which board to create or list cards on (pass a returned id as subProjectId, or set CONVEYOR_SUBPROJECT_ID) instead of asking the human for one. Pass projectId to target a specific project; otherwise the configured default project is used.",
101
+ {
102
+ projectId: z2.string().optional().describe("Target Conveyor project ID")
103
+ },
104
+ async (params) => {
105
+ const subprojects = await conn2.listAccessibleSubprojects(params.projectId);
106
+ return { content: [{ type: "text", text: JSON.stringify(subprojects, null, 2) }] };
107
+ }
108
+ );
109
+ }
110
+
111
+ // src/tools/project-config.ts
112
+ import { z as z3 } from "zod";
71
113
  function jsonResult(data) {
72
114
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
73
115
  }
@@ -85,7 +127,7 @@ function registerGetConnectUrls(server2, conn2) {
85
127
  "get_connect_urls",
86
128
  "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
129
  {
88
- projectId: z2.string().optional().describe("Target Conveyor project ID")
130
+ projectId: z3.string().optional().describe("Target Conveyor project ID")
89
131
  },
90
132
  async (params) => jsonResult(await conn2.getConnectUrls(params.projectId))
91
133
  );
@@ -95,16 +137,16 @@ function registerUpdateProjectSettings(server2, conn2) {
95
137
  "update_project_settings",
96
138
  "Update project configuration: name, description, default agent assignments, or a deep-merged patch of the project settings JSON (JSON Merge Patch: nested objects merge recursively, null deletes a key, arrays replace \u2014 a partial patch never wipes sibling keys). Requires a Moderate project role. Does NOT touch repositories or branches \u2014 those are configured in the Conveyor UI.",
97
139
  {
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(
140
+ projectId: z3.string().optional().describe("Target Conveyor project ID"),
141
+ name: z3.string().optional().describe("New project name"),
142
+ description: z3.string().optional().describe("New project description"),
143
+ settings: z3.record(z3.unknown()).optional().describe(
102
144
  "Deep-merged patch of the project settings JSON (JSON Merge Patch semantics: null deletes a key; advanced)"
103
145
  ),
104
- defaultPmAgentId: z2.string().nullable().optional().describe("Default PM agent ID"),
105
- defaultTaskAgentId: z2.string().nullable().optional().describe("Default task agent ID"),
106
- defaultReviewerAgentId: z2.string().nullable().optional().describe("Default reviewer agent ID"),
107
- helperAgentId: z2.string().nullable().optional().describe("Helper agent ID")
146
+ defaultPmAgentId: z3.string().nullable().optional().describe("Default PM agent ID"),
147
+ defaultTaskAgentId: z3.string().nullable().optional().describe("Default task agent ID"),
148
+ defaultReviewerAgentId: z3.string().nullable().optional().describe("Default reviewer agent ID"),
149
+ helperAgentId: z3.string().nullable().optional().describe("Helper agent ID")
108
150
  },
109
151
  async (params) => {
110
152
  const { projectId: projectId2, name, description, settings, ...agents } = params;
@@ -133,12 +175,12 @@ function registerManageTags(server2, conn2) {
133
175
  "manage_tags",
134
176
  "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.",
135
177
  {
136
- action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
137
- projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
138
- id: z2.string().optional().describe("Tag ID (update/delete)"),
139
- name: z2.string().optional().describe("Tag name"),
140
- color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
141
- description: z2.string().optional().describe("Tag description")
178
+ action: z3.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
179
+ projectId: z3.string().optional().describe("Target Conveyor project ID (list/create)"),
180
+ id: z3.string().optional().describe("Tag ID (update/delete)"),
181
+ name: z3.string().optional().describe("Tag name"),
182
+ color: z3.string().optional().describe("Hex color, e.g. #ff0000"),
183
+ description: z3.string().optional().describe("Tag description")
142
184
  },
143
185
  async (params) => {
144
186
  const { action, projectId: projectId2, id, name, color, description } = params;
@@ -172,13 +214,13 @@ function registerManagePriorities(server2, conn2) {
172
214
  "manage_priorities",
173
215
  "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.",
174
216
  {
175
- action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
176
- projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
177
- id: z2.string().optional().describe("Priority ID (update/delete)"),
178
- value: z2.number().int().min(1).max(100).optional().describe("Priority value (1 = highest urgency scale point)"),
179
- name: z2.string().optional().describe("Priority name"),
180
- color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
181
- description: z2.string().optional().describe("Priority description")
217
+ action: z3.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
218
+ projectId: z3.string().optional().describe("Target Conveyor project ID (list/create)"),
219
+ id: z3.string().optional().describe("Priority ID (update/delete)"),
220
+ value: z3.number().int().min(1).max(100).optional().describe("Priority value (1 = highest urgency scale point)"),
221
+ name: z3.string().optional().describe("Priority name"),
222
+ color: z3.string().optional().describe("Hex color, e.g. #ff0000"),
223
+ description: z3.string().optional().describe("Priority description")
182
224
  },
183
225
  async (params) => {
184
226
  const { action, projectId: projectId2, id, value, name, color, description } = params;
@@ -219,7 +261,7 @@ function registerProjectConfigTools(server2, conn2) {
219
261
  }
220
262
 
221
263
  // src/tools/tasks.ts
222
- import { z as z3 } from "zod";
264
+ import { z as z4 } from "zod";
223
265
  var CLI_EVENT_FORMATTERS = {
224
266
  thinking: (data) => String(data.message ?? ""),
225
267
  tool_use: (data) => `${data.tool}: ${String(data.input ?? "").slice(0, 1e3)}`,
@@ -289,22 +331,26 @@ var STATUS_ENUM = [
289
331
  ];
290
332
  var CARD_TYPE_ENUM = ["task", "incident", "suggestion"];
291
333
  var RISK_ENUM = ["critical", "high", "medium", "low"];
334
+ var BOARD_FILTER = z4.string().nullable().optional().describe(
335
+ "Filter to a sub-project board. Omit to use the connection's default board (CONVEYOR_SUBPROJECT_ID) when set, else the whole project; pass null to force the whole project. Use list_accessible_subprojects to find board IDs."
336
+ );
337
+ var BOARD_ASSIGN = z4.string().nullable().optional().describe(
338
+ "Assign the card to a sub-project board. Omit to use the connection's default board (CONVEYOR_SUBPROJECT_ID) when set, else the parent project; pass null to force the parent project. Use list_accessible_subprojects to find board IDs."
339
+ );
292
340
  function registerListTasks(server2, conn2) {
293
341
  server2.tool(
294
342
  "list_tasks",
295
343
  "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.",
296
344
  {
297
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
298
- status: z3.enum(STATUS_ENUM).optional().describe("Filter by task status"),
299
- typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
345
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
346
+ status: z4.enum(STATUS_ENUM).optional().describe("Filter by task status"),
347
+ typeFilters: z4.array(z4.enum(CARD_TYPE_ENUM)).optional().describe(
300
348
  'Card types to include, e.g. ["incident"] or ["task", "incident"]. Omit for tasks only.'
301
349
  ),
302
- assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
303
- unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
304
- subProjectId: z3.string().optional().describe(
305
- "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
306
- ),
307
- limit: z3.number().optional().describe("Max tasks to return (default 50)")
350
+ assigneeId: z4.string().optional().describe("Filter by assigned user ID"),
351
+ unassigned: z4.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
352
+ subProjectId: BOARD_FILTER,
353
+ limit: z4.number().optional().describe("Max tasks to return (default 50)")
308
354
  },
309
355
  async (params) => {
310
356
  const tasks = await conn2.listTasks(params);
@@ -319,8 +365,8 @@ function registerGetTask(server2, conn2) {
319
365
  "get_task",
320
366
  "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.",
321
367
  {
322
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
323
- taskId: z3.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
368
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
369
+ taskId: z4.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
324
370
  },
325
371
  async (params) => {
326
372
  const task = await conn2.getTask(params.taskId, params.projectId);
@@ -333,8 +379,8 @@ function registerGetCardBySlug(server2, conn2) {
333
379
  "get_card_by_slug",
334
380
  "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.",
335
381
  {
336
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
337
- slug: z3.string().describe("The card slug from a Conveyor card URL, e.g. 'ship-it'")
382
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
383
+ slug: z4.string().describe("The card slug from a Conveyor card URL, e.g. 'ship-it'")
338
384
  },
339
385
  async (params) => {
340
386
  const task = await conn2.getCardBySlug(params.slug, params.projectId);
@@ -347,19 +393,22 @@ function registerCreateTask(server2, conn2) {
347
393
  "create_task",
348
394
  "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.",
349
395
  {
350
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
351
- title: z3.string().describe("Task title"),
352
- description: z3.string().optional().describe("Task description"),
353
- plan: z3.string().optional().describe("Task implementation plan (markdown)"),
354
- status: z3.enum(["Planning", "Open"]).optional().describe("Initial status (default: Planning)"),
355
- subProjectId: z3.string().optional().describe(
356
- "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
357
- )
396
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
397
+ title: z4.string().describe("Task title"),
398
+ description: z4.string().optional().describe("Task description"),
399
+ plan: z4.string().optional().describe("Task implementation plan (markdown)"),
400
+ status: z4.enum(["Planning", "Open"]).optional().describe("Initial status (default: Planning)"),
401
+ subProjectId: BOARD_ASSIGN
358
402
  },
359
403
  async (params) => {
360
404
  const task = await conn2.createTask(params);
361
405
  return {
362
- content: [{ type: "text", text: `Task created: ${task.id} (slug: ${task.slug})` }]
406
+ content: [
407
+ {
408
+ type: "text",
409
+ text: `Task created: ${task.id} (slug: ${task.slug}). effectiveScope: ${JSON.stringify(task.effectiveScope)}`
410
+ }
411
+ ]
363
412
  };
364
413
  }
365
414
  );
@@ -369,17 +418,17 @@ function registerUpdateTask(server2, conn2) {
369
418
  "update_task",
370
419
  "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.",
371
420
  {
372
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
373
- taskId: z3.string().describe("The task ID"),
374
- title: z3.string().optional().describe("New title"),
375
- description: z3.string().optional().describe("New description"),
376
- plan: z3.string().optional().describe("New plan (markdown)"),
377
- status: z3.enum(STATUS_ENUM).optional().describe("New status"),
378
- risk: z3.enum(RISK_ENUM).nullable().optional().describe(
421
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
422
+ taskId: z4.string().describe("The task ID"),
423
+ title: z4.string().optional().describe("New title"),
424
+ description: z4.string().optional().describe("New description"),
425
+ plan: z4.string().optional().describe("New plan (markdown)"),
426
+ status: z4.enum(STATUS_ENUM).optional().describe("New status"),
427
+ risk: z4.enum(RISK_ENUM).nullable().optional().describe(
379
428
  "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
380
429
  ),
381
- assignedUserId: z3.string().nullable().optional().describe("User ID to assign, or null"),
382
- subProjectId: z3.string().nullable().optional().describe(
430
+ assignedUserId: z4.string().nullable().optional().describe("User ID to assign, or null"),
431
+ subProjectId: z4.string().nullable().optional().describe(
383
432
  "Assign the task to a sub-project board; null moves it back to the parent board. Omit to leave unchanged."
384
433
  )
385
434
  },
@@ -396,9 +445,9 @@ function registerMoveCard(server2, conn2) {
396
445
  "move_card",
397
446
  "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.",
398
447
  {
399
- projectId: z3.string().optional().describe("Source Conveyor project ID"),
400
- taskId: z3.string().describe("Card ID or slug"),
401
- destinationProjectId: z3.string().describe("Destination Conveyor project ID")
448
+ projectId: z4.string().optional().describe("Source Conveyor project ID"),
449
+ taskId: z4.string().describe("Card ID or slug"),
450
+ destinationProjectId: z4.string().describe("Destination Conveyor project ID")
402
451
  },
403
452
  async (params) => {
404
453
  const result = await conn2.moveCard(params);
@@ -419,9 +468,9 @@ function registerChatTools(server2, conn2) {
419
468
  "read_task_chat",
420
469
  "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.",
421
470
  {
422
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
423
- taskId: z3.string().describe("The task ID"),
424
- limit: z3.number().optional().describe("Max messages to return (default 50)")
471
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
472
+ taskId: z4.string().describe("The task ID"),
473
+ limit: z4.number().optional().describe("Max messages to return (default 50)")
425
474
  },
426
475
  async (params) => {
427
476
  const messages = await conn2.getTaskChat(params.taskId, params.limit, params.projectId);
@@ -432,9 +481,9 @@ function registerChatTools(server2, conn2) {
432
481
  "post_to_chat",
433
482
  "Post a message to a task's chat. Pass projectId to target a specific project; otherwise the configured default project is used.",
434
483
  {
435
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
436
- taskId: z3.string().describe("The task ID"),
437
- content: z3.string().describe("Message content")
484
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
485
+ taskId: z4.string().describe("The task ID"),
486
+ content: z4.string().describe("Message content")
438
487
  },
439
488
  async (params) => {
440
489
  await conn2.postToTaskChat(params.taskId, params.content, params.projectId);
@@ -447,12 +496,12 @@ function registerGetTaskCli(server2, conn2) {
447
496
  "get_task_logs",
448
497
  "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.",
449
498
  {
450
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
451
- taskId: z3.string().describe("The task ID or slug"),
452
- source: z3.enum(["agent", "application"]).optional().describe(
499
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
500
+ taskId: z4.string().describe("The task ID or slug"),
501
+ source: z4.enum(["agent", "application"]).optional().describe(
453
502
  "Filter by log source: 'agent' for reasoning/tool calls, 'application' for setup/dev-server output"
454
503
  ),
455
- limit: z3.number().optional().describe("Max entries to return (default 50, max 500)")
504
+ limit: z4.number().optional().describe("Max entries to return (default 50, max 500)")
456
505
  },
457
506
  async ({ taskId, source, limit, projectId: projectId2 }) => {
458
507
  const effectiveLimit = Math.min(limit ?? 50, 500);
@@ -471,8 +520,8 @@ function registerGetTaskSessions(server2, conn2) {
471
520
  "get_task_sessions",
472
521
  "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.",
473
522
  {
474
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
475
- taskId: z3.string().describe("The task ID or slug")
523
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
524
+ taskId: z4.string().describe("The task ID or slug")
476
525
  },
477
526
  async ({ taskId, projectId: projectId2 }) => {
478
527
  const tasks = await conn2.getTaskSessions(taskId, projectId2);
@@ -490,19 +539,17 @@ function registerSearchTasks(server2, conn2) {
490
539
  "search_tasks",
491
540
  "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.",
492
541
  {
493
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
494
- tagNames: z3.array(z3.string()).optional().describe('Tag names to filter by (e.g., ["agent-runner", "chat"])'),
495
- searchQuery: z3.string().optional().describe("Text search on title and description"),
496
- statusFilters: z3.array(z3.enum(STATUS_ENUM)).optional().describe("Filter by one or more statuses"),
497
- typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
542
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
543
+ tagNames: z4.array(z4.string()).optional().describe('Tag names to filter by (e.g., ["agent-runner", "chat"])'),
544
+ searchQuery: z4.string().optional().describe("Text search on title and description"),
545
+ statusFilters: z4.array(z4.enum(STATUS_ENUM)).optional().describe("Filter by one or more statuses"),
546
+ typeFilters: z4.array(z4.enum(CARD_TYPE_ENUM)).optional().describe(
498
547
  'Card types to include (default ["task"]). Pass e.g. ["incident"] or list several to search across types.'
499
548
  ),
500
- assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
501
- unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
502
- subProjectId: z3.string().optional().describe(
503
- "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
504
- ),
505
- limit: z3.number().optional().describe("Max results to return (default 20)")
549
+ assigneeId: z4.string().optional().describe("Filter by assigned user ID"),
550
+ unassigned: z4.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
551
+ subProjectId: BOARD_FILTER,
552
+ limit: z4.number().optional().describe("Max results to return (default 20)")
506
553
  },
507
554
  async (params) => {
508
555
  const tasks = await conn2.searchTasks(params);
@@ -517,7 +564,7 @@ function registerListTags(server2, conn2) {
517
564
  "list_tags",
518
565
  "List all project tags with their names, IDs, and colors. Pass projectId to target a specific project; otherwise the configured default project is used.",
519
566
  {
520
- projectId: z3.string().optional().describe("Target Conveyor project ID")
567
+ projectId: z4.string().optional().describe("Target Conveyor project ID")
521
568
  },
522
569
  async (params) => {
523
570
  const tags = await conn2.listTags(params.projectId);
@@ -530,9 +577,9 @@ function registerReviewTools(server2, conn2) {
530
577
  "approve_task",
531
578
  "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.",
532
579
  {
533
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
534
- taskId: z3.string().describe("The task ID"),
535
- risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'low' on approval.")
580
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
581
+ taskId: z4.string().describe("The task ID"),
582
+ risk: z4.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'low' on approval.")
536
583
  },
537
584
  async (params) => {
538
585
  const result = await conn2.approveTask(params.taskId, params.projectId, params.risk);
@@ -545,8 +592,8 @@ function registerReviewTools(server2, conn2) {
545
592
  "approve_and_merge_pr",
546
593
  "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.",
547
594
  {
548
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
549
- childTaskId: z3.string().describe("The child task ID whose PR should be approved and merged")
595
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
596
+ childTaskId: z4.string().describe("The child task ID whose PR should be approved and merged")
550
597
  },
551
598
  async (params) => {
552
599
  const result = await conn2.approveAndMergePR(params.childTaskId, params.projectId);
@@ -564,10 +611,10 @@ function registerReviewTools(server2, conn2) {
564
611
  "request_changes",
565
612
  "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.",
566
613
  {
567
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
568
- taskId: z3.string().describe("The task ID"),
569
- feedback: z3.string().describe("Feedback message describing requested changes"),
570
- risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'medium' on changes.")
614
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
615
+ taskId: z4.string().describe("The task ID"),
616
+ feedback: z4.string().describe("Feedback message describing requested changes"),
617
+ risk: z4.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'medium' on changes.")
571
618
  },
572
619
  async (params) => {
573
620
  await conn2.requestChanges(params.taskId, params.feedback, params.projectId, params.risk);
@@ -584,9 +631,9 @@ function registerReviewerTools(server2, conn2) {
584
631
  "add_reviewer",
585
632
  "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.",
586
633
  {
587
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
588
- taskId: z3.string().describe("The task ID or slug"),
589
- userId: z3.string().describe("User ID of the reviewer (use list_project_members to resolve a name or email)")
634
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
635
+ taskId: z4.string().describe("The task ID or slug"),
636
+ userId: z4.string().describe("User ID of the reviewer (use list_project_members to resolve a name or email)")
590
637
  },
591
638
  async (params) => {
592
639
  const result = await conn2.addReviewer(params);
@@ -604,9 +651,9 @@ function registerReviewerTools(server2, conn2) {
604
651
  "remove_reviewer",
605
652
  "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.",
606
653
  {
607
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
608
- taskId: z3.string().describe("The task ID or slug"),
609
- userId: z3.string().describe("User ID of the reviewer to remove")
654
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
655
+ taskId: z4.string().describe("The task ID or slug"),
656
+ userId: z4.string().describe("User ID of the reviewer to remove")
610
657
  },
611
658
  async (params) => {
612
659
  const result = await conn2.removeReviewer(params);
@@ -638,7 +685,7 @@ function registerTaskTools(server2, conn2) {
638
685
  }
639
686
 
640
687
  // src/tools/builds.ts
641
- import { z as z4 } from "zod";
688
+ import { z as z5 } from "zod";
642
689
  function textResult2(result) {
643
690
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
644
691
  }
@@ -647,8 +694,8 @@ function registerTaskLifecycleTools(server2, conn2) {
647
694
  "stop_task",
648
695
  "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.",
649
696
  {
650
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
651
- taskId: z4.string().describe("The task ID")
697
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
698
+ taskId: z5.string().describe("The task ID")
652
699
  },
653
700
  async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
654
701
  );
@@ -656,8 +703,8 @@ function registerTaskLifecycleTools(server2, conn2) {
656
703
  "sleep_task",
657
704
  "Sleep a task Claudespace, stopping compute while preserving durable state. Pass projectId to target a specific project; otherwise the configured default project is used.",
658
705
  {
659
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
660
- taskId: z4.string().describe("The task ID")
706
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
707
+ taskId: z5.string().describe("The task ID")
661
708
  },
662
709
  async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
663
710
  );
@@ -665,8 +712,8 @@ function registerTaskLifecycleTools(server2, conn2) {
665
712
  "resume_task",
666
713
  "Resume a sleeping task Claudespace. Pass projectId to target a specific project; otherwise the configured default project is used.",
667
714
  {
668
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
669
- taskId: z4.string().describe("The task ID")
715
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
716
+ taskId: z5.string().describe("The task ID")
670
717
  },
671
718
  async (params) => textResult2(await conn2.resumeTask(params.taskId, params.projectId))
672
719
  );
@@ -674,8 +721,8 @@ function registerTaskLifecycleTools(server2, conn2) {
674
721
  "delete_task_environment",
675
722
  "Delete a task environment, including durable Claudespace state. Pass projectId to target a specific project; otherwise the configured default project is used.",
676
723
  {
677
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
678
- taskId: z4.string().describe("The task ID")
724
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
725
+ taskId: z5.string().describe("The task ID")
679
726
  },
680
727
  async (params) => textResult2(await conn2.deleteTaskEnvironment(params.taskId, params.projectId))
681
728
  );
@@ -685,8 +732,8 @@ function registerBuildTools(server2, conn2) {
685
732
  "start_task",
686
733
  "Start a cloud build (codespace) for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
687
734
  {
688
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
689
- taskId: z4.string().describe("The task ID")
735
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
736
+ taskId: z5.string().describe("The task ID")
690
737
  },
691
738
  async (params) => {
692
739
  const result = await conn2.startBuild(params.taskId, params.projectId);
@@ -698,8 +745,8 @@ function registerBuildTools(server2, conn2) {
698
745
  "create_release",
699
746
  "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).",
700
747
  {
701
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
702
- taskIds: z4.array(z4.string()).optional().describe(
748
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
749
+ taskIds: z5.array(z5.string()).optional().describe(
703
750
  "Task IDs in Review (Dev) to cherry-pick into the release. Omit to release all of them."
704
751
  )
705
752
  },
@@ -712,8 +759,8 @@ function registerBuildTools(server2, conn2) {
712
759
  "get_build_status",
713
760
  "Check codespace and agent status for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
714
761
  {
715
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
716
- taskId: z4.string().describe("The task ID")
762
+ projectId: z5.string().optional().describe("Target Conveyor project ID"),
763
+ taskId: z5.string().describe("The task ID")
717
764
  },
718
765
  async (params) => {
719
766
  const status = await conn2.getBuildStatus(params.taskId, params.projectId);
@@ -725,7 +772,7 @@ function registerBuildTools(server2, conn2) {
725
772
  // src/tools/attachments.ts
726
773
  import { readFile, stat } from "fs/promises";
727
774
  import { basename, extname } from "path";
728
- import { z as z5 } from "zod";
775
+ import { z as z6 } from "zod";
729
776
  var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
730
777
  var MIME_BY_EXT = {
731
778
  ".png": "image/png",
@@ -762,8 +809,8 @@ function registerListTaskFiles(server2, conn2) {
762
809
  "list_task_files",
763
810
  "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.",
764
811
  {
765
- projectId: z5.string().optional().describe("Target Conveyor project ID"),
766
- taskId: z5.string().describe("The task ID or slug")
812
+ projectId: z6.string().optional().describe("Target Conveyor project ID"),
813
+ taskId: z6.string().describe("The task ID or slug")
767
814
  },
768
815
  async (params) => {
769
816
  const files = await conn2.listTaskFiles(params.taskId, params.projectId);
@@ -805,11 +852,11 @@ function registerGetAttachment(server2, conn2) {
805
852
  "get_attachment",
806
853
  "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.",
807
854
  {
808
- projectId: z5.string().optional().describe("Target Conveyor project ID"),
809
- taskId: z5.string().describe("The task ID or slug"),
810
- fileId: z5.string().describe("The file ID to fetch"),
811
- offset: z5.number().int().nonnegative().optional().describe("Byte offset into text content (paging). Default 0."),
812
- maxBytes: z5.number().int().positive().optional().describe("Max bytes of text content to return from offset.")
855
+ projectId: z6.string().optional().describe("Target Conveyor project ID"),
856
+ taskId: z6.string().describe("The task ID or slug"),
857
+ fileId: z6.string().describe("The file ID to fetch"),
858
+ offset: z6.number().int().nonnegative().optional().describe("Byte offset into text content (paging). Default 0."),
859
+ maxBytes: z6.number().int().positive().optional().describe("Max bytes of text content to return from offset.")
813
860
  },
814
861
  async (params) => {
815
862
  const file = await conn2.getAttachment(params.taskId, params.fileId, {
@@ -826,11 +873,11 @@ function registerUploadAttachment(server2, conn2) {
826
873
  "upload_attachment",
827
874
  "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.",
828
875
  {
829
- projectId: z5.string().optional().describe("Target Conveyor project ID"),
830
- taskId: z5.string().describe("The task ID or slug"),
831
- path: z5.string().describe("Absolute path to the local file to upload"),
832
- comment: z5.string().optional().describe("When set, also posts the attachment to the task chat with this text"),
833
- mimeType: z5.string().optional().describe("Override the mime type inferred from the file extension")
876
+ projectId: z6.string().optional().describe("Target Conveyor project ID"),
877
+ taskId: z6.string().describe("The task ID or slug"),
878
+ path: z6.string().describe("Absolute path to the local file to upload"),
879
+ comment: z6.string().optional().describe("When set, also posts the attachment to the task chat with this text"),
880
+ mimeType: z6.string().optional().describe("Override the mime type inferred from the file extension")
834
881
  },
835
882
  async (params) => {
836
883
  const info = await stat(params.path).catch(() => null);
@@ -893,18 +940,18 @@ function registerAttachmentTools(server2, conn2) {
893
940
  }
894
941
 
895
942
  // src/tools/pull-request.ts
896
- import { z as z6 } from "zod";
943
+ import { z as z7 } from "zod";
897
944
  function registerPullRequestTools(server2, conn2) {
898
945
  server2.tool(
899
946
  "create_pull_request",
900
947
  "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.",
901
948
  {
902
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
903
- taskId: z6.string().describe("The task ID whose branch should be opened as a PR"),
904
- title: z6.string().describe("Pull request title"),
905
- body: z6.string().describe("Pull request body (markdown)"),
906
- head: z6.string().optional().describe("Source branch for the PR (defaults to the task's branch)"),
907
- base: z6.string().optional().describe("Target branch for the PR (defaults to the repo default)")
949
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
950
+ taskId: z7.string().describe("The task ID whose branch should be opened as a PR"),
951
+ title: z7.string().describe("Pull request title"),
952
+ body: z7.string().describe("Pull request body (markdown)"),
953
+ head: z7.string().optional().describe("Source branch for the PR (defaults to the task's branch)"),
954
+ base: z7.string().optional().describe("Target branch for the PR (defaults to the repo default)")
908
955
  },
909
956
  async (params) => {
910
957
  const result = await conn2.createPullRequest(params);
@@ -916,7 +963,7 @@ function registerPullRequestTools(server2, conn2) {
916
963
  }
917
964
 
918
965
  // src/tools/subtasks.ts
919
- import { z as z7 } from "zod";
966
+ import { z as z8 } from "zod";
920
967
  var STATUS_ENUM2 = [
921
968
  "Planning",
922
969
  "Open",
@@ -934,15 +981,15 @@ function registerCreateSubtask(server2, conn2) {
934
981
  "create_subtask",
935
982
  "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.",
936
983
  {
937
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
938
- parentTaskId: z7.string().describe("The parent task ID"),
939
- title: z7.string().describe("Subtask title"),
940
- description: z7.string().optional().describe("Subtask description"),
941
- plan: z7.string().optional().describe("Subtask implementation plan (markdown)"),
942
- ordinal: z7.number().optional().describe("Ordering position among siblings"),
943
- storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
944
- followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
945
- dependsOn: z7.array(z7.string()).optional().describe(
984
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
985
+ parentTaskId: z8.string().describe("The parent task ID"),
986
+ title: z8.string().describe("Subtask title"),
987
+ description: z8.string().optional().describe("Subtask description"),
988
+ plan: z8.string().optional().describe("Subtask implementation plan (markdown)"),
989
+ ordinal: z8.number().optional().describe("Ordering position among siblings"),
990
+ storyPointValue: z8.number().optional().describe(SP_DESCRIPTION),
991
+ followParentStatus: z8.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
992
+ dependsOn: z8.array(z8.string()).optional().describe(
946
993
  "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."
947
994
  )
948
995
  },
@@ -959,16 +1006,16 @@ function registerUpdateSubtask(server2, conn2) {
959
1006
  "update_subtask",
960
1007
  "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.",
961
1008
  {
962
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
963
- subtaskId: z7.string().describe("The subtask ID"),
964
- title: z7.string().optional().describe("New title"),
965
- description: z7.string().optional().describe("New description"),
966
- plan: z7.string().optional().describe("New plan (markdown)"),
967
- status: z7.enum(STATUS_ENUM2).optional().describe("New status"),
968
- ordinal: z7.number().optional().describe("New ordering position among siblings"),
969
- storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
970
- followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
971
- dependsOn: z7.array(z7.string()).optional().describe(
1009
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1010
+ subtaskId: z8.string().describe("The subtask ID"),
1011
+ title: z8.string().optional().describe("New title"),
1012
+ description: z8.string().optional().describe("New description"),
1013
+ plan: z8.string().optional().describe("New plan (markdown)"),
1014
+ status: z8.enum(STATUS_ENUM2).optional().describe("New status"),
1015
+ ordinal: z8.number().optional().describe("New ordering position among siblings"),
1016
+ storyPointValue: z8.number().optional().describe(SP_DESCRIPTION),
1017
+ followParentStatus: z8.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
1018
+ dependsOn: z8.array(z8.string()).optional().describe(
972
1019
  "Replace the sibling subtask ids/slugs this subtask blocks on (pass [] to clear). Omit to leave dependencies unchanged."
973
1020
  )
974
1021
  },
@@ -987,8 +1034,8 @@ function registerListSubtasks(server2, conn2) {
987
1034
  "list_subtasks",
988
1035
  "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.",
989
1036
  {
990
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
991
- taskId: z7.string().describe("The parent task ID")
1037
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1038
+ taskId: z8.string().describe("The parent task ID")
992
1039
  },
993
1040
  async (params) => {
994
1041
  const subtasks = await conn2.listSubtasks(params.taskId, params.projectId);
@@ -1001,8 +1048,8 @@ function registerDeleteSubtask(server2, conn2) {
1001
1048
  "delete_subtask",
1002
1049
  "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.",
1003
1050
  {
1004
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
1005
- subtaskId: z7.string().describe("The subtask ID to delete")
1051
+ projectId: z8.string().optional().describe("Target Conveyor project ID"),
1052
+ subtaskId: z8.string().describe("The subtask ID to delete")
1006
1053
  },
1007
1054
  async (params) => {
1008
1055
  const result = await conn2.deleteSubtask(params.subtaskId, params.projectId);
@@ -1022,14 +1069,14 @@ function registerSubtaskTools(server2, conn2) {
1022
1069
  }
1023
1070
 
1024
1071
  // src/tools/dependencies.ts
1025
- import { z as z8 } from "zod";
1072
+ import { z as z9 } from "zod";
1026
1073
  function registerGetDependencies(server2, conn2) {
1027
1074
  server2.tool(
1028
1075
  "get_dependencies",
1029
1076
  "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.",
1030
1077
  {
1031
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
1032
- taskId: z8.string().describe("The task ID")
1078
+ projectId: z9.string().optional().describe("Target Conveyor project ID"),
1079
+ taskId: z9.string().describe("The task ID")
1033
1080
  },
1034
1081
  async (params) => {
1035
1082
  const deps = await conn2.getDependencies(params.taskId, params.projectId);
@@ -1042,9 +1089,9 @@ function registerAddDependency(server2, conn2) {
1042
1089
  "add_dependency",
1043
1090
  "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.",
1044
1091
  {
1045
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
1046
- taskId: z8.string().describe("The task ID that will be blocked"),
1047
- dependsOnSlugOrId: z8.string().describe("Slug or ID of the task this one depends on")
1092
+ projectId: z9.string().optional().describe("Target Conveyor project ID"),
1093
+ taskId: z9.string().describe("The task ID that will be blocked"),
1094
+ dependsOnSlugOrId: z9.string().describe("Slug or ID of the task this one depends on")
1048
1095
  },
1049
1096
  async (params) => {
1050
1097
  await conn2.addDependency(params);
@@ -1057,9 +1104,9 @@ function registerRemoveDependency(server2, conn2) {
1057
1104
  "remove_dependency",
1058
1105
  "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.",
1059
1106
  {
1060
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
1061
- taskId: z8.string().describe("The task ID to unblock"),
1062
- dependsOnSlugOrId: z8.string().describe("Slug or ID of the dependency to remove")
1107
+ projectId: z9.string().optional().describe("Target Conveyor project ID"),
1108
+ taskId: z9.string().describe("The task ID to unblock"),
1109
+ dependsOnSlugOrId: z9.string().describe("Slug or ID of the dependency to remove")
1063
1110
  },
1064
1111
  async (params) => {
1065
1112
  await conn2.removeDependency(params);
@@ -1074,16 +1121,16 @@ function registerDependencyTools(server2, conn2) {
1074
1121
  }
1075
1122
 
1076
1123
  // src/tools/suggestions.ts
1077
- import { z as z9 } from "zod";
1124
+ import { z as z10 } from "zod";
1078
1125
  function registerSuggestionTools(server2, conn2) {
1079
1126
  server2.tool(
1080
1127
  "create_suggestion",
1081
1128
  "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.",
1082
1129
  {
1083
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
1084
- title: z9.string().describe("Suggestion title"),
1085
- description: z9.string().optional().describe("Suggestion details (markdown)"),
1086
- tagNames: z9.array(z9.string()).optional().describe('Tag names to categorize the suggestion (e.g., ["agent-runner"])')
1130
+ projectId: z10.string().optional().describe("Target Conveyor project ID"),
1131
+ title: z10.string().describe("Suggestion title"),
1132
+ description: z10.string().optional().describe("Suggestion details (markdown)"),
1133
+ tagNames: z10.array(z10.string()).optional().describe('Tag names to categorize the suggestion (e.g., ["agent-runner"])')
1087
1134
  },
1088
1135
  async (params) => {
1089
1136
  const result = await conn2.createSuggestion(params);
@@ -1094,7 +1141,7 @@ function registerSuggestionTools(server2, conn2) {
1094
1141
  }
1095
1142
 
1096
1143
  // src/tools/checklists.ts
1097
- import { z as z10 } from "zod";
1144
+ import { z as z11 } from "zod";
1098
1145
  function renderManualTestGroups(groups) {
1099
1146
  const lines = [];
1100
1147
  for (const g of groups) {
@@ -1117,8 +1164,8 @@ function registerListManualTests(server2, conn2) {
1117
1164
  "list_manual_tests",
1118
1165
  "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.",
1119
1166
  {
1120
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1121
- taskId: z10.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
1167
+ projectId: z11.string().optional().describe("Target Conveyor project ID"),
1168
+ taskId: z11.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
1122
1169
  },
1123
1170
  async (params) => {
1124
1171
  const items = await conn2.listManualTests(params.taskId, params.projectId);
@@ -1144,9 +1191,9 @@ function registerSetManualTests(server2, conn2) {
1144
1191
  "set_manual_tests",
1145
1192
  "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.",
1146
1193
  {
1147
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1148
- taskId: z10.string().describe("The task ID or slug"),
1149
- 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")
1194
+ projectId: z11.string().optional().describe("Target Conveyor project ID"),
1195
+ taskId: z11.string().describe("The task ID or slug"),
1196
+ items: z11.array(z11.object({ title: z11.string().min(1).describe("A concise, actionable test step") })).min(1).describe("List of manual test steps to add")
1150
1197
  },
1151
1198
  async (params) => {
1152
1199
  const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
@@ -1161,10 +1208,10 @@ function registerEditManualTest(server2, conn2) {
1161
1208
  "edit_manual_test",
1162
1209
  "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.",
1163
1210
  {
1164
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1165
- taskId: z10.string().describe("The task ID or slug"),
1166
- title: z10.string().min(1).describe("The current title of the manual test to edit"),
1167
- newTitle: z10.string().min(1).describe("The new title for the manual test")
1211
+ projectId: z11.string().optional().describe("Target Conveyor project ID"),
1212
+ taskId: z11.string().describe("The task ID or slug"),
1213
+ title: z11.string().min(1).describe("The current title of the manual test to edit"),
1214
+ newTitle: z11.string().min(1).describe("The new title for the manual test")
1168
1215
  },
1169
1216
  async (params) => {
1170
1217
  await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
@@ -1177,9 +1224,9 @@ function registerRemoveManualTest(server2, conn2) {
1177
1224
  "remove_manual_test",
1178
1225
  "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).",
1179
1226
  {
1180
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1181
- taskId: z10.string().describe("The task ID or slug"),
1182
- title: z10.string().min(1).describe("The title of the manual test to remove")
1227
+ projectId: z11.string().optional().describe("Target Conveyor project ID"),
1228
+ taskId: z11.string().describe("The task ID or slug"),
1229
+ title: z11.string().min(1).describe("The title of the manual test to remove")
1183
1230
  },
1184
1231
  async (params) => {
1185
1232
  await conn2.removeManualTest(params.taskId, params.title, params.projectId);
@@ -1192,9 +1239,9 @@ function registerApproveManualTest(server2, conn2) {
1192
1239
  "approve_manual_test",
1193
1240
  "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.",
1194
1241
  {
1195
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1196
- taskId: z10.string().describe("The task ID or slug"),
1197
- title: z10.string().min(1).describe("The title of the manual test to approve")
1242
+ projectId: z11.string().optional().describe("Target Conveyor project ID"),
1243
+ taskId: z11.string().describe("The task ID or slug"),
1244
+ title: z11.string().min(1).describe("The title of the manual test to approve")
1198
1245
  },
1199
1246
  async (params) => {
1200
1247
  await conn2.approveManualTest(params.taskId, params.title, params.projectId);
@@ -1207,10 +1254,10 @@ function registerRejectManualTest(server2, conn2) {
1207
1254
  "reject_manual_test",
1208
1255
  "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.",
1209
1256
  {
1210
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1211
- taskId: z10.string().describe("The task ID or slug"),
1212
- title: z10.string().min(1).describe("The title of the manual test to reject"),
1213
- reason: z10.string().min(1).max(2e3).describe("Why the test failed \u2014 what went wrong, shown to the team")
1257
+ projectId: z11.string().optional().describe("Target Conveyor project ID"),
1258
+ taskId: z11.string().describe("The task ID or slug"),
1259
+ title: z11.string().min(1).describe("The title of the manual test to reject"),
1260
+ reason: z11.string().min(1).max(2e3).describe("Why the test failed \u2014 what went wrong, shown to the team")
1214
1261
  },
1215
1262
  async (params) => {
1216
1263
  await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
@@ -1230,9 +1277,9 @@ function registerQueryManualTests(server2, conn2) {
1230
1277
  "query_manual_tests",
1231
1278
  "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.",
1232
1279
  {
1233
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1234
- cardStatuses: z10.array(z10.string()).optional().describe('Filter tasks by card/column status, e.g. ["ReviewDev", "ReviewLive"]'),
1235
- testStatuses: z10.array(z10.enum(["open", "approved", "rejected"])).optional().describe("Filter tests by status: open | approved | rejected")
1280
+ projectId: z11.string().optional().describe("Target Conveyor project ID"),
1281
+ cardStatuses: z11.array(z11.string()).optional().describe('Filter tasks by card/column status, e.g. ["ReviewDev", "ReviewLive"]'),
1282
+ testStatuses: z11.array(z11.enum(["open", "approved", "rejected"])).optional().describe("Filter tests by status: open | approved | rejected")
1236
1283
  },
1237
1284
  async (params) => {
1238
1285
  const groups = await conn2.queryManualTests({
@@ -1258,7 +1305,7 @@ function registerChecklistTools(server2, conn2) {
1258
1305
  }
1259
1306
 
1260
1307
  // src/tools/workspace.ts
1261
- import { z as z11 } from "zod";
1308
+ import { z as z12 } from "zod";
1262
1309
 
1263
1310
  // src/workspace-ssh-tunnel.ts
1264
1311
  import net from "net";
@@ -1377,8 +1424,8 @@ function registerAttachInfoTool(server2, conn2) {
1377
1424
  "workspace_attach_info",
1378
1425
  "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.",
1379
1426
  {
1380
- taskId: z11.string().describe("The task ID"),
1381
- sshPublicKey: z11.string().optional().describe("Optional OpenSSH public key to install into the workspace")
1427
+ taskId: z12.string().describe("The task ID"),
1428
+ sshPublicKey: z12.string().optional().describe("Optional OpenSSH public key to install into the workspace")
1382
1429
  },
1383
1430
  async ({ taskId, sshPublicKey }) => {
1384
1431
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1391,7 +1438,7 @@ function registerPreviewUrlsTool(server2, conn2) {
1391
1438
  "workspace_preview_urls",
1392
1439
  "Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
1393
1440
  {
1394
- taskId: z11.string().describe("The task ID")
1441
+ taskId: z12.string().describe("The task ID")
1395
1442
  },
1396
1443
  async ({ taskId }) => {
1397
1444
  const info = await conn2.getWorkspaceAttachInfo(taskId);
@@ -1420,10 +1467,10 @@ function registerStartTunnelTool(server2, conn2, startTunnel) {
1420
1467
  "workspace_start_tunnel",
1421
1468
  "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.",
1422
1469
  {
1423
- taskId: z11.string().describe("The task ID"),
1424
- port: z11.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
1425
- preferredLocalPort: z11.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
1426
- sshPublicKey: z11.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
1470
+ taskId: z12.string().describe("The task ID"),
1471
+ port: z12.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
1472
+ preferredLocalPort: z12.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
1473
+ sshPublicKey: z12.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
1427
1474
  },
1428
1475
  async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
1429
1476
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1476,7 +1523,7 @@ function registerStopTunnelTool(server2) {
1476
1523
  "workspace_stop_tunnel",
1477
1524
  "Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
1478
1525
  {
1479
- tunnelId: z11.string().describe("Tunnel id returned by workspace_start_tunnel")
1526
+ tunnelId: z12.string().describe("Tunnel id returned by workspace_start_tunnel")
1480
1527
  },
1481
1528
  async ({ tunnelId }) => {
1482
1529
  const tunnel = activeTunnels.get(tunnelId);
@@ -1495,7 +1542,7 @@ function registerWorkspaceTools(server2, conn2, deps = {}) {
1495
1542
  }
1496
1543
 
1497
1544
  // src/tools/logs.ts
1498
- import { z as z12 } from "zod";
1545
+ import { z as z13 } from "zod";
1499
1546
  var SEVERITY_ENUM = [
1500
1547
  "DEBUG",
1501
1548
  "INFO",
@@ -1630,18 +1677,18 @@ function registerGrafanaLogTool(server2, conn2) {
1630
1677
  "query_grafana_logs",
1631
1678
  "Query the project's connected Grafana (Loki) logs \u2014 the application logs shipped to Grafana Cloud/Loki, complementing query_gcp_logs (GCP infrastructure logs). Start with structured filters (env, level=error, sinceMinutes=60, services), then narrow with search; pass raw LogQL via logql only when structured filters can't express the query (it REPLACES them). The response header echoes the composed LogQL \u2014 iterate on it. Returns compact lines: '<time> <SEVERITY> [<service>] <message>'.",
1632
1679
  {
1633
- projectId: z12.string().optional().describe("Target Conveyor project ID"),
1634
- env: z12.enum(["prod", "dev"]).optional().describe("Configured Grafana env mapping to scope by (default prod)"),
1635
- sinceMinutes: z12.number().int().min(1).max(10080).optional().describe(
1680
+ projectId: z13.string().optional().describe("Target Conveyor project ID"),
1681
+ env: z13.enum(["prod", "dev"]).optional().describe("Configured Grafana env mapping to scope by (default prod)"),
1682
+ sinceMinutes: z13.number().int().min(1).max(10080).optional().describe(
1636
1683
  "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
1637
1684
  ),
1638
- startTime: z12.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1639
- endTime: z12.string().optional().describe("ISO 8601 upper bound (default now)"),
1640
- level: z12.enum(["debug", "info", "warn", "error", "fatal"]).optional().describe("Minimum severity, inclusive \u2014 error returns error and above"),
1641
- services: z12.array(z12.string()).optional().describe("Restrict to these service_name label values"),
1642
- search: z12.string().max(256).optional().describe("Substring line filter (exact substring, not regex)"),
1643
- logql: z12.string().max(2e3).optional().describe("Advanced: raw LogQL query \u2014 REPLACES env/services/level/search composition"),
1644
- limit: z12.number().int().min(1).max(200).optional().describe("Max entries (default 50)")
1685
+ startTime: z13.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1686
+ endTime: z13.string().optional().describe("ISO 8601 upper bound (default now)"),
1687
+ level: z13.enum(["debug", "info", "warn", "error", "fatal"]).optional().describe("Minimum severity, inclusive \u2014 error returns error and above"),
1688
+ services: z13.array(z13.string()).optional().describe("Restrict to these service_name label values"),
1689
+ search: z13.string().max(256).optional().describe("Substring line filter (exact substring, not regex)"),
1690
+ logql: z13.string().max(2e3).optional().describe("Advanced: raw LogQL query \u2014 REPLACES env/services/level/search composition"),
1691
+ limit: z13.number().int().min(1).max(200).optional().describe("Max entries (default 50)")
1645
1692
  },
1646
1693
  async (params) => {
1647
1694
  const text = await runQueryGrafanaLogs(conn2, params);
@@ -1654,25 +1701,25 @@ function registerLogTools(server2, conn2) {
1654
1701
  "query_gcp_logs",
1655
1702
  "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.",
1656
1703
  {
1657
- projectId: z12.string().optional().describe("Target Conveyor project ID"),
1658
- env: z12.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
1659
- sinceMinutes: z12.number().int().min(1).max(10080).optional().describe(
1704
+ projectId: z13.string().optional().describe("Target Conveyor project ID"),
1705
+ env: z13.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
1706
+ sinceMinutes: z13.number().int().min(1).max(10080).optional().describe(
1660
1707
  "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
1661
1708
  ),
1662
- startTime: z12.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1663
- endTime: z12.string().optional().describe("ISO 8601 upper bound (default now)"),
1664
- severity: z12.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
1665
- services: z12.array(z12.string()).optional().describe(
1709
+ startTime: z13.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1710
+ endTime: z13.string().optional().describe("ISO 8601 upper bound (default now)"),
1711
+ severity: z13.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
1712
+ services: z13.array(z13.string()).optional().describe(
1666
1713
  "Restrict to these Cloud Run service names (prod/dev only). Defaults to all services linked in project settings."
1667
1714
  ),
1668
- sqlInstances: z12.array(z12.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
1669
- allServices: z12.boolean().optional().describe(
1715
+ sqlInstances: z13.array(z13.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
1716
+ allServices: z13.boolean().optional().describe(
1670
1717
  "Set true to search ALL logs in the GCP project, ignoring the linked-resource scope"
1671
1718
  ),
1672
- search: z12.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
1673
- filter: z12.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
1674
- limit: z12.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
1675
- pageToken: z12.string().optional().describe("Opaque token from a previous response to fetch the next page")
1719
+ search: z13.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
1720
+ filter: z13.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
1721
+ limit: z13.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
1722
+ pageToken: z13.string().optional().describe("Opaque token from a previous response to fetch the next page")
1676
1723
  },
1677
1724
  async (params) => {
1678
1725
  const text = await runQueryGcpLogs(conn2, params);
@@ -1685,6 +1732,7 @@ function registerLogTools(server2, conn2) {
1685
1732
  // src/tools/index.ts
1686
1733
  function registerAllTools(server2, conn2) {
1687
1734
  registerProjectTools(server2, conn2);
1735
+ registerConnectionTools(server2, conn2);
1688
1736
  registerProjectConfigTools(server2, conn2);
1689
1737
  registerTaskTools(server2, conn2);
1690
1738
  registerBuildTools(server2, conn2);
@@ -1703,13 +1751,14 @@ var { version } = createRequire(import.meta.url)("../package.json");
1703
1751
  var apiUrl = process.env.CONVEYOR_API_URL;
1704
1752
  var projectToken = process.env.CONVEYOR_USER_TOKEN ?? process.env.CONVEYOR_PROJECT_TOKEN;
1705
1753
  var projectId = process.env.CONVEYOR_PROJECT_ID;
1754
+ var subProjectId = process.env.CONVEYOR_SUBPROJECT_ID;
1706
1755
  if (!apiUrl || !projectToken) {
1707
1756
  process.stderr.write(
1708
1757
  "Error: CONVEYOR_API_URL and CONVEYOR_USER_TOKEN or CONVEYOR_PROJECT_TOKEN environment variables are required. CONVEYOR_PROJECT_ID is optional and sets the default project.\n"
1709
1758
  );
1710
1759
  process.exit(1);
1711
1760
  }
1712
- var conn = new ConveyorConnection({ apiUrl, projectToken, projectId });
1761
+ var conn = new ConveyorConnection({ apiUrl, projectToken, projectId, subProjectId });
1713
1762
  try {
1714
1763
  await conn.connect();
1715
1764
  process.stderr.write("Connected to Conveyor API\n");