@rallycry/conveyor-mcp 4.3.7 → 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-JSQLLIYJ.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
  );
@@ -93,16 +135,18 @@ function registerGetConnectUrls(server2, conn2) {
93
135
  function registerUpdateProjectSettings(server2, conn2) {
94
136
  server2.tool(
95
137
  "update_project_settings",
96
- "Update project configuration: name, description, default agent assignments, or a shallow-merged patch of the project settings JSON (e.g. compute tier keys). Requires a Moderate project role. Does NOT touch repositories or branches \u2014 those are configured in the Conveyor UI.",
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("Shallow-merged patch of the project settings JSON (advanced)"),
102
- defaultPmAgentId: z2.string().nullable().optional().describe("Default PM agent ID"),
103
- defaultTaskAgentId: z2.string().nullable().optional().describe("Default task agent ID"),
104
- defaultReviewerAgentId: z2.string().nullable().optional().describe("Default reviewer agent ID"),
105
- helperAgentId: z2.string().nullable().optional().describe("Helper agent ID")
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(
144
+ "Deep-merged patch of the project settings JSON (JSON Merge Patch semantics: null deletes a key; advanced)"
145
+ ),
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")
106
150
  },
107
151
  async (params) => {
108
152
  const { projectId: projectId2, name, description, settings, ...agents } = params;
@@ -131,12 +175,12 @@ function registerManageTags(server2, conn2) {
131
175
  "manage_tags",
132
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.",
133
177
  {
134
- action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
135
- projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
136
- id: z2.string().optional().describe("Tag ID (update/delete)"),
137
- name: z2.string().optional().describe("Tag name"),
138
- color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
139
- description: z2.string().optional().describe("Tag description")
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")
140
184
  },
141
185
  async (params) => {
142
186
  const { action, projectId: projectId2, id, name, color, description } = params;
@@ -170,13 +214,13 @@ function registerManagePriorities(server2, conn2) {
170
214
  "manage_priorities",
171
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.",
172
216
  {
173
- action: z2.enum(["list", "create", "update", "delete"]).describe("Operation to perform"),
174
- projectId: z2.string().optional().describe("Target Conveyor project ID (list/create)"),
175
- id: z2.string().optional().describe("Priority ID (update/delete)"),
176
- value: z2.number().int().min(1).max(100).optional().describe("Priority value (1 = highest urgency scale point)"),
177
- name: z2.string().optional().describe("Priority name"),
178
- color: z2.string().optional().describe("Hex color, e.g. #ff0000"),
179
- description: z2.string().optional().describe("Priority description")
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")
180
224
  },
181
225
  async (params) => {
182
226
  const { action, projectId: projectId2, id, value, name, color, description } = params;
@@ -217,7 +261,7 @@ function registerProjectConfigTools(server2, conn2) {
217
261
  }
218
262
 
219
263
  // src/tools/tasks.ts
220
- import { z as z3 } from "zod";
264
+ import { z as z4 } from "zod";
221
265
  var CLI_EVENT_FORMATTERS = {
222
266
  thinking: (data) => String(data.message ?? ""),
223
267
  tool_use: (data) => `${data.tool}: ${String(data.input ?? "").slice(0, 1e3)}`,
@@ -287,22 +331,26 @@ var STATUS_ENUM = [
287
331
  ];
288
332
  var CARD_TYPE_ENUM = ["task", "incident", "suggestion"];
289
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
+ );
290
340
  function registerListTasks(server2, conn2) {
291
341
  server2.tool(
292
342
  "list_tasks",
293
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.",
294
344
  {
295
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
296
- status: z3.enum(STATUS_ENUM).optional().describe("Filter by task status"),
297
- typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
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(
298
348
  'Card types to include, e.g. ["incident"] or ["task", "incident"]. Omit for tasks only.'
299
349
  ),
300
- assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
301
- unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
302
- subProjectId: z3.string().optional().describe(
303
- "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
304
- ),
305
- 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)")
306
354
  },
307
355
  async (params) => {
308
356
  const tasks = await conn2.listTasks(params);
@@ -317,8 +365,8 @@ function registerGetTask(server2, conn2) {
317
365
  "get_task",
318
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.",
319
367
  {
320
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
321
- taskId: z3.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
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>)")
322
370
  },
323
371
  async (params) => {
324
372
  const task = await conn2.getTask(params.taskId, params.projectId);
@@ -331,8 +379,8 @@ function registerGetCardBySlug(server2, conn2) {
331
379
  "get_card_by_slug",
332
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.",
333
381
  {
334
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
335
- slug: z3.string().describe("The card slug from a Conveyor card URL, e.g. 'ship-it'")
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'")
336
384
  },
337
385
  async (params) => {
338
386
  const task = await conn2.getCardBySlug(params.slug, params.projectId);
@@ -345,19 +393,22 @@ function registerCreateTask(server2, conn2) {
345
393
  "create_task",
346
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.",
347
395
  {
348
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
349
- title: z3.string().describe("Task title"),
350
- description: z3.string().optional().describe("Task description"),
351
- plan: z3.string().optional().describe("Task implementation plan (markdown)"),
352
- status: z3.enum(["Planning", "Open"]).optional().describe("Initial status (default: Planning)"),
353
- subProjectId: z3.string().optional().describe(
354
- "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
355
- )
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
356
402
  },
357
403
  async (params) => {
358
404
  const task = await conn2.createTask(params);
359
405
  return {
360
- 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
+ ]
361
412
  };
362
413
  }
363
414
  );
@@ -367,17 +418,17 @@ function registerUpdateTask(server2, conn2) {
367
418
  "update_task",
368
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.",
369
420
  {
370
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
371
- taskId: z3.string().describe("The task ID"),
372
- title: z3.string().optional().describe("New title"),
373
- description: z3.string().optional().describe("New description"),
374
- plan: z3.string().optional().describe("New plan (markdown)"),
375
- status: z3.enum(STATUS_ENUM).optional().describe("New status"),
376
- risk: z3.enum(RISK_ENUM).nullable().optional().describe(
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(
377
428
  "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
378
429
  ),
379
- assignedUserId: z3.string().nullable().optional().describe("User ID to assign, or null"),
380
- 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(
381
432
  "Assign the task to a sub-project board; null moves it back to the parent board. Omit to leave unchanged."
382
433
  )
383
434
  },
@@ -394,9 +445,9 @@ function registerMoveCard(server2, conn2) {
394
445
  "move_card",
395
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.",
396
447
  {
397
- projectId: z3.string().optional().describe("Source Conveyor project ID"),
398
- taskId: z3.string().describe("Card ID or slug"),
399
- destinationProjectId: z3.string().describe("Destination Conveyor project ID")
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")
400
451
  },
401
452
  async (params) => {
402
453
  const result = await conn2.moveCard(params);
@@ -417,9 +468,9 @@ function registerChatTools(server2, conn2) {
417
468
  "read_task_chat",
418
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.",
419
470
  {
420
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
421
- taskId: z3.string().describe("The task ID"),
422
- limit: z3.number().optional().describe("Max messages to return (default 50)")
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)")
423
474
  },
424
475
  async (params) => {
425
476
  const messages = await conn2.getTaskChat(params.taskId, params.limit, params.projectId);
@@ -430,9 +481,9 @@ function registerChatTools(server2, conn2) {
430
481
  "post_to_chat",
431
482
  "Post a message to a task's chat. Pass projectId to target a specific project; otherwise the configured default project is used.",
432
483
  {
433
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
434
- taskId: z3.string().describe("The task ID"),
435
- content: z3.string().describe("Message content")
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")
436
487
  },
437
488
  async (params) => {
438
489
  await conn2.postToTaskChat(params.taskId, params.content, params.projectId);
@@ -445,12 +496,12 @@ function registerGetTaskCli(server2, conn2) {
445
496
  "get_task_logs",
446
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.",
447
498
  {
448
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
449
- taskId: z3.string().describe("The task ID or slug"),
450
- source: z3.enum(["agent", "application"]).optional().describe(
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(
451
502
  "Filter by log source: 'agent' for reasoning/tool calls, 'application' for setup/dev-server output"
452
503
  ),
453
- 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)")
454
505
  },
455
506
  async ({ taskId, source, limit, projectId: projectId2 }) => {
456
507
  const effectiveLimit = Math.min(limit ?? 50, 500);
@@ -469,8 +520,8 @@ function registerGetTaskSessions(server2, conn2) {
469
520
  "get_task_sessions",
470
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.",
471
522
  {
472
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
473
- 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")
474
525
  },
475
526
  async ({ taskId, projectId: projectId2 }) => {
476
527
  const tasks = await conn2.getTaskSessions(taskId, projectId2);
@@ -488,19 +539,17 @@ function registerSearchTasks(server2, conn2) {
488
539
  "search_tasks",
489
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.",
490
541
  {
491
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
492
- tagNames: z3.array(z3.string()).optional().describe('Tag names to filter by (e.g., ["agent-runner", "chat"])'),
493
- searchQuery: z3.string().optional().describe("Text search on title and description"),
494
- statusFilters: z3.array(z3.enum(STATUS_ENUM)).optional().describe("Filter by one or more statuses"),
495
- typeFilters: z3.array(z3.enum(CARD_TYPE_ENUM)).optional().describe(
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(
496
547
  'Card types to include (default ["task"]). Pass e.g. ["incident"] or list several to search across types.'
497
548
  ),
498
- assigneeId: z3.string().optional().describe("Filter by assigned user ID"),
499
- unassigned: z3.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
500
- subProjectId: z3.string().optional().describe(
501
- "Scope to a sub-project board (list/search: filter; create/update: assign). Omit for the whole project."
502
- ),
503
- 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)")
504
553
  },
505
554
  async (params) => {
506
555
  const tasks = await conn2.searchTasks(params);
@@ -515,7 +564,7 @@ function registerListTags(server2, conn2) {
515
564
  "list_tags",
516
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.",
517
566
  {
518
- projectId: z3.string().optional().describe("Target Conveyor project ID")
567
+ projectId: z4.string().optional().describe("Target Conveyor project ID")
519
568
  },
520
569
  async (params) => {
521
570
  const tags = await conn2.listTags(params.projectId);
@@ -528,9 +577,9 @@ function registerReviewTools(server2, conn2) {
528
577
  "approve_task",
529
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.",
530
579
  {
531
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
532
- taskId: z3.string().describe("The task ID"),
533
- risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'low' on approval.")
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.")
534
583
  },
535
584
  async (params) => {
536
585
  const result = await conn2.approveTask(params.taskId, params.projectId, params.risk);
@@ -543,8 +592,8 @@ function registerReviewTools(server2, conn2) {
543
592
  "approve_and_merge_pr",
544
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.",
545
594
  {
546
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
547
- childTaskId: z3.string().describe("The child task ID whose PR should be approved and merged")
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")
548
597
  },
549
598
  async (params) => {
550
599
  const result = await conn2.approveAndMergePR(params.childTaskId, params.projectId);
@@ -562,10 +611,10 @@ function registerReviewTools(server2, conn2) {
562
611
  "request_changes",
563
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.",
564
613
  {
565
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
566
- taskId: z3.string().describe("The task ID"),
567
- feedback: z3.string().describe("Feedback message describing requested changes"),
568
- risk: z3.enum(RISK_ENUM).optional().describe("Optional risk level to record; defaults to a raise-only 'medium' on changes.")
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.")
569
618
  },
570
619
  async (params) => {
571
620
  await conn2.requestChanges(params.taskId, params.feedback, params.projectId, params.risk);
@@ -582,9 +631,9 @@ function registerReviewerTools(server2, conn2) {
582
631
  "add_reviewer",
583
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.",
584
633
  {
585
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
586
- taskId: z3.string().describe("The task ID or slug"),
587
- userId: z3.string().describe("User ID of the reviewer (use list_project_members to resolve a name or email)")
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)")
588
637
  },
589
638
  async (params) => {
590
639
  const result = await conn2.addReviewer(params);
@@ -602,9 +651,9 @@ function registerReviewerTools(server2, conn2) {
602
651
  "remove_reviewer",
603
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.",
604
653
  {
605
- projectId: z3.string().optional().describe("Target Conveyor project ID"),
606
- taskId: z3.string().describe("The task ID or slug"),
607
- userId: z3.string().describe("User ID of the reviewer to remove")
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")
608
657
  },
609
658
  async (params) => {
610
659
  const result = await conn2.removeReviewer(params);
@@ -636,7 +685,7 @@ function registerTaskTools(server2, conn2) {
636
685
  }
637
686
 
638
687
  // src/tools/builds.ts
639
- import { z as z4 } from "zod";
688
+ import { z as z5 } from "zod";
640
689
  function textResult2(result) {
641
690
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
642
691
  }
@@ -645,8 +694,8 @@ function registerTaskLifecycleTools(server2, conn2) {
645
694
  "stop_task",
646
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.",
647
696
  {
648
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
649
- 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")
650
699
  },
651
700
  async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
652
701
  );
@@ -654,8 +703,8 @@ function registerTaskLifecycleTools(server2, conn2) {
654
703
  "sleep_task",
655
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.",
656
705
  {
657
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
658
- 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")
659
708
  },
660
709
  async (params) => textResult2(await conn2.sleepTask(params.taskId, params.projectId))
661
710
  );
@@ -663,8 +712,8 @@ function registerTaskLifecycleTools(server2, conn2) {
663
712
  "resume_task",
664
713
  "Resume a sleeping task Claudespace. Pass projectId to target a specific project; otherwise the configured default project is used.",
665
714
  {
666
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
667
- 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")
668
717
  },
669
718
  async (params) => textResult2(await conn2.resumeTask(params.taskId, params.projectId))
670
719
  );
@@ -672,8 +721,8 @@ function registerTaskLifecycleTools(server2, conn2) {
672
721
  "delete_task_environment",
673
722
  "Delete a task environment, including durable Claudespace state. Pass projectId to target a specific project; otherwise the configured default project is used.",
674
723
  {
675
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
676
- 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")
677
726
  },
678
727
  async (params) => textResult2(await conn2.deleteTaskEnvironment(params.taskId, params.projectId))
679
728
  );
@@ -683,8 +732,8 @@ function registerBuildTools(server2, conn2) {
683
732
  "start_task",
684
733
  "Start a cloud build (codespace) for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
685
734
  {
686
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
687
- 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")
688
737
  },
689
738
  async (params) => {
690
739
  const result = await conn2.startBuild(params.taskId, params.projectId);
@@ -696,8 +745,8 @@ function registerBuildTools(server2, conn2) {
696
745
  "create_release",
697
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).",
698
747
  {
699
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
700
- 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(
701
750
  "Task IDs in Review (Dev) to cherry-pick into the release. Omit to release all of them."
702
751
  )
703
752
  },
@@ -710,8 +759,8 @@ function registerBuildTools(server2, conn2) {
710
759
  "get_build_status",
711
760
  "Check codespace and agent status for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
712
761
  {
713
- projectId: z4.string().optional().describe("Target Conveyor project ID"),
714
- 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")
715
764
  },
716
765
  async (params) => {
717
766
  const status = await conn2.getBuildStatus(params.taskId, params.projectId);
@@ -723,7 +772,7 @@ function registerBuildTools(server2, conn2) {
723
772
  // src/tools/attachments.ts
724
773
  import { readFile, stat } from "fs/promises";
725
774
  import { basename, extname } from "path";
726
- import { z as z5 } from "zod";
775
+ import { z as z6 } from "zod";
727
776
  var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
728
777
  var MIME_BY_EXT = {
729
778
  ".png": "image/png",
@@ -760,8 +809,8 @@ function registerListTaskFiles(server2, conn2) {
760
809
  "list_task_files",
761
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.",
762
811
  {
763
- projectId: z5.string().optional().describe("Target Conveyor project ID"),
764
- 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")
765
814
  },
766
815
  async (params) => {
767
816
  const files = await conn2.listTaskFiles(params.taskId, params.projectId);
@@ -803,11 +852,11 @@ function registerGetAttachment(server2, conn2) {
803
852
  "get_attachment",
804
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.",
805
854
  {
806
- projectId: z5.string().optional().describe("Target Conveyor project ID"),
807
- taskId: z5.string().describe("The task ID or slug"),
808
- fileId: z5.string().describe("The file ID to fetch"),
809
- offset: z5.number().int().nonnegative().optional().describe("Byte offset into text content (paging). Default 0."),
810
- maxBytes: z5.number().int().positive().optional().describe("Max bytes of text content to return from offset.")
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.")
811
860
  },
812
861
  async (params) => {
813
862
  const file = await conn2.getAttachment(params.taskId, params.fileId, {
@@ -824,11 +873,11 @@ function registerUploadAttachment(server2, conn2) {
824
873
  "upload_attachment",
825
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.",
826
875
  {
827
- projectId: z5.string().optional().describe("Target Conveyor project ID"),
828
- taskId: z5.string().describe("The task ID or slug"),
829
- path: z5.string().describe("Absolute path to the local file to upload"),
830
- comment: z5.string().optional().describe("When set, also posts the attachment to the task chat with this text"),
831
- mimeType: z5.string().optional().describe("Override the mime type inferred from the file extension")
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")
832
881
  },
833
882
  async (params) => {
834
883
  const info = await stat(params.path).catch(() => null);
@@ -891,18 +940,18 @@ function registerAttachmentTools(server2, conn2) {
891
940
  }
892
941
 
893
942
  // src/tools/pull-request.ts
894
- import { z as z6 } from "zod";
943
+ import { z as z7 } from "zod";
895
944
  function registerPullRequestTools(server2, conn2) {
896
945
  server2.tool(
897
946
  "create_pull_request",
898
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.",
899
948
  {
900
- projectId: z6.string().optional().describe("Target Conveyor project ID"),
901
- taskId: z6.string().describe("The task ID whose branch should be opened as a PR"),
902
- title: z6.string().describe("Pull request title"),
903
- body: z6.string().describe("Pull request body (markdown)"),
904
- head: z6.string().optional().describe("Source branch for the PR (defaults to the task's branch)"),
905
- base: z6.string().optional().describe("Target branch for the PR (defaults to the repo default)")
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)")
906
955
  },
907
956
  async (params) => {
908
957
  const result = await conn2.createPullRequest(params);
@@ -914,7 +963,7 @@ function registerPullRequestTools(server2, conn2) {
914
963
  }
915
964
 
916
965
  // src/tools/subtasks.ts
917
- import { z as z7 } from "zod";
966
+ import { z as z8 } from "zod";
918
967
  var STATUS_ENUM2 = [
919
968
  "Planning",
920
969
  "Open",
@@ -932,15 +981,15 @@ function registerCreateSubtask(server2, conn2) {
932
981
  "create_subtask",
933
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.",
934
983
  {
935
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
936
- parentTaskId: z7.string().describe("The parent task ID"),
937
- title: z7.string().describe("Subtask title"),
938
- description: z7.string().optional().describe("Subtask description"),
939
- plan: z7.string().optional().describe("Subtask implementation plan (markdown)"),
940
- ordinal: z7.number().optional().describe("Ordering position among siblings"),
941
- storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
942
- followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
943
- dependsOn: z7.array(z7.string()).optional().describe(
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(
944
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."
945
994
  )
946
995
  },
@@ -957,16 +1006,16 @@ function registerUpdateSubtask(server2, conn2) {
957
1006
  "update_subtask",
958
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.",
959
1008
  {
960
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
961
- subtaskId: z7.string().describe("The subtask ID"),
962
- title: z7.string().optional().describe("New title"),
963
- description: z7.string().optional().describe("New description"),
964
- plan: z7.string().optional().describe("New plan (markdown)"),
965
- status: z7.enum(STATUS_ENUM2).optional().describe("New status"),
966
- ordinal: z7.number().optional().describe("New ordering position among siblings"),
967
- storyPointValue: z7.number().optional().describe(SP_DESCRIPTION),
968
- followParentStatus: z7.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
969
- dependsOn: z7.array(z7.string()).optional().describe(
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(
970
1019
  "Replace the sibling subtask ids/slugs this subtask blocks on (pass [] to clear). Omit to leave dependencies unchanged."
971
1020
  )
972
1021
  },
@@ -985,8 +1034,8 @@ function registerListSubtasks(server2, conn2) {
985
1034
  "list_subtasks",
986
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.",
987
1036
  {
988
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
989
- 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")
990
1039
  },
991
1040
  async (params) => {
992
1041
  const subtasks = await conn2.listSubtasks(params.taskId, params.projectId);
@@ -999,8 +1048,8 @@ function registerDeleteSubtask(server2, conn2) {
999
1048
  "delete_subtask",
1000
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.",
1001
1050
  {
1002
- projectId: z7.string().optional().describe("Target Conveyor project ID"),
1003
- 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")
1004
1053
  },
1005
1054
  async (params) => {
1006
1055
  const result = await conn2.deleteSubtask(params.subtaskId, params.projectId);
@@ -1020,14 +1069,14 @@ function registerSubtaskTools(server2, conn2) {
1020
1069
  }
1021
1070
 
1022
1071
  // src/tools/dependencies.ts
1023
- import { z as z8 } from "zod";
1072
+ import { z as z9 } from "zod";
1024
1073
  function registerGetDependencies(server2, conn2) {
1025
1074
  server2.tool(
1026
1075
  "get_dependencies",
1027
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.",
1028
1077
  {
1029
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
1030
- 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")
1031
1080
  },
1032
1081
  async (params) => {
1033
1082
  const deps = await conn2.getDependencies(params.taskId, params.projectId);
@@ -1040,9 +1089,9 @@ function registerAddDependency(server2, conn2) {
1040
1089
  "add_dependency",
1041
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.",
1042
1091
  {
1043
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
1044
- taskId: z8.string().describe("The task ID that will be blocked"),
1045
- dependsOnSlugOrId: z8.string().describe("Slug or ID of the task this one depends on")
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")
1046
1095
  },
1047
1096
  async (params) => {
1048
1097
  await conn2.addDependency(params);
@@ -1055,9 +1104,9 @@ function registerRemoveDependency(server2, conn2) {
1055
1104
  "remove_dependency",
1056
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.",
1057
1106
  {
1058
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
1059
- taskId: z8.string().describe("The task ID to unblock"),
1060
- dependsOnSlugOrId: z8.string().describe("Slug or ID of the dependency to remove")
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")
1061
1110
  },
1062
1111
  async (params) => {
1063
1112
  await conn2.removeDependency(params);
@@ -1072,16 +1121,16 @@ function registerDependencyTools(server2, conn2) {
1072
1121
  }
1073
1122
 
1074
1123
  // src/tools/suggestions.ts
1075
- import { z as z9 } from "zod";
1124
+ import { z as z10 } from "zod";
1076
1125
  function registerSuggestionTools(server2, conn2) {
1077
1126
  server2.tool(
1078
1127
  "create_suggestion",
1079
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.",
1080
1129
  {
1081
- projectId: z9.string().optional().describe("Target Conveyor project ID"),
1082
- title: z9.string().describe("Suggestion title"),
1083
- description: z9.string().optional().describe("Suggestion details (markdown)"),
1084
- tagNames: z9.array(z9.string()).optional().describe('Tag names to categorize the suggestion (e.g., ["agent-runner"])')
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"])')
1085
1134
  },
1086
1135
  async (params) => {
1087
1136
  const result = await conn2.createSuggestion(params);
@@ -1092,7 +1141,7 @@ function registerSuggestionTools(server2, conn2) {
1092
1141
  }
1093
1142
 
1094
1143
  // src/tools/checklists.ts
1095
- import { z as z10 } from "zod";
1144
+ import { z as z11 } from "zod";
1096
1145
  function renderManualTestGroups(groups) {
1097
1146
  const lines = [];
1098
1147
  for (const g of groups) {
@@ -1115,8 +1164,8 @@ function registerListManualTests(server2, conn2) {
1115
1164
  "list_manual_tests",
1116
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.",
1117
1166
  {
1118
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1119
- taskId: z10.string().describe("The task ID or slug (the value in a card URL, /cards/<slug>)")
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>)")
1120
1169
  },
1121
1170
  async (params) => {
1122
1171
  const items = await conn2.listManualTests(params.taskId, params.projectId);
@@ -1142,9 +1191,9 @@ function registerSetManualTests(server2, conn2) {
1142
1191
  "set_manual_tests",
1143
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.",
1144
1193
  {
1145
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1146
- taskId: z10.string().describe("The task ID or slug"),
1147
- items: z10.array(z10.object({ title: z10.string().min(1).describe("A concise, actionable test step") })).min(1).describe("List of manual test steps to add")
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")
1148
1197
  },
1149
1198
  async (params) => {
1150
1199
  const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
@@ -1159,10 +1208,10 @@ function registerEditManualTest(server2, conn2) {
1159
1208
  "edit_manual_test",
1160
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.",
1161
1210
  {
1162
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1163
- taskId: z10.string().describe("The task ID or slug"),
1164
- title: z10.string().min(1).describe("The current title of the manual test to edit"),
1165
- newTitle: z10.string().min(1).describe("The new title for the manual test")
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")
1166
1215
  },
1167
1216
  async (params) => {
1168
1217
  await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
@@ -1175,9 +1224,9 @@ function registerRemoveManualTest(server2, conn2) {
1175
1224
  "remove_manual_test",
1176
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).",
1177
1226
  {
1178
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1179
- taskId: z10.string().describe("The task ID or slug"),
1180
- title: z10.string().min(1).describe("The title of the manual test to remove")
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")
1181
1230
  },
1182
1231
  async (params) => {
1183
1232
  await conn2.removeManualTest(params.taskId, params.title, params.projectId);
@@ -1190,9 +1239,9 @@ function registerApproveManualTest(server2, conn2) {
1190
1239
  "approve_manual_test",
1191
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.",
1192
1241
  {
1193
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1194
- taskId: z10.string().describe("The task ID or slug"),
1195
- title: z10.string().min(1).describe("The title of the manual test to approve")
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")
1196
1245
  },
1197
1246
  async (params) => {
1198
1247
  await conn2.approveManualTest(params.taskId, params.title, params.projectId);
@@ -1205,10 +1254,10 @@ function registerRejectManualTest(server2, conn2) {
1205
1254
  "reject_manual_test",
1206
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.",
1207
1256
  {
1208
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1209
- taskId: z10.string().describe("The task ID or slug"),
1210
- title: z10.string().min(1).describe("The title of the manual test to reject"),
1211
- reason: z10.string().min(1).max(2e3).describe("Why the test failed \u2014 what went wrong, shown to the team")
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")
1212
1261
  },
1213
1262
  async (params) => {
1214
1263
  await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
@@ -1228,9 +1277,9 @@ function registerQueryManualTests(server2, conn2) {
1228
1277
  "query_manual_tests",
1229
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.",
1230
1279
  {
1231
- projectId: z10.string().optional().describe("Target Conveyor project ID"),
1232
- cardStatuses: z10.array(z10.string()).optional().describe('Filter tasks by card/column status, e.g. ["ReviewDev", "ReviewLive"]'),
1233
- testStatuses: z10.array(z10.enum(["open", "approved", "rejected"])).optional().describe("Filter tests by status: open | approved | rejected")
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")
1234
1283
  },
1235
1284
  async (params) => {
1236
1285
  const groups = await conn2.queryManualTests({
@@ -1256,7 +1305,7 @@ function registerChecklistTools(server2, conn2) {
1256
1305
  }
1257
1306
 
1258
1307
  // src/tools/workspace.ts
1259
- import { z as z11 } from "zod";
1308
+ import { z as z12 } from "zod";
1260
1309
 
1261
1310
  // src/workspace-ssh-tunnel.ts
1262
1311
  import net from "net";
@@ -1375,8 +1424,8 @@ function registerAttachInfoTool(server2, conn2) {
1375
1424
  "workspace_attach_info",
1376
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.",
1377
1426
  {
1378
- taskId: z11.string().describe("The task ID"),
1379
- 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")
1380
1429
  },
1381
1430
  async ({ taskId, sshPublicKey }) => {
1382
1431
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1389,7 +1438,7 @@ function registerPreviewUrlsTool(server2, conn2) {
1389
1438
  "workspace_preview_urls",
1390
1439
  "Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
1391
1440
  {
1392
- taskId: z11.string().describe("The task ID")
1441
+ taskId: z12.string().describe("The task ID")
1393
1442
  },
1394
1443
  async ({ taskId }) => {
1395
1444
  const info = await conn2.getWorkspaceAttachInfo(taskId);
@@ -1418,10 +1467,10 @@ function registerStartTunnelTool(server2, conn2, startTunnel) {
1418
1467
  "workspace_start_tunnel",
1419
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.",
1420
1469
  {
1421
- taskId: z11.string().describe("The task ID"),
1422
- port: z11.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
1423
- preferredLocalPort: z11.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
1424
- sshPublicKey: z11.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
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")
1425
1474
  },
1426
1475
  async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
1427
1476
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -1474,7 +1523,7 @@ function registerStopTunnelTool(server2) {
1474
1523
  "workspace_stop_tunnel",
1475
1524
  "Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
1476
1525
  {
1477
- tunnelId: z11.string().describe("Tunnel id returned by workspace_start_tunnel")
1526
+ tunnelId: z12.string().describe("Tunnel id returned by workspace_start_tunnel")
1478
1527
  },
1479
1528
  async ({ tunnelId }) => {
1480
1529
  const tunnel = activeTunnels.get(tunnelId);
@@ -1493,7 +1542,7 @@ function registerWorkspaceTools(server2, conn2, deps = {}) {
1493
1542
  }
1494
1543
 
1495
1544
  // src/tools/logs.ts
1496
- import { z as z12 } from "zod";
1545
+ import { z as z13 } from "zod";
1497
1546
  var SEVERITY_ENUM = [
1498
1547
  "DEBUG",
1499
1548
  "INFO",
@@ -1592,41 +1641,98 @@ async function runQueryGcpLogs(conn2, params, now = Date.now) {
1592
1641
  }
1593
1642
  return [header, ...lines, ...footer].join("\n");
1594
1643
  }
1644
+ async function runQueryGrafanaLogs(conn2, params, now = Date.now) {
1645
+ const startTime = params.startTime ?? new Date(now() - (params.sinceMinutes ?? DEFAULT_SINCE_MINUTES) * 6e4).toISOString();
1646
+ const result = await conn2.queryGrafanaLogs({
1647
+ projectId: params.projectId,
1648
+ env: params.env,
1649
+ level: params.level,
1650
+ services: params.services,
1651
+ search: params.search,
1652
+ logql: params.logql,
1653
+ startTime,
1654
+ endTime: params.endTime,
1655
+ limit: params.limit
1656
+ });
1657
+ if (result.error) return result.error;
1658
+ const header = [
1659
+ `env=${params.env ?? "prod"}`,
1660
+ `window=${startTime}\u2192${params.endTime ?? "now"}`,
1661
+ ...params.level ? [`minLevel=${params.level}`] : [],
1662
+ ...result.logql ? [`logql=${truncateLine(result.logql)}`] : [],
1663
+ `entries=${result.entries.length}`
1664
+ ].join(" ");
1665
+ const lines = result.entries.map(formatLogEntryLine);
1666
+ const footer = result.hasMore ? ["-- hit the limit: narrow the window (startTime/endTime) for older lines"] : [];
1667
+ if (lines.length === 0) {
1668
+ return [
1669
+ header,
1670
+ "(no matching log entries \u2014 widen the window, lower minLevel, or drop filters)"
1671
+ ].join("\n");
1672
+ }
1673
+ return [header, ...lines, ...footer].join("\n");
1674
+ }
1675
+ function registerGrafanaLogTool(server2, conn2) {
1676
+ server2.tool(
1677
+ "query_grafana_logs",
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>'.",
1679
+ {
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(
1683
+ "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
1684
+ ),
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)")
1692
+ },
1693
+ async (params) => {
1694
+ const text = await runQueryGrafanaLogs(conn2, params);
1695
+ return { content: [{ type: "text", text }] };
1696
+ }
1697
+ );
1698
+ }
1595
1699
  function registerLogTools(server2, conn2) {
1596
1700
  server2.tool(
1597
1701
  "query_gcp_logs",
1598
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.",
1599
1703
  {
1600
- projectId: z12.string().optional().describe("Target Conveyor project ID"),
1601
- env: z12.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
1602
- sinceMinutes: z12.number().int().min(1).max(10080).optional().describe(
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(
1603
1707
  "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
1604
1708
  ),
1605
- startTime: z12.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
1606
- endTime: z12.string().optional().describe("ISO 8601 upper bound (default now)"),
1607
- severity: z12.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
1608
- services: z12.array(z12.string()).optional().describe(
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(
1609
1713
  "Restrict to these Cloud Run service names (prod/dev only). Defaults to all services linked in project settings."
1610
1714
  ),
1611
- sqlInstances: z12.array(z12.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
1612
- 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(
1613
1717
  "Set true to search ALL logs in the GCP project, ignoring the linked-resource scope"
1614
1718
  ),
1615
- search: z12.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
1616
- filter: z12.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
1617
- limit: z12.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
1618
- pageToken: z12.string().optional().describe("Opaque token from a previous response to fetch the next page")
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")
1619
1723
  },
1620
1724
  async (params) => {
1621
1725
  const text = await runQueryGcpLogs(conn2, params);
1622
1726
  return { content: [{ type: "text", text }] };
1623
1727
  }
1624
1728
  );
1729
+ registerGrafanaLogTool(server2, conn2);
1625
1730
  }
1626
1731
 
1627
1732
  // src/tools/index.ts
1628
1733
  function registerAllTools(server2, conn2) {
1629
1734
  registerProjectTools(server2, conn2);
1735
+ registerConnectionTools(server2, conn2);
1630
1736
  registerProjectConfigTools(server2, conn2);
1631
1737
  registerTaskTools(server2, conn2);
1632
1738
  registerBuildTools(server2, conn2);
@@ -1645,13 +1751,14 @@ var { version } = createRequire(import.meta.url)("../package.json");
1645
1751
  var apiUrl = process.env.CONVEYOR_API_URL;
1646
1752
  var projectToken = process.env.CONVEYOR_USER_TOKEN ?? process.env.CONVEYOR_PROJECT_TOKEN;
1647
1753
  var projectId = process.env.CONVEYOR_PROJECT_ID;
1754
+ var subProjectId = process.env.CONVEYOR_SUBPROJECT_ID;
1648
1755
  if (!apiUrl || !projectToken) {
1649
1756
  process.stderr.write(
1650
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"
1651
1758
  );
1652
1759
  process.exit(1);
1653
1760
  }
1654
- var conn = new ConveyorConnection({ apiUrl, projectToken, projectId });
1761
+ var conn = new ConveyorConnection({ apiUrl, projectToken, projectId, subProjectId });
1655
1762
  try {
1656
1763
  await conn.connect();
1657
1764
  process.stderr.write("Connected to Conveyor API\n");