edsger 0.53.0 → 0.54.1

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.
Files changed (148) hide show
  1. package/README.md +20 -0
  2. package/dist/api/financing.d.ts +47 -0
  3. package/dist/api/financing.js +37 -0
  4. package/dist/api/issues/approval-checker.d.ts +11 -9
  5. package/dist/api/issues/approval-checker.js +30 -41
  6. package/dist/api/issues/status-updater.d.ts +47 -20
  7. package/dist/api/issues/status-updater.js +114 -46
  8. package/dist/api/issues/update-issue.d.ts +5 -0
  9. package/dist/api/issues/update-issue.js +6 -0
  10. package/dist/commands/agent-workflow/processor.js +5 -1
  11. package/dist/commands/checklists/index.d.ts +5 -2
  12. package/dist/commands/checklists/index.js +73 -12
  13. package/dist/commands/checklists/tools.d.ts +14 -7
  14. package/dist/commands/checklists/tools.js +15 -208
  15. package/dist/commands/financing-deck/index.d.ts +8 -0
  16. package/dist/commands/financing-deck/index.js +66 -0
  17. package/dist/commands/find-architecture/index.d.ts +13 -0
  18. package/dist/commands/find-architecture/index.js +41 -0
  19. package/dist/commands/sync-github-issues/index.d.ts +11 -0
  20. package/dist/commands/sync-github-issues/index.js +42 -0
  21. package/dist/commands/sync-sentry-issues/index.d.ts +14 -0
  22. package/dist/commands/sync-sentry-issues/index.js +73 -0
  23. package/dist/commands/workflow/executors/phase-executor.js +6 -4
  24. package/dist/commands/workflow/phase-orchestrator.js +0 -1
  25. package/dist/config/issue-status.d.ts +18 -45
  26. package/dist/config/issue-status.js +21 -107
  27. package/dist/index.js +97 -3
  28. package/dist/phases/app-store-generation/agent.js +2 -1
  29. package/dist/phases/app-store-generation/index.js +11 -3
  30. package/dist/phases/autonomous/index.js +9 -6
  31. package/dist/phases/branch-planning/index.js +1 -2
  32. package/dist/phases/branch-planning/prompts.d.ts +1 -1
  33. package/dist/phases/branch-planning/prompts.js +3 -2
  34. package/dist/phases/bug-fixing/analyzer.js +6 -3
  35. package/dist/phases/bug-fixing/mcp-server.d.ts +18 -1
  36. package/dist/phases/bug-fixing/mcp-server.js +19 -76
  37. package/dist/phases/chat-processor/product-tools.d.ts +5 -8
  38. package/dist/phases/chat-processor/product-tools.js +6 -512
  39. package/dist/phases/chat-processor/tools.d.ts +5 -9
  40. package/dist/phases/chat-processor/tools.js +6 -704
  41. package/dist/phases/code-implementation/branch-pr-creator.js +7 -5
  42. package/dist/phases/code-implementation/index.js +6 -3
  43. package/dist/phases/code-implementation-verification/agent.js +6 -1
  44. package/dist/phases/code-refine/index.js +8 -6
  45. package/dist/phases/code-refine/refine-iteration.js +2 -1
  46. package/dist/phases/code-review/index.js +8 -6
  47. package/dist/phases/code-testing/analyzer.js +11 -8
  48. package/dist/phases/financing-deck/agent.d.ts +1 -0
  49. package/dist/phases/financing-deck/agent.js +96 -0
  50. package/dist/phases/financing-deck/context.d.ts +13 -0
  51. package/dist/phases/financing-deck/context.js +69 -0
  52. package/dist/phases/financing-deck/index.d.ts +15 -0
  53. package/dist/phases/financing-deck/index.js +89 -0
  54. package/dist/phases/financing-deck/prompts.d.ts +2 -0
  55. package/dist/phases/financing-deck/prompts.js +94 -0
  56. package/dist/phases/find-architecture/index.d.ts +44 -0
  57. package/dist/phases/find-architecture/index.js +248 -0
  58. package/dist/phases/find-architecture/prompts.d.ts +31 -0
  59. package/dist/phases/find-architecture/prompts.js +128 -0
  60. package/dist/phases/find-architecture/state.d.ts +21 -0
  61. package/dist/phases/find-architecture/state.js +17 -0
  62. package/dist/phases/find-architecture/types.d.ts +55 -0
  63. package/dist/phases/find-architecture/types.js +69 -0
  64. package/dist/phases/find-bugs/index.js +13 -4
  65. package/dist/phases/find-features/index.js +10 -5
  66. package/dist/phases/find-smells/index.js +10 -3
  67. package/dist/phases/functional-testing/analyzer.js +27 -17
  68. package/dist/phases/functional-testing/http-fallback.d.ts +1 -1
  69. package/dist/phases/functional-testing/http-fallback.js +32 -16
  70. package/dist/phases/functional-testing/mcp-server.d.ts +9 -1
  71. package/dist/phases/functional-testing/mcp-server.js +13 -132
  72. package/dist/phases/growth-analysis/agent.js +2 -2
  73. package/dist/phases/growth-analysis/index.js +9 -3
  74. package/dist/phases/intelligence-analysis/agent.js +2 -2
  75. package/dist/phases/intelligence-analysis/index.js +9 -2
  76. package/dist/phases/issue-analysis/agent.d.ts +9 -1
  77. package/dist/phases/issue-analysis/agent.js +68 -27
  78. package/dist/phases/issue-analysis/context.d.ts +5 -9
  79. package/dist/phases/issue-analysis/context.js +31 -76
  80. package/dist/phases/issue-analysis/index.js +32 -84
  81. package/dist/phases/issue-analysis/outcome.d.ts +3 -33
  82. package/dist/phases/issue-analysis/outcome.js +15 -253
  83. package/dist/phases/issue-analysis/prompts.d.ts +3 -5
  84. package/dist/phases/issue-analysis/prompts.js +45 -158
  85. package/dist/phases/issue-analysis-verification/agent.d.ts +4 -4
  86. package/dist/phases/issue-analysis-verification/agent.js +5 -5
  87. package/dist/phases/issue-analysis-verification/index.d.ts +4 -2
  88. package/dist/phases/issue-analysis-verification/index.js +9 -22
  89. package/dist/phases/issue-analysis-verification/prompts.d.ts +1 -2
  90. package/dist/phases/issue-analysis-verification/prompts.js +21 -46
  91. package/dist/phases/output-contracts.js +66 -78
  92. package/dist/phases/pr-execution/context.d.ts +2 -0
  93. package/dist/phases/pr-execution/context.js +1 -0
  94. package/dist/phases/pr-execution/index.js +28 -19
  95. package/dist/phases/pr-execution/prompts.d.ts +2 -1
  96. package/dist/phases/pr-execution/prompts.js +12 -10
  97. package/dist/phases/pr-resolve/index.js +2 -8
  98. package/dist/phases/pr-splitting/index.js +3 -3
  99. package/dist/phases/pr-splitting/prompts.d.ts +1 -1
  100. package/dist/phases/pr-splitting/prompts.js +3 -2
  101. package/dist/phases/pull-request/creator.js +10 -7
  102. package/dist/phases/pull-request/handler.js +3 -1
  103. package/dist/phases/release-sync/index.js +52 -43
  104. package/dist/phases/run-sheet/index.js +2 -1
  105. package/dist/phases/smoke-test/agent.js +2 -1
  106. package/dist/phases/smoke-test/index.js +4 -1
  107. package/dist/phases/sync-github-issues/index.d.ts +41 -0
  108. package/dist/phases/sync-github-issues/index.js +187 -0
  109. package/dist/phases/sync-github-issues/state.d.ts +26 -0
  110. package/dist/phases/sync-github-issues/state.js +18 -0
  111. package/dist/phases/sync-github-issues/types.d.ts +35 -0
  112. package/dist/phases/sync-github-issues/types.js +6 -0
  113. package/dist/phases/sync-sentry-issues/index.d.ts +29 -0
  114. package/dist/phases/sync-sentry-issues/index.js +153 -0
  115. package/dist/phases/sync-sentry-issues/sentry-client.d.ts +66 -0
  116. package/dist/phases/sync-sentry-issues/sentry-client.js +221 -0
  117. package/dist/phases/sync-sentry-issues/state.d.ts +23 -0
  118. package/dist/phases/sync-sentry-issues/state.js +18 -0
  119. package/dist/phases/sync-sentry-issues/types.d.ts +46 -0
  120. package/dist/phases/sync-sentry-issues/types.js +6 -0
  121. package/dist/phases/sync-shared/mcp.d.ts +81 -0
  122. package/dist/phases/sync-shared/mcp.js +111 -0
  123. package/dist/phases/technical-design/index.js +0 -1
  124. package/dist/phases/test-cases-analysis/agent.js +2 -1
  125. package/dist/phases/test-cases-analysis/index.js +0 -1
  126. package/dist/phases/user-stories-analysis/agent.js +2 -1
  127. package/dist/phases/user-stories-analysis/index.js +0 -1
  128. package/dist/services/coaching/coaching-agent.js +29 -4
  129. package/dist/services/feedbacks.d.ts +1 -1
  130. package/dist/services/repo-config.d.ts +17 -0
  131. package/dist/services/repo-config.js +50 -0
  132. package/dist/skills/phase/issue-analysis/SKILL.md +48 -92
  133. package/dist/skills/phase/issue-analysis-verification/SKILL.md +46 -31
  134. package/dist/tools/bootstrap.d.ts +45 -0
  135. package/dist/tools/bootstrap.js +50 -0
  136. package/dist/types/external-sources.d.ts +22 -0
  137. package/dist/types/external-sources.js +23 -0
  138. package/dist/types/index.d.ts +5 -10
  139. package/dist/types/issues.d.ts +2 -0
  140. package/dist/types/llm-responses.d.ts +1 -14
  141. package/dist/utils/formatters.js +1 -7
  142. package/dist/utils/git-branch-manager-async.js +9 -5
  143. package/dist/utils/git-branch-manager.js +17 -7
  144. package/dist/workspace/workspace-manager.d.ts +10 -0
  145. package/dist/workspace/workspace-manager.js +22 -1
  146. package/package.json +6 -2
  147. package/vitest.config.ts +4 -0
  148. package/.env.local +0 -12
@@ -1,709 +1,11 @@
1
- /* eslint-disable max-lines -- tool definitions with schemas and handlers are clearer co-located in one file */
2
1
  /**
3
- * Chat MCP server — registers custom tools with the Claude Agent SDK.
2
+ * Chat (issue) MCP server — CLI entry point.
4
3
  *
5
- * Uses createSdkMcpServer + tool() so the SDK can discover and execute
6
- * these tools automatically. Claude Code's built-in tools (Read, Write,
7
- * Bash, Grep, Glob) are also available via the preset.
8
- */
9
- import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
10
- import { z } from 'zod';
11
- import { listChatMessages, sendAiMessage } from '../../api/chat.js';
12
- import { callMcpEndpoint } from '../../api/mcp-client.js';
13
- /**
14
- * Create an in-process MCP server with chat-specific tools.
15
- * Pass the returned config to query() options.mcpServers.
4
+ * Tool implementations live in `edsger-tools`. This shim adapts the
5
+ * CLI's `callMcpEndpoint` transport into `ToolDeps`.
16
6
  */
7
+ import { createChatIssueMcpServer } from 'edsger-tools';
8
+ import { getToolDeps } from '../../tools/bootstrap.js';
17
9
  export function createChatMcpServer() {
18
- return createSdkMcpServer({
19
- name: 'edsger-chat',
20
- version: '1.0.0',
21
- tools: [
22
- tool('update_issue_status', 'Change the issue status (e.g., back to ready_for_ai, backlog). Use when the user wants to restart or change the workflow state.', {
23
- issue_id: z.string().describe('Issue ID'),
24
- status: z.string().describe('New status value'),
25
- }, async (args) => {
26
- const result = await callMcpEndpoint('issues/update', {
27
- issue_id: args.issue_id,
28
- status: args.status,
29
- });
30
- return {
31
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
32
- };
33
- }),
34
- tool('update_execution_mode', 'Change the issue execution mode (e.g., from_user_stories_analysis, from_code_implementation).', {
35
- issue_id: z.string().describe('Issue ID'),
36
- execution_mode: z
37
- .enum([
38
- 'full_pipeline',
39
- 'only_issue_analysis',
40
- 'only_user_stories_analysis',
41
- 'only_test_cases_analysis',
42
- 'only_technical_design',
43
- 'only_branch_planning',
44
- 'only_code_implementation',
45
- 'only_pr_splitting',
46
- 'only_pr_execution',
47
- 'only_functional_testing',
48
- 'only_code_refine',
49
- 'only_code_review',
50
- 'from_issue_analysis',
51
- 'from_user_stories_analysis',
52
- 'from_test_cases_analysis',
53
- 'from_technical_design',
54
- 'from_branch_planning',
55
- 'from_code_implementation',
56
- 'from_pr_splitting',
57
- 'from_pr_execution',
58
- 'from_functional_testing',
59
- 'from_code_review',
60
- 'custom',
61
- 'autonomous',
62
- ])
63
- .describe('New execution mode'),
64
- }, async (args) => {
65
- const result = await callMcpEndpoint('issues/update', {
66
- issue_id: args.issue_id,
67
- execution_mode: args.execution_mode,
68
- });
69
- return {
70
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
71
- };
72
- }),
73
- tool('update_workflow', 'Modify workflow phases — reset phases to pending, skip phases, or reorder. Phase names must use snake_case (e.g., code_implementation, NOT code-implementation).', {
74
- issue_id: z.string().describe('Issue ID'),
75
- workflow: z
76
- .array(z.object({
77
- phase: z
78
- .string()
79
- .describe('Phase name in snake_case. Valid phases: issue_analysis, user_stories_analysis, test_cases_analysis, technical_design, branch_planning, code_implementation, pr_splitting, pr_execution, functional_testing, code_review, code_refine, autonomous'),
80
- status: z.enum(['pending', 'completed', 'skipped']),
81
- }))
82
- .describe('Updated workflow array'),
83
- }, async (args) => {
84
- // Normalize phase names: kebab-case → snake_case
85
- const normalizedWorkflow = args.workflow.map((p) => ({
86
- ...p,
87
- phase: p.phase.replace(/-/g, '_'),
88
- }));
89
- const result = await callMcpEndpoint('issues/update', {
90
- issue_id: args.issue_id,
91
- workflow: normalizedWorkflow,
92
- });
93
- return {
94
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
95
- };
96
- }),
97
- tool('update_user_stories', 'Create, update, or delete user stories based on feedback. Always provide a reason explaining why the change is being made.', {
98
- issue_id: z.string().describe('Issue ID'),
99
- reason: z
100
- .string()
101
- .describe('Why this change is being made — e.g. user feedback, bug found, requirement changed. Required for audit trail.'),
102
- actions: z.array(z.object({
103
- action: z.enum(['create', 'update', 'delete']),
104
- user_story_id: z
105
- .string()
106
- .optional()
107
- .describe('Required for update/delete'),
108
- title: z.string().optional(),
109
- description: z.string().optional(),
110
- status: z
111
- .enum(['draft', 'pending_approval', 'approved'])
112
- .optional(),
113
- })),
114
- }, async (args) => {
115
- const results = [];
116
- for (const action of args.actions) {
117
- try {
118
- if (action.action === 'create') {
119
- await callMcpEndpoint('user_stories/create', {
120
- issue_id: args.issue_id,
121
- user_stories: [
122
- {
123
- title: action.title,
124
- description: action.description,
125
- status: action.status || 'draft',
126
- },
127
- ],
128
- });
129
- results.push({ action: 'create', success: true });
130
- }
131
- else if (action.action === 'delete' && action.user_story_id) {
132
- await callMcpEndpoint('user_stories/delete', {
133
- user_story_id: action.user_story_id,
134
- });
135
- results.push({ action: 'delete', success: true });
136
- }
137
- else if (action.action === 'update' && action.user_story_id) {
138
- await callMcpEndpoint('user_stories/update_status', {
139
- user_story_id: action.user_story_id,
140
- status: action.status || 'draft',
141
- reason: args.reason,
142
- });
143
- results.push({ action: 'update', success: true });
144
- }
145
- }
146
- catch (error) {
147
- const msg = error instanceof Error ? error.message : String(error);
148
- results.push({
149
- action: action.action,
150
- success: false,
151
- error: msg,
152
- });
153
- }
154
- }
155
- return {
156
- content: [
157
- { type: 'text', text: JSON.stringify({ results }, null, 2) },
158
- ],
159
- };
160
- }),
161
- tool('update_test_cases', 'Create, update, or delete test cases based on feedback. Always provide a reason explaining why the change is being made.', {
162
- issue_id: z.string().describe('Issue ID'),
163
- reason: z
164
- .string()
165
- .describe('Why this change is being made — e.g. user feedback, bug found, requirement changed. Required for audit trail.'),
166
- actions: z.array(z.object({
167
- action: z.enum(['create', 'update', 'delete']),
168
- test_case_id: z
169
- .string()
170
- .optional()
171
- .describe('Required for update/delete'),
172
- name: z.string().optional(),
173
- description: z.string().optional(),
174
- is_critical: z.boolean().optional(),
175
- status: z
176
- .enum([
177
- 'draft',
178
- 'pending_approval',
179
- 'in_progress',
180
- 'done',
181
- 'cancelled',
182
- ])
183
- .optional()
184
- .describe('Test case status. Cannot be set to approved — only humans can approve via the web UI.'),
185
- })),
186
- }, async (args) => {
187
- const results = [];
188
- for (const action of args.actions) {
189
- try {
190
- if (action.action === 'create') {
191
- await callMcpEndpoint('test_cases/create', {
192
- issue_id: args.issue_id,
193
- test_cases: [
194
- {
195
- name: action.name,
196
- description: action.description,
197
- is_critical: action.is_critical || false,
198
- },
199
- ],
200
- });
201
- results.push({ action: 'create', success: true });
202
- }
203
- else if (action.action === 'delete' && action.test_case_id) {
204
- await callMcpEndpoint('test_cases/delete', {
205
- test_case_id: action.test_case_id,
206
- });
207
- results.push({ action: 'delete', success: true });
208
- }
209
- else if (action.action === 'update' && action.test_case_id) {
210
- const updateParams = {
211
- test_case_id: action.test_case_id,
212
- reason: args.reason,
213
- };
214
- if (action.name !== undefined) {
215
- updateParams.name = action.name;
216
- }
217
- if (action.description !== undefined) {
218
- updateParams.description = action.description;
219
- }
220
- if (action.is_critical !== undefined) {
221
- updateParams.is_critical = action.is_critical;
222
- }
223
- if (action.status !== undefined) {
224
- updateParams.status = action.status;
225
- }
226
- await callMcpEndpoint('test_cases/update', updateParams);
227
- results.push({ action: 'update', success: true });
228
- }
229
- }
230
- catch (error) {
231
- const msg = error instanceof Error ? error.message : String(error);
232
- results.push({
233
- action: action.action,
234
- success: false,
235
- error: msg,
236
- });
237
- }
238
- }
239
- return {
240
- content: [
241
- { type: 'text', text: JSON.stringify({ results }, null, 2) },
242
- ],
243
- };
244
- }),
245
- tool('list_checklists', 'List checklists for the product this issue belongs to. Each checklist can apply to multiple phases and is assigned to a specific role. Returns checklists with their items.', {
246
- product_id: z.string().describe('Product ID'),
247
- role: z
248
- .string()
249
- .optional()
250
- .describe('Filter by role (product_manager, developer, qa_engineer, technical_lead, ux_designer, devops_engineer, security_engineer)'),
251
- phase: z
252
- .string()
253
- .optional()
254
- .describe('Filter by phase (e.g., code_implementation, code_review, technical_design)'),
255
- }, async (args) => {
256
- const params = {
257
- product_id: args.product_id,
258
- };
259
- if (args.role) {
260
- params.role = args.role;
261
- }
262
- if (args.phase) {
263
- params.phase = args.phase;
264
- }
265
- const result = await callMcpEndpoint('checklists/list', params);
266
- return {
267
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
268
- };
269
- }),
270
- tool('manage_checklists', 'Create, update, or delete checklists. A checklist belongs to a product, is assigned to a role, and applies to one or more phases (phases is an array). When creating, you can include items inline.', {
271
- product_id: z.string().describe('Product ID'),
272
- actions: z.array(z.object({
273
- action: z.enum(['create', 'update', 'delete']),
274
- checklist_id: z
275
- .string()
276
- .optional()
277
- .describe('Required for update/delete'),
278
- name: z
279
- .string()
280
- .optional()
281
- .describe('Checklist name (for create/update)'),
282
- description: z
283
- .string()
284
- .optional()
285
- .describe('Checklist description'),
286
- role: z
287
- .string()
288
- .optional()
289
- .describe('Role: product_manager, developer, qa_engineer, technical_lead, ux_designer, devops_engineer, security_engineer'),
290
- phases: z
291
- .array(z.string())
292
- .optional()
293
- .describe('Array of phases this checklist applies to (e.g., ["code_implementation", "code_review"]). A checklist can apply to multiple phases.'),
294
- is_active: z
295
- .boolean()
296
- .optional()
297
- .describe('Whether the checklist is active'),
298
- sort_order: z
299
- .number()
300
- .optional()
301
- .describe('Sort order for display'),
302
- items: z
303
- .array(z.object({
304
- title: z.string(),
305
- description: z.string().optional(),
306
- item_type: z
307
- .enum(['boolean', 'text', 'number', 'select'])
308
- .optional(),
309
- is_required: z.boolean().optional(),
310
- }))
311
- .optional()
312
- .describe('Items to create with the checklist (only for create action)'),
313
- })),
314
- },
315
- // eslint-disable-next-line complexity -- handles multiple CRUD action types in a single tool handler
316
- async (args) => {
317
- const results = [];
318
- for (const action of args.actions) {
319
- try {
320
- if (action.action === 'create') {
321
- if (!action.name || !action.role || !action.phases?.length) {
322
- results.push({
323
- action: 'create',
324
- success: false,
325
- error: 'name, role, and phases are required for create',
326
- });
327
- continue;
328
- }
329
- const result = await callMcpEndpoint('checklists/create', {
330
- product_id: args.product_id,
331
- name: action.name,
332
- description: action.description || null,
333
- role: action.role,
334
- phases: action.phases,
335
- is_active: action.is_active ?? true,
336
- sort_order: action.sort_order ?? 0,
337
- items: action.items,
338
- });
339
- results.push({ action: 'create', success: true, data: result });
340
- }
341
- else if (action.action === 'update' && action.checklist_id) {
342
- const params = {
343
- checklist_id: action.checklist_id,
344
- };
345
- if (action.name !== undefined) {
346
- params.name = action.name;
347
- }
348
- if (action.description !== undefined) {
349
- params.description = action.description;
350
- }
351
- if (action.role !== undefined) {
352
- params.role = action.role;
353
- }
354
- if (action.phases !== undefined) {
355
- params.phases = action.phases;
356
- }
357
- if (action.is_active !== undefined) {
358
- params.is_active = action.is_active;
359
- }
360
- if (action.sort_order !== undefined) {
361
- params.sort_order = action.sort_order;
362
- }
363
- const result = await callMcpEndpoint('checklists/update', params);
364
- results.push({ action: 'update', success: true, data: result });
365
- }
366
- else if (action.action === 'delete' && action.checklist_id) {
367
- const result = await callMcpEndpoint('checklists/delete', {
368
- checklist_id: action.checklist_id,
369
- });
370
- results.push({ action: 'delete', success: true, data: result });
371
- }
372
- else {
373
- results.push({
374
- action: action.action,
375
- success: false,
376
- error: `Missing required checklist_id for ${action.action}`,
377
- });
378
- }
379
- }
380
- catch (error) {
381
- const msg = error instanceof Error ? error.message : String(error);
382
- results.push({
383
- action: action.action,
384
- success: false,
385
- error: msg,
386
- });
387
- }
388
- }
389
- return {
390
- content: [
391
- { type: 'text', text: JSON.stringify({ results }, null, 2) },
392
- ],
393
- };
394
- }),
395
- tool('manage_checklist_items', 'Add, update, or delete items within a checklist. Each item has a title, type (boolean/text/number/select), and required flag.', {
396
- actions: z.array(z.object({
397
- action: z.enum(['create', 'update', 'delete']),
398
- checklist_id: z
399
- .string()
400
- .optional()
401
- .describe('Checklist ID to add items to (required for create)'),
402
- item_id: z
403
- .string()
404
- .optional()
405
- .describe('Item ID (required for update/delete)'),
406
- title: z.string().optional().describe('Item title'),
407
- description: z.string().optional().describe('Item description'),
408
- item_type: z
409
- .enum(['boolean', 'text', 'number', 'select'])
410
- .optional()
411
- .describe('Item type (default: boolean)'),
412
- is_required: z
413
- .boolean()
414
- .optional()
415
- .describe('Whether this item is required'),
416
- sort_order: z.number().optional().describe('Sort order'),
417
- config: z
418
- .record(z.string(), z.unknown())
419
- .optional()
420
- .describe('Additional config (e.g., options for select type)'),
421
- })),
422
- },
423
- // eslint-disable-next-line complexity -- handles multiple CRUD action types for checklist items
424
- async (args) => {
425
- const results = [];
426
- for (const action of args.actions) {
427
- try {
428
- if (action.action === 'create' && action.checklist_id) {
429
- if (!action.title) {
430
- results.push({
431
- action: 'create',
432
- success: false,
433
- error: 'title is required for create',
434
- });
435
- continue;
436
- }
437
- const result = await callMcpEndpoint('checklist_items/create', {
438
- checklist_id: action.checklist_id,
439
- items: [
440
- {
441
- title: action.title,
442
- description: action.description || null,
443
- item_type: action.item_type || 'boolean',
444
- is_required: action.is_required ?? true,
445
- sort_order: action.sort_order,
446
- config: action.config,
447
- },
448
- ],
449
- });
450
- results.push({ action: 'create', success: true, data: result });
451
- }
452
- else if (action.action === 'update' && action.item_id) {
453
- const params = {
454
- item_id: action.item_id,
455
- };
456
- if (action.title !== undefined) {
457
- params.title = action.title;
458
- }
459
- if (action.description !== undefined) {
460
- params.description = action.description;
461
- }
462
- if (action.item_type !== undefined) {
463
- params.item_type = action.item_type;
464
- }
465
- if (action.is_required !== undefined) {
466
- params.is_required = action.is_required;
467
- }
468
- if (action.sort_order !== undefined) {
469
- params.sort_order = action.sort_order;
470
- }
471
- if (action.config !== undefined) {
472
- params.config = action.config;
473
- }
474
- const result = await callMcpEndpoint('checklist_items/update', params);
475
- results.push({ action: 'update', success: true, data: result });
476
- }
477
- else if (action.action === 'delete' && action.item_id) {
478
- const result = await callMcpEndpoint('checklist_items/delete', {
479
- item_id: action.item_id,
480
- });
481
- results.push({ action: 'delete', success: true, data: result });
482
- }
483
- else {
484
- const missing = action.action === 'create' ? 'checklist_id' : 'item_id';
485
- results.push({
486
- action: action.action,
487
- success: false,
488
- error: `Missing required ${missing} for ${action.action}`,
489
- });
490
- }
491
- }
492
- catch (error) {
493
- const msg = error instanceof Error ? error.message : String(error);
494
- results.push({
495
- action: action.action,
496
- success: false,
497
- error: msg,
498
- });
499
- }
500
- }
501
- return {
502
- content: [
503
- { type: 'text', text: JSON.stringify({ results }, null, 2) },
504
- ],
505
- };
506
- }),
507
- tool('send_chat_message', 'Send a follow-up message to the chat. Use for explanations, summaries, or asking clarifying questions.', {
508
- channel_id: z.string().describe('Chat channel ID'),
509
- content: z.string().describe('Message content (markdown)'),
510
- message_type: z
511
- .enum(['text', 'question', 'answer'])
512
- .optional()
513
- .describe('Type of message'),
514
- }, async (args) => {
515
- await sendAiMessage(args.channel_id, args.content, {}, {
516
- messageType: args.message_type || 'text',
517
- });
518
- return {
519
- content: [{ type: 'text', text: 'Message sent successfully.' }],
520
- };
521
- }),
522
- tool('provide_options', 'Present 2-4 actionable options to the user. Each option has a label and description. The user will click one to respond.', {
523
- channel_id: z.string().describe('Chat channel ID'),
524
- prompt: z.string().describe('Question or context for the options'),
525
- options: z.array(z.object({
526
- label: z.string().describe('Short option label'),
527
- description: z.string().describe('What this option does'),
528
- action_key: z
529
- .string()
530
- .describe('Machine-readable key (e.g. "continue", "add_tests")'),
531
- })),
532
- }, async (args) => {
533
- await sendAiMessage(args.channel_id, args.prompt, { options: args.options }, { messageType: 'options' });
534
- return {
535
- content: [{ type: 'text', text: 'Options presented to the user.' }],
536
- };
537
- }),
538
- tool('get_issue_context', 'Fetch the full current state of an issue: status, workflow, user stories, test cases.', {
539
- issue_id: z.string().describe('Issue ID'),
540
- }, async (args) => {
541
- const [issueResult, storiesResult, testCasesResult] = await Promise.all([
542
- callMcpEndpoint('issues/get', {
543
- issue_id: args.issue_id,
544
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
545
- }),
546
- callMcpEndpoint('user_stories/list', {
547
- issue_id: args.issue_id,
548
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
549
- }),
550
- callMcpEndpoint('test_cases/list', {
551
- issue_id: args.issue_id,
552
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
553
- }),
554
- ]);
555
- const issue = issueResult?.issues?.[0] || {};
556
- const result = {
557
- issue,
558
- user_stories: storiesResult?.user_stories || [],
559
- test_cases: testCasesResult?.test_cases || [],
560
- summary: {
561
- status: issue.status,
562
- execution_mode: issue.execution_mode,
563
- workflow_phases: (issue.workflow || []).map(
564
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
565
- (p) => `${p.phase}: ${p.status}`),
566
- },
567
- };
568
- return {
569
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
570
- };
571
- }),
572
- tool('get_chat_history', 'Retrieve older chat messages from the channel. Use this when you need more context about what was discussed earlier — for example, to understand referenced bugs, prior decisions, or feedback the user gave in earlier messages.', {
573
- channel_id: z.string().describe('Chat channel ID'),
574
- limit: z
575
- .number()
576
- .optional()
577
- .describe('Number of messages to retrieve (default 50, max 100)'),
578
- before: z
579
- .string()
580
- .optional()
581
- .describe('Fetch messages before this ISO timestamp for pagination'),
582
- }, async (args) => {
583
- const limit = Math.min(args.limit || 50, 100);
584
- const messages = await listChatMessages(args.channel_id, {
585
- limit,
586
- ...(args.before ? { since: args.before } : {}),
587
- });
588
- const formatted = messages.map((m) => {
589
- const nonAiSender = m.sender_type === 'system' ? 'System' : m.sender_name || 'User';
590
- return {
591
- id: m.id,
592
- sender: m.sender_type === 'ai' ? 'AI' : nonAiSender,
593
- sender_type: m.sender_type,
594
- content: m.content,
595
- message_type: m.message_type,
596
- created_at: m.created_at,
597
- };
598
- });
599
- return {
600
- content: [
601
- {
602
- type: 'text',
603
- text: JSON.stringify({ messages: formatted, count: formatted.length }, null, 2),
604
- },
605
- ],
606
- };
607
- }),
608
- tool('create_task', 'Create a task for a team member (human) or for AI to execute. Use this when the user asks to assign work, request reviews, or notify someone about something they need to do.', {
609
- product_id: z.string().describe('Product ID'),
610
- name: z
611
- .string()
612
- .describe('Short task name (e.g., "Review user stories for Login issue")'),
613
- description: z
614
- .string()
615
- .optional()
616
- .describe('Detailed description of what needs to be done'),
617
- executor: z
618
- .enum(['ai', 'human'])
619
- .describe('Who should do this: "ai" for automated work, "human" for manual review/approval'),
620
- assigned_to: z
621
- .string()
622
- .optional()
623
- .describe('User ID to assign to (use list_product_members to look up)'),
624
- issue_id: z.string().optional().describe('Related issue ID'),
625
- action_url: z
626
- .string()
627
- .optional()
628
- .describe('URL where the assignee should take action. See prompt for URL patterns.'),
629
- priority: z
630
- .number()
631
- .optional()
632
- .describe('1=low, 2=medium, 3=high, 4=urgent'),
633
- }, async (args) => {
634
- // Get next sequence number
635
- const listResult = (await callMcpEndpoint('tasks/list_for_product', {
636
- product_id: args.product_id,
637
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
638
- }));
639
- const text = listResult?.content?.[0]?.text || '[]';
640
- const existingTasks = JSON.parse(text);
641
- const nextSequence = existingTasks.length + 1;
642
- // Use explicit action_url if provided, otherwise auto-generate from issue_id
643
- const actionUrl = args.action_url ||
644
- (args.issue_id
645
- ? `/products/${args.product_id}/issues/${args.issue_id}`
646
- : null);
647
- const result = await callMcpEndpoint('tasks/create', {
648
- product_id: args.product_id,
649
- sequence: nextSequence,
650
- name: args.name,
651
- description: args.description || null,
652
- executor: args.executor,
653
- source: 'system',
654
- assigned_to: args.assigned_to || null,
655
- issue_id: args.issue_id || null,
656
- action_url: actionUrl,
657
- priority: args.priority || (args.executor === 'human' ? 3 : 2),
658
- });
659
- return {
660
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
661
- };
662
- }),
663
- tool('list_product_members', 'List all members of a product (owner + developers) with their names, emails, and IDs. Use this to look up a person by name before assigning tasks.', {
664
- product_id: z.string().describe('Product ID'),
665
- }, async (args) => {
666
- const result = await callMcpEndpoint('tasks/product_members', {
667
- product_id: args.product_id,
668
- });
669
- return {
670
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
671
- };
672
- }),
673
- tool('trigger_phase_rerun', 'Reset a workflow phase to pending so it will re-run.', {
674
- issue_id: z.string().describe('Issue ID'),
675
- phase: z.string().describe('Phase name to reset'),
676
- }, async (args) => {
677
- const issueResult = (await callMcpEndpoint('issues/get', {
678
- issue_id: args.issue_id,
679
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
680
- }));
681
- const issue = issueResult?.issues?.[0];
682
- if (!issue) {
683
- return {
684
- content: [{ type: 'text', text: 'Issue not found' }],
685
- isError: true,
686
- };
687
- }
688
- const workflow = issue.workflow || [];
689
- const normalizedPhase = args.phase.replace(/-/g, '_');
690
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
691
- const updatedWorkflow = workflow.map((p) => p.phase === normalizedPhase
692
- ? { ...p, status: 'pending', executed_at: null }
693
- : p);
694
- await callMcpEndpoint('issues/update', {
695
- issue_id: args.issue_id,
696
- workflow: updatedWorkflow,
697
- });
698
- return {
699
- content: [
700
- {
701
- type: 'text',
702
- text: `Phase "${args.phase}" reset to pending.`,
703
- },
704
- ],
705
- };
706
- }),
707
- ],
708
- });
10
+ return createChatIssueMcpServer(getToolDeps());
709
11
  }