@agllama/mcp 0.5.0

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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +248 -0
  3. package/dist/api-client.d.ts +120 -0
  4. package/dist/api-client.d.ts.map +1 -0
  5. package/dist/api-client.js +421 -0
  6. package/dist/api-client.js.map +1 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +18 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/server.d.ts +4 -0
  12. package/dist/server.d.ts.map +1 -0
  13. package/dist/server.js +673 -0
  14. package/dist/server.js.map +1 -0
  15. package/dist/tools/backlog.d.ts +22 -0
  16. package/dist/tools/backlog.d.ts.map +1 -0
  17. package/dist/tools/backlog.js +109 -0
  18. package/dist/tools/backlog.js.map +1 -0
  19. package/dist/tools/boardConfig.d.ts +77 -0
  20. package/dist/tools/boardConfig.d.ts.map +1 -0
  21. package/dist/tools/boardConfig.js +154 -0
  22. package/dist/tools/boardConfig.js.map +1 -0
  23. package/dist/tools/boardTemplates.d.ts +47 -0
  24. package/dist/tools/boardTemplates.d.ts.map +1 -0
  25. package/dist/tools/boardTemplates.js +119 -0
  26. package/dist/tools/boardTemplates.js.map +1 -0
  27. package/dist/tools/boards.d.ts +62 -0
  28. package/dist/tools/boards.d.ts.map +1 -0
  29. package/dist/tools/boards.js +260 -0
  30. package/dist/tools/boards.js.map +1 -0
  31. package/dist/tools/comments.d.ts +82 -0
  32. package/dist/tools/comments.d.ts.map +1 -0
  33. package/dist/tools/comments.js +275 -0
  34. package/dist/tools/comments.js.map +1 -0
  35. package/dist/tools/connect.d.ts +7 -0
  36. package/dist/tools/connect.d.ts.map +1 -0
  37. package/dist/tools/connect.js +50 -0
  38. package/dist/tools/connect.js.map +1 -0
  39. package/dist/tools/context.d.ts +19 -0
  40. package/dist/tools/context.d.ts.map +1 -0
  41. package/dist/tools/context.js +136 -0
  42. package/dist/tools/context.js.map +1 -0
  43. package/dist/tools/documents.d.ts +99 -0
  44. package/dist/tools/documents.d.ts.map +1 -0
  45. package/dist/tools/documents.js +252 -0
  46. package/dist/tools/documents.js.map +1 -0
  47. package/dist/tools/help.d.ts +13 -0
  48. package/dist/tools/help.d.ts.map +1 -0
  49. package/dist/tools/help.js +485 -0
  50. package/dist/tools/help.js.map +1 -0
  51. package/dist/tools/index.d.ts +21 -0
  52. package/dist/tools/index.d.ts.map +1 -0
  53. package/dist/tools/index.js +21 -0
  54. package/dist/tools/index.js.map +1 -0
  55. package/dist/tools/issueLinks.d.ts +45 -0
  56. package/dist/tools/issueLinks.d.ts.map +1 -0
  57. package/dist/tools/issueLinks.js +147 -0
  58. package/dist/tools/issueLinks.js.map +1 -0
  59. package/dist/tools/issues.d.ts +307 -0
  60. package/dist/tools/issues.d.ts.map +1 -0
  61. package/dist/tools/issues.js +715 -0
  62. package/dist/tools/issues.js.map +1 -0
  63. package/dist/tools/labels.d.ts +56 -0
  64. package/dist/tools/labels.d.ts.map +1 -0
  65. package/dist/tools/labels.js +213 -0
  66. package/dist/tools/labels.js.map +1 -0
  67. package/dist/tools/members.d.ts +13 -0
  68. package/dist/tools/members.d.ts.map +1 -0
  69. package/dist/tools/members.js +43 -0
  70. package/dist/tools/members.js.map +1 -0
  71. package/dist/tools/organizations.d.ts +35 -0
  72. package/dist/tools/organizations.d.ts.map +1 -0
  73. package/dist/tools/organizations.js +116 -0
  74. package/dist/tools/organizations.js.map +1 -0
  75. package/dist/tools/projects.d.ts +67 -0
  76. package/dist/tools/projects.d.ts.map +1 -0
  77. package/dist/tools/projects.js +165 -0
  78. package/dist/tools/projects.js.map +1 -0
  79. package/dist/tools/search.d.ts +34 -0
  80. package/dist/tools/search.d.ts.map +1 -0
  81. package/dist/tools/search.js +85 -0
  82. package/dist/tools/search.js.map +1 -0
  83. package/dist/tools/session.d.ts +38 -0
  84. package/dist/tools/session.d.ts.map +1 -0
  85. package/dist/tools/session.js +158 -0
  86. package/dist/tools/session.js.map +1 -0
  87. package/dist/tools/sprints.d.ts +141 -0
  88. package/dist/tools/sprints.d.ts.map +1 -0
  89. package/dist/tools/sprints.js +470 -0
  90. package/dist/tools/sprints.js.map +1 -0
  91. package/dist/tools/status.d.ts +22 -0
  92. package/dist/tools/status.d.ts.map +1 -0
  93. package/dist/tools/status.js +81 -0
  94. package/dist/tools/status.js.map +1 -0
  95. package/dist/tools/workflows.d.ts +137 -0
  96. package/dist/tools/workflows.d.ts.map +1 -0
  97. package/dist/tools/workflows.js +355 -0
  98. package/dist/tools/workflows.js.map +1 -0
  99. package/dist/types.d.ts +434 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +5 -0
  102. package/dist/types.js.map +1 -0
  103. package/dist/utils/column-instructions.d.ts +21 -0
  104. package/dist/utils/column-instructions.d.ts.map +1 -0
  105. package/dist/utils/column-instructions.js +54 -0
  106. package/dist/utils/column-instructions.js.map +1 -0
  107. package/package.json +63 -0
@@ -0,0 +1,715 @@
1
+ import { z } from 'zod';
2
+ import { getApiClient, LlamaApiError } from '../api-client.js';
3
+ import { getColumnInstructionsForStatus } from '../utils/column-instructions.js';
4
+ import { getSessionDefaults } from './session.js';
5
+ // ============================================
6
+ // Get Issue
7
+ // ============================================
8
+ export const getIssueToolName = 'llama_get_issue';
9
+ export const getIssueToolDescription = `Get full details for a specific issue by its key (e.g., "PROJ-123").
10
+ Returns complete issue information including description, comments, and history. Uses session defaults if orgSlug/projectKey not provided.`;
11
+ export const getIssueToolSchema = z.object({
12
+ orgSlug: z
13
+ .string()
14
+ .optional()
15
+ .describe('Organization slug (uses session default if not provided)'),
16
+ projectKey: z
17
+ .string()
18
+ .optional()
19
+ .describe('Project key (uses session default if not provided)'),
20
+ issueKey: z.string().describe('Issue key (e.g., "PROJ-123")'),
21
+ });
22
+ export async function executeGetIssue(input) {
23
+ try {
24
+ const session = await getSessionDefaults();
25
+ const orgSlug = input.orgSlug ?? session?.orgSlug;
26
+ const projectKey = input.projectKey ?? session?.projectKey;
27
+ if (!orgSlug) {
28
+ return JSON.stringify({
29
+ success: false,
30
+ error: 'orgSlug is required. Either provide it or set session context with llama_set_context first.',
31
+ });
32
+ }
33
+ if (!projectKey) {
34
+ return JSON.stringify({
35
+ success: false,
36
+ error: 'projectKey is required. Either provide it or set session context with llama_set_context first.',
37
+ });
38
+ }
39
+ const client = getApiClient();
40
+ // Record tool call
41
+ try {
42
+ await client.recordToolCall('llama_get_issue', {
43
+ orgSlug,
44
+ projectKey,
45
+ issueKey: input.issueKey,
46
+ });
47
+ }
48
+ catch {
49
+ // Ignore recording errors
50
+ }
51
+ const issue = await client.getIssue(orgSlug, projectKey, input.issueKey);
52
+ return JSON.stringify({
53
+ success: true,
54
+ issue,
55
+ });
56
+ }
57
+ catch (error) {
58
+ if (error instanceof LlamaApiError) {
59
+ return JSON.stringify({
60
+ success: false,
61
+ error: `Failed to get issue: ${error.message}`,
62
+ statusCode: error.statusCode,
63
+ });
64
+ }
65
+ return JSON.stringify({
66
+ success: false,
67
+ error: error instanceof Error ? error.message : 'Unknown error',
68
+ });
69
+ }
70
+ }
71
+ // ============================================
72
+ // Create Issue
73
+ // ============================================
74
+ export const createIssueToolName = 'llama_create_issue';
75
+ export const createIssueToolDescription = `Create a new issue in Llama.
76
+
77
+ Issue types: EPIC, STORY, TASK, BUG, SUBTASK
78
+ Priority levels: CRITICAL, HIGH, MEDIUM (default), LOW, TRIVIAL
79
+
80
+ **IMPORTANT: SUBTASK type REQUIRES parentKey** - You must provide the parent issue key (e.g., "PROJ-123") when creating a subtask. The parent must be a STORY, TASK, or BUG.
81
+
82
+ To add to a sprint, provide the sprintId from llama_context.
83
+ Uses session defaults if orgSlug/projectKey not provided.`;
84
+ export const createIssueToolSchema = z.object({
85
+ orgSlug: z
86
+ .string()
87
+ .optional()
88
+ .describe('Organization slug (uses session default if not provided)'),
89
+ projectKey: z
90
+ .string()
91
+ .optional()
92
+ .describe('Project key (uses session default if not provided)'),
93
+ summary: z.string().describe('Issue title/summary'),
94
+ description: z.string().optional().describe('Detailed description (markdown supported)'),
95
+ type: z
96
+ .enum(['EPIC', 'STORY', 'TASK', 'BUG', 'SUBTASK'])
97
+ .describe('Issue type'),
98
+ priority: z
99
+ .enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'TRIVIAL'])
100
+ .optional()
101
+ .describe('Priority level (default: MEDIUM)'),
102
+ assigneeId: z.string().optional().describe('User ID to assign the issue to'),
103
+ sprintId: z.string().optional().describe('Sprint ID to add the issue to'),
104
+ parentKey: z.string().optional().describe('Parent issue key - REQUIRED for SUBTASK type (e.g., "PROJ-123"). Parent must be STORY, TASK, or BUG.'),
105
+ epicKey: z.string().optional().describe('Epic issue key to link this issue to (for STORY, TASK, BUG)'),
106
+ estimate: z.number().optional().describe('Story points estimate'),
107
+ labels: z.array(z.string()).optional().describe('Labels to attach'),
108
+ });
109
+ export async function executeCreateIssue(input) {
110
+ try {
111
+ const session = await getSessionDefaults();
112
+ const orgSlug = input.orgSlug ?? session?.orgSlug;
113
+ const projectKey = input.projectKey ?? session?.projectKey;
114
+ if (!orgSlug) {
115
+ return JSON.stringify({
116
+ success: false,
117
+ error: 'orgSlug is required. Either provide it or set session context with llama_set_context first.',
118
+ });
119
+ }
120
+ if (!projectKey) {
121
+ return JSON.stringify({
122
+ success: false,
123
+ error: 'projectKey is required. Either provide it or set session context with llama_set_context first.',
124
+ });
125
+ }
126
+ const client = getApiClient();
127
+ // Validate SUBTASK requires parentKey
128
+ if (input.type === 'SUBTASK' && !input.parentKey) {
129
+ return JSON.stringify({
130
+ success: false,
131
+ error: 'SUBTASK type requires parentKey. You must specify the parent issue key (e.g., "PROJ-123") when creating a subtask. The parent must be a STORY, TASK, or BUG.',
132
+ });
133
+ }
134
+ // Resolve epicKey to parentId if provided
135
+ let parentId;
136
+ if (input.epicKey) {
137
+ // Look up the Epic to get its ID
138
+ const epic = await client.getIssue(orgSlug, projectKey, input.epicKey);
139
+ if (epic.type !== 'EPIC') {
140
+ return JSON.stringify({
141
+ success: false,
142
+ error: `Issue ${input.epicKey} is not an Epic (type: ${epic.type})`,
143
+ });
144
+ }
145
+ parentId = epic.id;
146
+ }
147
+ else if (input.parentKey) {
148
+ // For subtasks, look up the parent to get its ID
149
+ const parent = await client.getIssue(orgSlug, projectKey, input.parentKey);
150
+ parentId = parent.id;
151
+ }
152
+ // Record tool call
153
+ try {
154
+ await client.recordToolCall('llama_create_issue', {
155
+ orgSlug,
156
+ projectKey,
157
+ summary: input.summary,
158
+ type: input.type,
159
+ });
160
+ }
161
+ catch {
162
+ // Ignore recording errors
163
+ }
164
+ const issue = await client.createIssue(orgSlug, projectKey, {
165
+ summary: input.summary,
166
+ description: input.description,
167
+ type: input.type,
168
+ priority: input.priority,
169
+ assigneeId: input.assigneeId,
170
+ sprintId: input.sprintId,
171
+ parentId,
172
+ estimate: input.estimate,
173
+ labels: input.labels,
174
+ });
175
+ return JSON.stringify({
176
+ success: true,
177
+ message: `Created issue ${issue.key}`,
178
+ issue: {
179
+ key: issue.key,
180
+ summary: issue.summary,
181
+ type: issue.type,
182
+ status: issue.status,
183
+ priority: issue.priority,
184
+ },
185
+ });
186
+ }
187
+ catch (error) {
188
+ if (error instanceof LlamaApiError) {
189
+ return JSON.stringify({
190
+ success: false,
191
+ error: `Failed to create issue: ${error.message}`,
192
+ statusCode: error.statusCode,
193
+ });
194
+ }
195
+ return JSON.stringify({
196
+ success: false,
197
+ error: error instanceof Error ? error.message : 'Unknown error',
198
+ });
199
+ }
200
+ }
201
+ // ============================================
202
+ // Update Issue
203
+ // ============================================
204
+ export const updateIssueToolName = 'llama_update_issue';
205
+ export const updateIssueToolDescription = `Update an existing issue's fields.
206
+ Only include the fields you want to change.
207
+ Set assigneeId or sprintId to null to unassign/remove from sprint.
208
+ Uses session defaults if orgSlug/projectKey not provided.`;
209
+ export const updateIssueToolSchema = z.object({
210
+ orgSlug: z
211
+ .string()
212
+ .optional()
213
+ .describe('Organization slug (uses session default if not provided)'),
214
+ projectKey: z
215
+ .string()
216
+ .optional()
217
+ .describe('Project key (uses session default if not provided)'),
218
+ issueKey: z.string().describe('Issue key to update'),
219
+ summary: z.string().optional().describe('New summary'),
220
+ description: z.string().optional().describe('New description'),
221
+ type: z.enum(['EPIC', 'STORY', 'TASK', 'BUG', 'SUBTASK']).optional(),
222
+ priority: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'TRIVIAL']).optional(),
223
+ statusId: z.string().optional().describe('Status ID to transition to'),
224
+ assigneeId: z
225
+ .string()
226
+ .nullable()
227
+ .optional()
228
+ .describe('User ID or null to unassign'),
229
+ sprintId: z
230
+ .string()
231
+ .nullable()
232
+ .optional()
233
+ .describe('Sprint ID or null to remove from sprint'),
234
+ epicKey: z
235
+ .string()
236
+ .nullable()
237
+ .optional()
238
+ .describe('Epic issue key to link to, or null to remove from Epic'),
239
+ estimate: z.number().nullable().optional().describe('Story points'),
240
+ labels: z.array(z.string()).optional().describe('Replace all labels'),
241
+ });
242
+ export async function executeUpdateIssue(input) {
243
+ try {
244
+ const session = await getSessionDefaults();
245
+ const orgSlug = input.orgSlug ?? session?.orgSlug;
246
+ const projectKey = input.projectKey ?? session?.projectKey;
247
+ if (!orgSlug) {
248
+ return JSON.stringify({
249
+ success: false,
250
+ error: 'orgSlug is required. Either provide it or set session context with llama_set_context first.',
251
+ });
252
+ }
253
+ if (!projectKey) {
254
+ return JSON.stringify({
255
+ success: false,
256
+ error: 'projectKey is required. Either provide it or set session context with llama_set_context first.',
257
+ });
258
+ }
259
+ const { issueKey, ...updates } = input;
260
+ const client = getApiClient();
261
+ // Build update object with only provided fields
262
+ const updateFields = {};
263
+ if (updates.summary !== undefined)
264
+ updateFields.summary = updates.summary;
265
+ if (updates.description !== undefined)
266
+ updateFields.description = updates.description;
267
+ if (updates.type !== undefined)
268
+ updateFields.type = updates.type;
269
+ if (updates.priority !== undefined)
270
+ updateFields.priority = updates.priority;
271
+ if (updates.statusId !== undefined)
272
+ updateFields.statusId = updates.statusId;
273
+ if (updates.assigneeId !== undefined)
274
+ updateFields.assigneeId = updates.assigneeId;
275
+ if (updates.sprintId !== undefined)
276
+ updateFields.sprintId = updates.sprintId;
277
+ if (updates.estimate !== undefined)
278
+ updateFields.estimate = updates.estimate;
279
+ if (updates.labels !== undefined)
280
+ updateFields.labels = updates.labels;
281
+ // Handle epicKey -> parentId conversion
282
+ if (updates.epicKey !== undefined) {
283
+ if (updates.epicKey === null) {
284
+ updateFields.parentId = null;
285
+ }
286
+ else {
287
+ // Look up the Epic to get its ID
288
+ const epic = await client.getIssue(orgSlug, projectKey, updates.epicKey);
289
+ if (epic.type !== 'EPIC') {
290
+ return JSON.stringify({
291
+ success: false,
292
+ error: `Issue ${updates.epicKey} is not an Epic (type: ${epic.type})`,
293
+ });
294
+ }
295
+ updateFields.parentId = epic.id;
296
+ }
297
+ }
298
+ // Record tool call
299
+ try {
300
+ await client.recordToolCall('llama_update_issue', {
301
+ orgSlug,
302
+ projectKey,
303
+ issueKey,
304
+ updates: Object.keys(updateFields),
305
+ });
306
+ }
307
+ catch {
308
+ // Ignore recording errors
309
+ }
310
+ const issue = await client.updateIssue(orgSlug, projectKey, issueKey, updateFields);
311
+ // Check if status was changed and get column instructions
312
+ let columnInstructions = null;
313
+ if (updates.statusId !== undefined) {
314
+ columnInstructions = await getColumnInstructionsForStatus(orgSlug, projectKey, updates.statusId);
315
+ }
316
+ const response = {
317
+ success: true,
318
+ message: `Updated issue ${issue.key}`,
319
+ issue: {
320
+ key: issue.key,
321
+ summary: issue.summary,
322
+ type: issue.type,
323
+ status: issue.status,
324
+ priority: issue.priority,
325
+ assignee: issue.assignee,
326
+ },
327
+ };
328
+ // Add column instructions if they exist
329
+ if (columnInstructions) {
330
+ response.columnInstructions = columnInstructions;
331
+ }
332
+ return JSON.stringify(response);
333
+ }
334
+ catch (error) {
335
+ if (error instanceof LlamaApiError) {
336
+ return JSON.stringify({
337
+ success: false,
338
+ error: `Failed to update issue: ${error.message}`,
339
+ statusCode: error.statusCode,
340
+ });
341
+ }
342
+ return JSON.stringify({
343
+ success: false,
344
+ error: error instanceof Error ? error.message : 'Unknown error',
345
+ });
346
+ }
347
+ }
348
+ // ============================================
349
+ // Delete Issue (Soft)
350
+ // ============================================
351
+ export const deleteIssueToolName = 'llama_delete_issue';
352
+ export const deleteIssueToolDescription = `Soft-delete an issue. The issue can be restored later if needed.
353
+ Use with caution - this action removes the issue from views.
354
+ Returns the number of subtasks that were also deleted.
355
+ Uses session defaults if orgSlug/projectKey not provided.`;
356
+ export const deleteIssueToolSchema = z.object({
357
+ orgSlug: z
358
+ .string()
359
+ .optional()
360
+ .describe('Organization slug (uses session default if not provided)'),
361
+ projectKey: z
362
+ .string()
363
+ .optional()
364
+ .describe('Project key (uses session default if not provided)'),
365
+ issueKey: z.string().describe('Issue key to delete'),
366
+ });
367
+ export async function executeDeleteIssue(input) {
368
+ try {
369
+ const session = await getSessionDefaults();
370
+ const orgSlug = input.orgSlug ?? session?.orgSlug;
371
+ const projectKey = input.projectKey ?? session?.projectKey;
372
+ if (!orgSlug) {
373
+ return JSON.stringify({
374
+ success: false,
375
+ error: 'orgSlug is required. Either provide it or set session context with llama_set_context first.',
376
+ });
377
+ }
378
+ if (!projectKey) {
379
+ return JSON.stringify({
380
+ success: false,
381
+ error: 'projectKey is required. Either provide it or set session context with llama_set_context first.',
382
+ });
383
+ }
384
+ const client = getApiClient();
385
+ // Record tool call
386
+ try {
387
+ await client.recordToolCall('llama_delete_issue', {
388
+ orgSlug,
389
+ projectKey,
390
+ issueKey: input.issueKey,
391
+ });
392
+ }
393
+ catch {
394
+ // Ignore recording errors
395
+ }
396
+ const result = await client.deleteIssue(orgSlug, projectKey, input.issueKey);
397
+ return JSON.stringify({
398
+ success: true,
399
+ message: `Deleted issue ${input.issueKey}`,
400
+ subtasksDeleted: result.subtasksDeleted,
401
+ impact: result.subtasksDeleted > 0
402
+ ? `${result.subtasksDeleted} subtask${result.subtasksDeleted === 1 ? '' : 's'} also deleted`
403
+ : 'No subtasks were affected',
404
+ });
405
+ }
406
+ catch (error) {
407
+ if (error instanceof LlamaApiError) {
408
+ return JSON.stringify({
409
+ success: false,
410
+ error: `Failed to delete issue: ${error.message}`,
411
+ statusCode: error.statusCode,
412
+ });
413
+ }
414
+ return JSON.stringify({
415
+ success: false,
416
+ error: error instanceof Error ? error.message : 'Unknown error',
417
+ });
418
+ }
419
+ }
420
+ // ============================================
421
+ // Batch Create Issues
422
+ // ============================================
423
+ export const batchCreateIssuesToolName = 'llama_batch_create_issues';
424
+ export const batchCreateIssuesToolDescription = `Create multiple issues in one call (max 20).
425
+ Returns individual success/failure for each issue.
426
+ Uses session defaults if orgSlug/projectKey not provided.
427
+
428
+ **IMPORTANT: SUBTASK type REQUIRES parentKey** - When creating subtasks, you must provide the parentKey field.
429
+
430
+ Example:
431
+ issues: [
432
+ { summary: "Issue 1", type: "TASK" },
433
+ { summary: "Issue 2", type: "BUG", priority: "HIGH" },
434
+ { summary: "Subtask 1", type: "SUBTASK", parentKey: "PROJ-1" }
435
+ ]`;
436
+ export const batchCreateIssuesToolSchema = z.object({
437
+ orgSlug: z
438
+ .string()
439
+ .optional()
440
+ .describe('Organization slug (uses session default if not provided)'),
441
+ projectKey: z
442
+ .string()
443
+ .optional()
444
+ .describe('Project key (uses session default if not provided)'),
445
+ issues: z
446
+ .array(z.object({
447
+ summary: z.string().describe('Issue title/summary'),
448
+ type: z.enum(['EPIC', 'STORY', 'TASK', 'BUG', 'SUBTASK']).describe('Issue type'),
449
+ description: z.string().optional().nullable().describe('Description (markdown)'),
450
+ priority: z
451
+ .enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'TRIVIAL'])
452
+ .optional()
453
+ .describe('Priority (default: MEDIUM)'),
454
+ assigneeId: z.string().optional().nullable().describe('Assignee user ID'),
455
+ sprintId: z.string().optional().nullable().describe('Sprint ID'),
456
+ estimate: z.number().optional().nullable().describe('Story points'),
457
+ labels: z.array(z.string()).optional().describe('Labels'),
458
+ epicKey: z.string().optional().nullable().describe('Epic key to link to (for STORY, TASK, BUG)'),
459
+ parentKey: z.string().optional().nullable().describe('Parent issue key - REQUIRED for SUBTASK type'),
460
+ }))
461
+ .min(1)
462
+ .max(20)
463
+ .describe('Issues to create (max 20)'),
464
+ });
465
+ export async function executeBatchCreateIssues(input) {
466
+ try {
467
+ const session = await getSessionDefaults();
468
+ const orgSlug = input.orgSlug ?? session?.orgSlug;
469
+ const projectKey = input.projectKey ?? session?.projectKey;
470
+ if (!orgSlug) {
471
+ return JSON.stringify({
472
+ success: false,
473
+ error: 'orgSlug is required. Either provide it or set session context with llama_set_context first.',
474
+ });
475
+ }
476
+ if (!projectKey) {
477
+ return JSON.stringify({
478
+ success: false,
479
+ error: 'projectKey is required. Either provide it or set session context with llama_set_context first.',
480
+ });
481
+ }
482
+ const client = getApiClient();
483
+ // Validate SUBTASK issues have parentKey
484
+ const subtasksWithoutParent = input.issues.filter((i) => i.type === 'SUBTASK' && !i.parentKey);
485
+ if (subtasksWithoutParent.length > 0) {
486
+ return JSON.stringify({
487
+ success: false,
488
+ error: `SUBTASK type requires parentKey. The following issues are missing parentKey: ${subtasksWithoutParent.map((i) => i.summary).join(', ')}`,
489
+ });
490
+ }
491
+ // Record tool call
492
+ try {
493
+ await client.recordToolCall('llama_batch_create_issues', {
494
+ orgSlug,
495
+ projectKey,
496
+ count: input.issues.length,
497
+ });
498
+ }
499
+ catch {
500
+ // Ignore recording errors
501
+ }
502
+ const issues = input.issues.map((i) => ({
503
+ summary: i.summary,
504
+ type: i.type,
505
+ description: i.description,
506
+ priority: i.priority,
507
+ assigneeId: i.assigneeId,
508
+ sprintId: i.sprintId,
509
+ estimate: i.estimate,
510
+ labels: i.labels,
511
+ epicKey: i.epicKey,
512
+ parentKey: i.parentKey,
513
+ }));
514
+ const result = await client.batchCreateIssues(orgSlug, projectKey, issues);
515
+ return JSON.stringify({
516
+ success: true,
517
+ message: `Created ${result.succeeded} of ${result.total} issues`,
518
+ ...result,
519
+ });
520
+ }
521
+ catch (error) {
522
+ if (error instanceof LlamaApiError) {
523
+ return JSON.stringify({
524
+ success: false,
525
+ error: `Failed to batch create issues: ${error.message}`,
526
+ statusCode: error.statusCode,
527
+ });
528
+ }
529
+ return JSON.stringify({
530
+ success: false,
531
+ error: error instanceof Error ? error.message : 'Unknown error',
532
+ });
533
+ }
534
+ }
535
+ // ============================================
536
+ // Batch Update Status
537
+ // ============================================
538
+ export const batchUpdateStatusToolName = 'llama_batch_update_status';
539
+ export const batchUpdateStatusToolDescription = `Move multiple issues to the same status in one call (max 50).
540
+ Returns individual success/failure for each issue.
541
+ Uses session defaults if orgSlug/projectKey not provided.
542
+
543
+ First use llama_context to get valid status IDs.`;
544
+ export const batchUpdateStatusToolSchema = z.object({
545
+ orgSlug: z
546
+ .string()
547
+ .optional()
548
+ .describe('Organization slug (uses session default if not provided)'),
549
+ projectKey: z
550
+ .string()
551
+ .optional()
552
+ .describe('Project key (uses session default if not provided)'),
553
+ issueKeys: z
554
+ .array(z.string())
555
+ .min(1)
556
+ .max(50)
557
+ .describe('Issue keys to update (max 50)'),
558
+ statusId: z.string().describe('Target status ID (from llama_context)'),
559
+ });
560
+ export async function executeBatchUpdateStatus(input) {
561
+ try {
562
+ const session = await getSessionDefaults();
563
+ const orgSlug = input.orgSlug ?? session?.orgSlug;
564
+ const projectKey = input.projectKey ?? session?.projectKey;
565
+ if (!orgSlug) {
566
+ return JSON.stringify({
567
+ success: false,
568
+ error: 'orgSlug is required. Either provide it or set session context with llama_set_context first.',
569
+ });
570
+ }
571
+ if (!projectKey) {
572
+ return JSON.stringify({
573
+ success: false,
574
+ error: 'projectKey is required. Either provide it or set session context with llama_set_context first.',
575
+ });
576
+ }
577
+ const client = getApiClient();
578
+ // Record tool call
579
+ try {
580
+ await client.recordToolCall('llama_batch_update_status', {
581
+ orgSlug,
582
+ projectKey,
583
+ count: input.issueKeys.length,
584
+ statusId: input.statusId,
585
+ });
586
+ }
587
+ catch {
588
+ // Ignore recording errors
589
+ }
590
+ const result = await client.batchUpdateStatus(orgSlug, projectKey, input.issueKeys, input.statusId);
591
+ return JSON.stringify({
592
+ success: true,
593
+ message: `Updated ${result.succeeded} of ${result.total} issues`,
594
+ ...result,
595
+ });
596
+ }
597
+ catch (error) {
598
+ if (error instanceof LlamaApiError) {
599
+ return JSON.stringify({
600
+ success: false,
601
+ error: `Failed to batch update status: ${error.message}`,
602
+ statusCode: error.statusCode,
603
+ });
604
+ }
605
+ return JSON.stringify({
606
+ success: false,
607
+ error: error instanceof Error ? error.message : 'Unknown error',
608
+ });
609
+ }
610
+ }
611
+ // ============================================
612
+ // Batch Update Issues
613
+ // ============================================
614
+ export const batchUpdateIssuesToolName = 'llama_batch_update_issues';
615
+ export const batchUpdateIssuesToolDescription = `Update multiple issues with different field changes (max 50).
616
+ Returns individual success/failure for each issue.
617
+ Uses session defaults if orgSlug/projectKey not provided.
618
+
619
+ Example:
620
+ updates: [
621
+ { issueKey: "PROJ-1", fields: { priority: "HIGH" } },
622
+ { issueKey: "PROJ-2", fields: { assigneeId: "user-id", estimate: 5 } }
623
+ ]`;
624
+ export const batchUpdateIssuesToolSchema = z.object({
625
+ orgSlug: z
626
+ .string()
627
+ .optional()
628
+ .describe('Organization slug (uses session default if not provided)'),
629
+ projectKey: z
630
+ .string()
631
+ .optional()
632
+ .describe('Project key (uses session default if not provided)'),
633
+ updates: z
634
+ .array(z.object({
635
+ issueKey: z.string().describe('Issue key to update'),
636
+ fields: z.object({
637
+ summary: z.string().optional().describe('New summary'),
638
+ description: z.string().optional().nullable().describe('New description'),
639
+ priority: z
640
+ .enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'TRIVIAL'])
641
+ .optional()
642
+ .describe('New priority'),
643
+ assigneeId: z.string().optional().nullable().describe('Assignee ID or null'),
644
+ sprintId: z.string().optional().nullable().describe('Sprint ID or null'),
645
+ estimate: z.number().optional().nullable().describe('Story points'),
646
+ labels: z.array(z.string()).optional().describe('Replace all labels'),
647
+ }),
648
+ }))
649
+ .min(1)
650
+ .max(50)
651
+ .describe('Updates to apply (max 50)'),
652
+ });
653
+ export async function executeBatchUpdateIssues(input) {
654
+ try {
655
+ const session = await getSessionDefaults();
656
+ const orgSlug = input.orgSlug ?? session?.orgSlug;
657
+ const projectKey = input.projectKey ?? session?.projectKey;
658
+ if (!orgSlug) {
659
+ return JSON.stringify({
660
+ success: false,
661
+ error: 'orgSlug is required. Either provide it or set session context with llama_set_context first.',
662
+ });
663
+ }
664
+ if (!projectKey) {
665
+ return JSON.stringify({
666
+ success: false,
667
+ error: 'projectKey is required. Either provide it or set session context with llama_set_context first.',
668
+ });
669
+ }
670
+ const client = getApiClient();
671
+ // Record tool call
672
+ try {
673
+ await client.recordToolCall('llama_batch_update_issues', {
674
+ orgSlug,
675
+ projectKey,
676
+ count: input.updates.length,
677
+ });
678
+ }
679
+ catch {
680
+ // Ignore recording errors
681
+ }
682
+ const updates = input.updates.map((u) => ({
683
+ issueKey: u.issueKey,
684
+ fields: {
685
+ summary: u.fields.summary,
686
+ description: u.fields.description,
687
+ priority: u.fields.priority,
688
+ assigneeId: u.fields.assigneeId,
689
+ sprintId: u.fields.sprintId,
690
+ estimate: u.fields.estimate,
691
+ labels: u.fields.labels,
692
+ },
693
+ }));
694
+ const result = await client.batchUpdateIssues(orgSlug, projectKey, updates);
695
+ return JSON.stringify({
696
+ success: true,
697
+ message: `Updated ${result.succeeded} of ${result.total} issues`,
698
+ ...result,
699
+ });
700
+ }
701
+ catch (error) {
702
+ if (error instanceof LlamaApiError) {
703
+ return JSON.stringify({
704
+ success: false,
705
+ error: `Failed to batch update issues: ${error.message}`,
706
+ statusCode: error.statusCode,
707
+ });
708
+ }
709
+ return JSON.stringify({
710
+ success: false,
711
+ error: error instanceof Error ? error.message : 'Unknown error',
712
+ });
713
+ }
714
+ }
715
+ //# sourceMappingURL=issues.js.map