@jagadeesh52423/atlassian-mcp-server 1.1.1 → 1.1.2
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/formatters/adf-formatter.d.ts +30 -2
- package/dist/formatters/adf-formatter.d.ts.map +1 -1
- package/dist/formatters/adf-formatter.js +423 -107
- package/dist/formatters/adf-formatter.js.map +1 -1
- package/dist/formatters/index.d.ts +1 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +1 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/handlers/base-handler.d.ts +5 -0
- package/dist/handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/base-handler.js +7 -0
- package/dist/handlers/base-handler.js.map +1 -0
- package/dist/handlers/bitbucket-handlers.d.ts +2 -1
- package/dist/handlers/bitbucket-handlers.d.ts.map +1 -1
- package/dist/handlers/bitbucket-handlers.js +31 -191
- package/dist/handlers/bitbucket-handlers.js.map +1 -1
- package/dist/handlers/confluence-handlers.d.ts +2 -1
- package/dist/handlers/confluence-handlers.d.ts.map +1 -1
- package/dist/handlers/confluence-handlers.js +46 -243
- package/dist/handlers/confluence-handlers.js.map +1 -1
- package/dist/handlers/jira-handlers.d.ts +9 -1
- package/dist/handlers/jira-handlers.d.ts.map +1 -1
- package/dist/handlers/jira-handlers.js +144 -575
- package/dist/handlers/jira-handlers.js.map +1 -1
- package/dist/index.js +111 -189
- package/dist/index.js.map +1 -1
- package/dist/registry/tool-registry.d.ts +11 -0
- package/dist/registry/tool-registry.d.ts.map +1 -0
- package/dist/registry/tool-registry.js +24 -0
- package/dist/registry/tool-registry.js.map +1 -0
- package/dist/services/bitbucket.d.ts +2 -0
- package/dist/services/bitbucket.d.ts.map +1 -1
- package/dist/services/bitbucket.js +22 -74
- package/dist/services/bitbucket.js.map +1 -1
- package/dist/services/confluence.d.ts.map +1 -1
- package/dist/services/confluence.js +5 -4
- package/dist/services/confluence.js.map +1 -1
- package/dist/services/jira/agile.d.ts +33 -0
- package/dist/services/jira/agile.d.ts.map +1 -0
- package/dist/services/jira/agile.js +118 -0
- package/dist/services/jira/agile.js.map +1 -0
- package/dist/services/jira/attachments.d.ts +7 -0
- package/dist/services/jira/attachments.d.ts.map +1 -0
- package/dist/services/jira/attachments.js +26 -0
- package/dist/services/jira/attachments.js.map +1 -0
- package/dist/services/jira/base.d.ts +9 -0
- package/dist/services/jira/base.d.ts.map +1 -0
- package/dist/services/jira/base.js +11 -0
- package/dist/services/jira/base.js.map +1 -0
- package/dist/services/jira/comments.d.ts +8 -0
- package/dist/services/jira/comments.d.ts.map +1 -0
- package/dist/services/jira/comments.js +33 -0
- package/dist/services/jira/comments.js.map +1 -0
- package/dist/services/jira/issues.d.ts +32 -0
- package/dist/services/jira/issues.d.ts.map +1 -0
- package/dist/services/jira/issues.js +177 -0
- package/dist/services/jira/issues.js.map +1 -0
- package/dist/services/jira/labels.d.ts +7 -0
- package/dist/services/jira/labels.d.ts.map +1 -0
- package/dist/services/jira/labels.js +21 -0
- package/dist/services/jira/labels.js.map +1 -0
- package/dist/services/jira/links.d.ts +9 -0
- package/dist/services/jira/links.d.ts.map +1 -0
- package/dist/services/jira/links.js +55 -0
- package/dist/services/jira/links.js.map +1 -0
- package/dist/services/jira/metadata.d.ts +8 -0
- package/dist/services/jira/metadata.d.ts.map +1 -0
- package/dist/services/jira/metadata.js +32 -0
- package/dist/services/jira/metadata.js.map +1 -0
- package/dist/services/jira/subtasks.d.ts +9 -0
- package/dist/services/jira/subtasks.d.ts.map +1 -0
- package/dist/services/jira/subtasks.js +32 -0
- package/dist/services/jira/subtasks.js.map +1 -0
- package/dist/services/jira/transitions.d.ts +16 -0
- package/dist/services/jira/transitions.d.ts.map +1 -0
- package/dist/services/jira/transitions.js +203 -0
- package/dist/services/jira/transitions.js.map +1 -0
- package/dist/services/jira/versions.d.ts +19 -0
- package/dist/services/jira/versions.d.ts.map +1 -0
- package/dist/services/jira/versions.js +62 -0
- package/dist/services/jira/versions.js.map +1 -0
- package/dist/services/jira/watchers.d.ts +7 -0
- package/dist/services/jira/watchers.d.ts.map +1 -0
- package/dist/services/jira/watchers.js +23 -0
- package/dist/services/jira/watchers.js.map +1 -0
- package/dist/services/jira/worklogs.d.ts +8 -0
- package/dist/services/jira/worklogs.d.ts.map +1 -0
- package/dist/services/jira/worklogs.js +37 -0
- package/dist/services/jira/worklogs.js.map +1 -0
- package/dist/services/jira.d.ts +58 -103
- package/dist/services/jira.d.ts.map +1 -1
- package/dist/services/jira.js +100 -1481
- package/dist/services/jira.js.map +1 -1
- package/dist/tools/jira-tools.d.ts.map +1 -1
- package/dist/tools/jira-tools.js +35 -3
- package/dist/tools/jira-tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,46 +1,60 @@
|
|
|
1
1
|
import { successResponse, jsonResponse, errorResponse, ERROR_TIPS, } from '../error-handler.js';
|
|
2
2
|
import { ICONS } from '../constants.js';
|
|
3
|
+
import { BaseHandler } from './base-handler.js';
|
|
3
4
|
/**
|
|
4
5
|
* Jira tool handlers
|
|
5
6
|
*/
|
|
6
|
-
export class JiraHandlers {
|
|
7
|
+
export class JiraHandlers extends BaseHandler {
|
|
7
8
|
service;
|
|
8
9
|
constructor(service) {
|
|
10
|
+
super();
|
|
9
11
|
this.service = service;
|
|
10
12
|
}
|
|
11
13
|
async searchIssues(args) {
|
|
12
14
|
const { query, assignee, status, project, labels, reporter, createdAfter, updatedAfter, jql, fields, limit } = args;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
if (fields !== undefined && !Array.isArray(fields)) {
|
|
16
|
+
return errorResponse(new Error('Invalid fields parameter'), {
|
|
17
|
+
operation: 'Failed to search Jira issues',
|
|
18
|
+
params: {},
|
|
19
|
+
tip: 'The "fields" parameter must be an array of strings, e.g. ["summary", "status", "customfield_10020"]. Do not pass it as a string.',
|
|
20
|
+
});
|
|
16
21
|
}
|
|
17
|
-
|
|
18
|
-
return errorResponse(
|
|
22
|
+
if (labels !== undefined && !Array.isArray(labels)) {
|
|
23
|
+
return errorResponse(new Error('Invalid labels parameter'), {
|
|
19
24
|
operation: 'Failed to search Jira issues',
|
|
20
|
-
params: {
|
|
21
|
-
tip:
|
|
25
|
+
params: {},
|
|
26
|
+
tip: 'The "labels" parameter must be an array of strings, e.g. ["bug", "urgent"]. Do not pass it as a string.',
|
|
22
27
|
});
|
|
23
28
|
}
|
|
29
|
+
return this.handle(() => this.service.searchIssues({ query, assignee, status, project, labels, reporter, createdAfter, updatedAfter, jql, fields, limit }), { operation: 'Failed to search Jira issues', params: { Query: query || 'none', Assignee: assignee || 'none', JQL: jql || 'none', Limit: limit || 50 }, tip: ERROR_TIPS.JIRA_SEARCH }, jsonResponse);
|
|
24
30
|
}
|
|
25
31
|
async getIssue(args) {
|
|
26
|
-
const { issueKey } = args;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return jsonResponse(issue);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
return errorResponse(error, {
|
|
32
|
+
const { issueKey, fields } = args;
|
|
33
|
+
if (fields !== undefined && !Array.isArray(fields)) {
|
|
34
|
+
return errorResponse(new Error('Invalid fields parameter'), {
|
|
33
35
|
operation: 'Failed to get Jira issue',
|
|
34
36
|
params: { 'Issue Key': issueKey },
|
|
35
|
-
tip:
|
|
37
|
+
tip: 'The "fields" parameter must be an array of strings, e.g. ["summary", "status", "customfield_10020"]. Do not pass it as a string.',
|
|
36
38
|
});
|
|
37
39
|
}
|
|
40
|
+
return this.handle(() => this.service.getIssue(issueKey, fields), { operation: 'Failed to get Jira issue', params: { 'Issue Key': issueKey, Fields: fields?.join(', ') || 'default' }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
38
41
|
}
|
|
39
42
|
async createIssue(args) {
|
|
40
43
|
const { projectKey, summary, description, issueType, duedate, priority, labels, components, assignee, customFields } = args;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
return this.handle(() => this.service.createIssue(projectKey, summary, description, issueType, { duedate, priority, labels, components, assignee, customFields }), {
|
|
45
|
+
operation: 'Failed to create Jira issue',
|
|
46
|
+
params: {
|
|
47
|
+
Project: projectKey,
|
|
48
|
+
Summary: summary,
|
|
49
|
+
'Issue Type': issueType || 'Task',
|
|
50
|
+
'Due Date': duedate || 'none',
|
|
51
|
+
Priority: priority || 'none',
|
|
52
|
+
Labels: labels?.join(', ') || 'none',
|
|
53
|
+
Components: components?.join(', ') || 'none',
|
|
54
|
+
Assignee: assignee || 'none',
|
|
55
|
+
},
|
|
56
|
+
tip: ERROR_TIPS.JIRA_ISSUE_CREATE + ' For custom fields, ensure field IDs are correct.',
|
|
57
|
+
}, (issue) => {
|
|
44
58
|
const optionalFields = [];
|
|
45
59
|
if (duedate)
|
|
46
60
|
optionalFields.push(`${ICONS.DATE} **Due Date:** ${duedate}`);
|
|
@@ -62,28 +76,15 @@ export class JiraHandlers {
|
|
|
62
76
|
`${ICONS.DATE} **Created:** ${new Date(issue.created).toLocaleString()}${optionalFieldsText}\n\n` +
|
|
63
77
|
`${ICONS.DESCRIPTION} **Description:** Successfully created with provided content\n\n` +
|
|
64
78
|
`${ICONS.LINK} **Issue URL:** [View Issue](https://${domain}/browse/${issue.key})`);
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
return errorResponse(error, {
|
|
68
|
-
operation: 'Failed to create Jira issue',
|
|
69
|
-
params: {
|
|
70
|
-
Project: projectKey,
|
|
71
|
-
Summary: summary,
|
|
72
|
-
'Issue Type': issueType || 'Task',
|
|
73
|
-
'Due Date': duedate || 'none',
|
|
74
|
-
Priority: priority || 'none',
|
|
75
|
-
Labels: labels?.join(', ') || 'none',
|
|
76
|
-
Components: components?.join(', ') || 'none',
|
|
77
|
-
Assignee: assignee || 'none',
|
|
78
|
-
},
|
|
79
|
-
tip: ERROR_TIPS.JIRA_ISSUE_CREATE + ' For custom fields, ensure field IDs are correct.',
|
|
80
|
-
});
|
|
81
|
-
}
|
|
79
|
+
});
|
|
82
80
|
}
|
|
83
81
|
async updateIssue(args) {
|
|
84
|
-
const { issueKey, summary, description, assignee } = args;
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
const { issueKey, summary, description, assignee, customFields } = args;
|
|
83
|
+
return this.handle(() => this.service.updateIssue(issueKey, { summary, description, assignee, customFields }), {
|
|
84
|
+
operation: `Failed to update Jira issue ${issueKey}`,
|
|
85
|
+
params: { Summary: summary || 'unchanged', Description: description || 'unchanged', Assignee: assignee || 'unchanged' },
|
|
86
|
+
tip: ERROR_TIPS.JIRA_ISSUE_EDIT,
|
|
87
|
+
}, (issue) => {
|
|
87
88
|
const updatedFields = [];
|
|
88
89
|
if (summary)
|
|
89
90
|
updatedFields.push(`${ICONS.SUMMARY} **Summary:** Updated to "${issue.summary}"`);
|
|
@@ -91,6 +92,8 @@ export class JiraHandlers {
|
|
|
91
92
|
updatedFields.push(`${ICONS.DESCRIPTION} **Description:** Updated successfully`);
|
|
92
93
|
if (assignee)
|
|
93
94
|
updatedFields.push(`${ICONS.ASSIGNEE} **Assignee:** ${issue.assignee || assignee}`);
|
|
95
|
+
if (customFields)
|
|
96
|
+
updatedFields.push(`**Custom Fields:** ${Object.keys(customFields).join(', ')} updated`);
|
|
94
97
|
const updatedFieldsText = updatedFields.length > 0 ? `\n\n**Updated Fields:**\n${updatedFields.join('\n')}` : '';
|
|
95
98
|
const domain = process.env.JIRA_DOMAIN || process.env.ATLASSIAN_DOMAIN || 'your-domain.atlassian.net';
|
|
96
99
|
return successResponse(`${ICONS.SUCCESS} **Jira Issue Updated Successfully**\n\n` +
|
|
@@ -98,91 +101,30 @@ export class JiraHandlers {
|
|
|
98
101
|
`${ICONS.STATUS} **Current Status:** ${issue.status}\n` +
|
|
99
102
|
`${ICONS.DATE} **Last Updated:** ${new Date(issue.updated).toLocaleString()}${updatedFieldsText}\n\n` +
|
|
100
103
|
`${ICONS.LINK} **Issue URL:** [View Issue](https://${domain}/browse/${issue.key})`);
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
return errorResponse(error, {
|
|
104
|
-
operation: `Failed to update Jira issue ${issueKey}`,
|
|
105
|
-
params: {
|
|
106
|
-
Summary: summary || 'unchanged',
|
|
107
|
-
Description: description || 'unchanged',
|
|
108
|
-
Assignee: assignee || 'unchanged',
|
|
109
|
-
},
|
|
110
|
-
tip: ERROR_TIPS.JIRA_ISSUE_EDIT,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
104
|
+
});
|
|
113
105
|
}
|
|
114
106
|
async addComment(args) {
|
|
115
107
|
const { issueKey, comment } = args;
|
|
116
|
-
|
|
117
|
-
await this.service.addComment(issueKey, comment);
|
|
118
|
-
return successResponse(`Comment added to issue ${issueKey}`);
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
return errorResponse(error, {
|
|
122
|
-
operation: 'Failed to add comment to Jira issue',
|
|
123
|
-
params: { 'Issue Key': issueKey, Comment: comment.substring(0, 100) + '...' },
|
|
124
|
-
tip: ERROR_TIPS.JIRA_COMMENT,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
108
|
+
return this.handle(() => this.service.addComment(issueKey, comment), { operation: 'Failed to add comment to Jira issue', params: { 'Issue Key': issueKey, Comment: comment.substring(0, 100) + '...' }, tip: ERROR_TIPS.JIRA_COMMENT }, () => successResponse(`Comment added to issue ${issueKey}`));
|
|
127
109
|
}
|
|
128
110
|
async getComments(args) {
|
|
129
111
|
const { issueKey } = args;
|
|
130
|
-
|
|
131
|
-
const comments = await this.service.getComments(issueKey);
|
|
132
|
-
return jsonResponse(comments);
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
return errorResponse(error, {
|
|
136
|
-
operation: 'Failed to get comments for Jira issue',
|
|
137
|
-
params: { 'Issue Key': issueKey },
|
|
138
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
112
|
+
return this.handle(() => this.service.getComments(issueKey), { operation: 'Failed to get comments for Jira issue', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
141
113
|
}
|
|
142
114
|
async updateComment(args) {
|
|
143
115
|
const { issueKey, commentId, comment } = args;
|
|
144
|
-
|
|
145
|
-
const result = await this.service.updateComment(issueKey, commentId, comment);
|
|
146
|
-
return successResponse(`${ICONS.SUCCESS} Comment ${commentId} updated successfully on issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`);
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
return errorResponse(error, {
|
|
150
|
-
operation: 'Failed to update comment on Jira issue',
|
|
151
|
-
params: { 'Issue Key': issueKey, 'Comment ID': commentId },
|
|
152
|
-
tip: 'Check if the comment exists, you are the author, and you have permission to edit it.',
|
|
153
|
-
});
|
|
154
|
-
}
|
|
116
|
+
return this.handle(() => this.service.updateComment(issueKey, commentId, comment), { operation: 'Failed to update comment on Jira issue', params: { 'Issue Key': issueKey, 'Comment ID': commentId }, tip: 'Check if the comment exists, you are the author, and you have permission to edit it.' }, (result) => successResponse(`${ICONS.SUCCESS} Comment ${commentId} updated successfully on issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`));
|
|
155
117
|
}
|
|
156
118
|
async deleteComment(args) {
|
|
157
119
|
const { issueKey, commentId } = args;
|
|
158
|
-
|
|
159
|
-
await this.service.deleteComment(issueKey, commentId);
|
|
160
|
-
return successResponse(`${ICONS.SUCCESS} Comment ${commentId} deleted successfully from issue ${issueKey}`);
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
return errorResponse(error, {
|
|
164
|
-
operation: 'Failed to delete comment from Jira issue',
|
|
165
|
-
params: { 'Issue Key': issueKey, 'Comment ID': commentId },
|
|
166
|
-
tip: 'Check if the comment exists and you have permission to delete it.',
|
|
167
|
-
});
|
|
168
|
-
}
|
|
120
|
+
return this.handle(() => this.service.deleteComment(issueKey, commentId), { operation: 'Failed to delete comment from Jira issue', params: { 'Issue Key': issueKey, 'Comment ID': commentId }, tip: 'Check if the comment exists and you have permission to delete it.' }, () => successResponse(`${ICONS.SUCCESS} Comment ${commentId} deleted successfully from issue ${issueKey}`));
|
|
169
121
|
}
|
|
170
122
|
async getProjects() {
|
|
171
|
-
|
|
172
|
-
const projects = await this.service.getProjects();
|
|
173
|
-
return jsonResponse(projects);
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
return errorResponse(error, {
|
|
177
|
-
operation: 'Failed to get Jira projects',
|
|
178
|
-
tip: 'Check your Jira permissions and API access.',
|
|
179
|
-
});
|
|
180
|
-
}
|
|
123
|
+
return this.handle(() => this.service.getProjects(), { operation: 'Failed to get Jira projects', tip: 'Check your Jira permissions and API access.' }, jsonResponse);
|
|
181
124
|
}
|
|
182
125
|
async getIssueTransitions(args) {
|
|
183
126
|
const { issueKey } = args;
|
|
184
|
-
|
|
185
|
-
const transitions = await this.service.getIssueTransitions(issueKey);
|
|
127
|
+
return this.handle(() => this.service.getIssueTransitions(issueKey), { operation: 'Failed to get transitions for Jira issue', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, (transitions) => {
|
|
186
128
|
const transitionInfo = transitions.map((t) => ({
|
|
187
129
|
id: t.id,
|
|
188
130
|
name: t.name,
|
|
@@ -191,19 +133,15 @@ export class JiraHandlers {
|
|
|
191
133
|
return successResponse(`Available transitions for ${issueKey}:\n\n` +
|
|
192
134
|
`${transitionInfo.map((t) => `• **${t.name}** (ID: ${t.id}) → ${t.to}`).join('\n')}\n\n` +
|
|
193
135
|
`Use the transition ID with jira_transition_issue_interactive for smart field handling.`);
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
return errorResponse(error, {
|
|
197
|
-
operation: 'Failed to get transitions for Jira issue',
|
|
198
|
-
params: { 'Issue Key': issueKey },
|
|
199
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
136
|
+
});
|
|
202
137
|
}
|
|
203
138
|
async transitionIssue(args) {
|
|
204
139
|
const { issueKey, transitionId, fieldValues } = args;
|
|
205
|
-
|
|
206
|
-
|
|
140
|
+
return this.handle(() => this.service.transitionIssue(issueKey, transitionId, fieldValues), {
|
|
141
|
+
operation: `Failed to transition Jira issue ${issueKey}`,
|
|
142
|
+
params: { 'Transition ID': transitionId, 'Field Values': JSON.stringify(fieldValues || {}, null, 2) },
|
|
143
|
+
tip: ERROR_TIPS.JIRA_TRANSITION,
|
|
144
|
+
}, (result) => {
|
|
207
145
|
if (result.success) {
|
|
208
146
|
return successResponse(result.message || `Issue ${issueKey} transitioned successfully`);
|
|
209
147
|
}
|
|
@@ -221,28 +159,16 @@ export class JiraHandlers {
|
|
|
221
159
|
return successResponse(`${result.message}\n\n**Required Fields:**\n${fieldInfo}\n\n` +
|
|
222
160
|
`**Next Steps:**\nUse jira_transition_issue_interactive for automatic field suggestion, or provide fieldValues with the correct field IDs and values.`);
|
|
223
161
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
content: [{ type: 'text', text: result.message || 'Transition failed with unknown error' }],
|
|
227
|
-
isError: true,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
catch (error) {
|
|
232
|
-
return errorResponse(error, {
|
|
233
|
-
operation: `Failed to transition Jira issue ${issueKey}`,
|
|
234
|
-
params: {
|
|
235
|
-
'Transition ID': transitionId,
|
|
236
|
-
'Field Values': JSON.stringify(fieldValues || {}, null, 2),
|
|
237
|
-
},
|
|
238
|
-
tip: ERROR_TIPS.JIRA_TRANSITION,
|
|
239
|
-
});
|
|
240
|
-
}
|
|
162
|
+
return { content: [{ type: 'text', text: result.message || 'Transition failed with unknown error' }], isError: true };
|
|
163
|
+
});
|
|
241
164
|
}
|
|
242
165
|
async transitionIssueInteractive(args) {
|
|
243
166
|
const { issueKey, transitionId, fieldValues } = args;
|
|
244
|
-
|
|
245
|
-
|
|
167
|
+
return this.handle(() => this.service.transitionIssueInteractive(issueKey, transitionId, fieldValues), {
|
|
168
|
+
operation: `Failed to transition Jira issue ${issueKey} (interactive mode)`,
|
|
169
|
+
params: { 'Transition ID': transitionId, 'Field Values': JSON.stringify(fieldValues || {}, null, 2) },
|
|
170
|
+
tip: ERROR_TIPS.JIRA_TRANSITION,
|
|
171
|
+
}, (result) => {
|
|
246
172
|
if (result.success) {
|
|
247
173
|
return successResponse(result.message || `Issue ${issueKey} transitioned successfully`);
|
|
248
174
|
}
|
|
@@ -259,558 +185,201 @@ export class JiraHandlers {
|
|
|
259
185
|
}).join('\n\n');
|
|
260
186
|
return successResponse(`${result.message}\n\n**Fields still requiring input:**\n${fieldInfo}`);
|
|
261
187
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
content: [{ type: 'text', text: result.message || 'Transition failed with unknown error' }],
|
|
265
|
-
isError: true,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
catch (error) {
|
|
270
|
-
return errorResponse(error, {
|
|
271
|
-
operation: `Failed to transition Jira issue ${issueKey} (interactive mode)`,
|
|
272
|
-
params: {
|
|
273
|
-
'Transition ID': transitionId,
|
|
274
|
-
'Field Values': JSON.stringify(fieldValues || {}, null, 2),
|
|
275
|
-
},
|
|
276
|
-
tip: ERROR_TIPS.JIRA_TRANSITION,
|
|
277
|
-
});
|
|
278
|
-
}
|
|
188
|
+
return { content: [{ type: 'text', text: result.message || 'Transition failed with unknown error' }], isError: true };
|
|
189
|
+
});
|
|
279
190
|
}
|
|
280
191
|
async getAttachments(args) {
|
|
281
192
|
const { issueKey } = args;
|
|
282
|
-
|
|
283
|
-
const attachments = await this.service.getAttachments(issueKey);
|
|
284
|
-
return jsonResponse(attachments);
|
|
285
|
-
}
|
|
286
|
-
catch (error) {
|
|
287
|
-
return errorResponse(error, {
|
|
288
|
-
operation: 'Failed to get attachments for Jira issue',
|
|
289
|
-
params: { 'Issue Key': issueKey },
|
|
290
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
291
|
-
});
|
|
292
|
-
}
|
|
193
|
+
return this.handle(() => this.service.getAttachments(issueKey), { operation: 'Failed to get attachments for Jira issue', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
293
194
|
}
|
|
294
195
|
async addAttachment(args) {
|
|
295
196
|
const { issueKey, filename, fileContent } = args;
|
|
296
|
-
|
|
297
|
-
const result = await this.service.addAttachment(issueKey, filename, fileContent);
|
|
298
|
-
return successResponse(`Attachment "${filename}" added to issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`);
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
return errorResponse(error, {
|
|
302
|
-
operation: 'Failed to add attachment to Jira issue',
|
|
303
|
-
params: { 'Issue Key': issueKey, Filename: filename },
|
|
304
|
-
tip: ERROR_TIPS.JIRA_ATTACHMENT,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
197
|
+
return this.handle(() => this.service.addAttachment(issueKey, filename, fileContent), { operation: 'Failed to add attachment to Jira issue', params: { 'Issue Key': issueKey, Filename: filename }, tip: ERROR_TIPS.JIRA_ATTACHMENT }, (result) => successResponse(`Attachment "${filename}" added to issue ${issueKey}\n\n${JSON.stringify(result, null, 2)}`));
|
|
307
198
|
}
|
|
308
199
|
async deleteAttachment(args) {
|
|
309
200
|
const { attachmentId } = args;
|
|
310
|
-
|
|
311
|
-
await this.service.deleteAttachment(attachmentId);
|
|
312
|
-
return successResponse(`Attachment ${attachmentId} deleted successfully`);
|
|
313
|
-
}
|
|
314
|
-
catch (error) {
|
|
315
|
-
return errorResponse(error, {
|
|
316
|
-
operation: 'Failed to delete attachment',
|
|
317
|
-
params: { 'Attachment ID': attachmentId },
|
|
318
|
-
tip: 'Check if the attachment exists and you have permission to delete it.',
|
|
319
|
-
});
|
|
320
|
-
}
|
|
201
|
+
return this.handle(() => this.service.deleteAttachment(attachmentId), { operation: 'Failed to delete attachment', params: { 'Attachment ID': attachmentId }, tip: 'Check if the attachment exists and you have permission to delete it.' }, () => successResponse(`Attachment ${attachmentId} deleted successfully`));
|
|
321
202
|
}
|
|
322
203
|
async getIssueLinks(args) {
|
|
323
204
|
const { issueKey } = args;
|
|
324
|
-
|
|
325
|
-
const links = await this.service.getIssueLinks(issueKey);
|
|
326
|
-
return jsonResponse(links);
|
|
327
|
-
}
|
|
328
|
-
catch (error) {
|
|
329
|
-
return errorResponse(error, {
|
|
330
|
-
operation: 'Failed to get issue links',
|
|
331
|
-
params: { 'Issue Key': issueKey },
|
|
332
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
333
|
-
});
|
|
334
|
-
}
|
|
205
|
+
return this.handle(() => this.service.getIssueLinks(issueKey), { operation: 'Failed to get issue links', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
335
206
|
}
|
|
336
207
|
async createIssueLink(args) {
|
|
337
208
|
const { inwardIssueKey, outwardIssueKey, linkType } = args;
|
|
338
|
-
|
|
339
|
-
await this.service.createIssueLink(inwardIssueKey, outwardIssueKey, linkType);
|
|
340
|
-
return successResponse(`Link created: ${inwardIssueKey} ${linkType} ${outwardIssueKey}`);
|
|
341
|
-
}
|
|
342
|
-
catch (error) {
|
|
343
|
-
return errorResponse(error, {
|
|
344
|
-
operation: 'Failed to create issue link',
|
|
345
|
-
params: { 'Inward Issue': inwardIssueKey, 'Outward Issue': outwardIssueKey, 'Link Type': linkType },
|
|
346
|
-
tip: ERROR_TIPS.JIRA_LINK,
|
|
347
|
-
});
|
|
348
|
-
}
|
|
209
|
+
return this.handle(() => this.service.createIssueLink(inwardIssueKey, outwardIssueKey, linkType), { operation: 'Failed to create issue link', params: { 'Inward Issue': inwardIssueKey, 'Outward Issue': outwardIssueKey, 'Link Type': linkType }, tip: ERROR_TIPS.JIRA_LINK }, () => successResponse(`Link created: ${inwardIssueKey} ${linkType} ${outwardIssueKey}`));
|
|
349
210
|
}
|
|
350
211
|
async deleteIssueLink(args) {
|
|
351
212
|
const { linkId } = args;
|
|
352
|
-
|
|
353
|
-
await this.service.deleteIssueLink(linkId);
|
|
354
|
-
return successResponse(`Issue link ${linkId} deleted successfully`);
|
|
355
|
-
}
|
|
356
|
-
catch (error) {
|
|
357
|
-
return errorResponse(error, {
|
|
358
|
-
operation: 'Failed to delete issue link',
|
|
359
|
-
params: { 'Link ID': linkId },
|
|
360
|
-
tip: 'Check if the link exists and you have permission to delete it. Use jira_get_issue_links to get link IDs.',
|
|
361
|
-
});
|
|
362
|
-
}
|
|
213
|
+
return this.handle(() => this.service.deleteIssueLink(linkId), { operation: 'Failed to delete issue link', params: { 'Link ID': linkId }, tip: 'Check if the link exists and you have permission to delete it. Use jira_get_issue_links to get link IDs.' }, () => successResponse(`Issue link ${linkId} deleted successfully`));
|
|
363
214
|
}
|
|
364
215
|
async getLinkTypes() {
|
|
365
|
-
|
|
366
|
-
const linkTypes = await this.service.getIssueLinkTypes();
|
|
367
|
-
return jsonResponse(linkTypes);
|
|
368
|
-
}
|
|
369
|
-
catch (error) {
|
|
370
|
-
return errorResponse(error, {
|
|
371
|
-
operation: 'Failed to get issue link types',
|
|
372
|
-
tip: 'Check your Jira permissions and API access.',
|
|
373
|
-
});
|
|
374
|
-
}
|
|
216
|
+
return this.handle(() => this.service.getIssueLinkTypes(), { operation: 'Failed to get issue link types', tip: 'Check your Jira permissions and API access.' }, jsonResponse);
|
|
375
217
|
}
|
|
376
218
|
async getWorklogs(args) {
|
|
377
219
|
const { issueKey } = args;
|
|
378
|
-
|
|
379
|
-
const worklogs = await this.service.getWorkLogs(issueKey);
|
|
380
|
-
return jsonResponse(worklogs);
|
|
381
|
-
}
|
|
382
|
-
catch (error) {
|
|
383
|
-
return errorResponse(error, {
|
|
384
|
-
operation: 'Failed to get work logs',
|
|
385
|
-
params: { 'Issue Key': issueKey },
|
|
386
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
220
|
+
return this.handle(() => this.service.getWorkLogs(issueKey), { operation: 'Failed to get work logs', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
389
221
|
}
|
|
390
222
|
async addWorklog(args) {
|
|
391
223
|
const { issueKey, timeSpentSeconds, comment, started } = args;
|
|
392
|
-
|
|
393
|
-
const result = await this.service.addWorkLog(issueKey, timeSpentSeconds, comment, started);
|
|
224
|
+
return this.handle(() => this.service.addWorkLog(issueKey, timeSpentSeconds, comment, started), { operation: 'Failed to add work log', params: { 'Issue Key': issueKey, 'Time Spent': `${timeSpentSeconds}s`, Comment: comment || 'none' }, tip: ERROR_TIPS.JIRA_WORKLOG }, (result) => {
|
|
394
225
|
const hours = Math.floor(timeSpentSeconds / 3600);
|
|
395
226
|
const minutes = Math.floor((timeSpentSeconds % 3600) / 60);
|
|
396
227
|
return successResponse(`Work log added to ${issueKey}\nTime spent: ${hours}h ${minutes}m\n\n${JSON.stringify(result, null, 2)}`);
|
|
397
|
-
}
|
|
398
|
-
catch (error) {
|
|
399
|
-
return errorResponse(error, {
|
|
400
|
-
operation: 'Failed to add work log',
|
|
401
|
-
params: { 'Issue Key': issueKey, 'Time Spent': `${timeSpentSeconds}s`, Comment: comment || 'none' },
|
|
402
|
-
tip: ERROR_TIPS.JIRA_WORKLOG,
|
|
403
|
-
});
|
|
404
|
-
}
|
|
228
|
+
});
|
|
405
229
|
}
|
|
406
230
|
async updateWorklog(args) {
|
|
407
231
|
const { issueKey, worklogId, timeSpentSeconds, comment } = args;
|
|
408
|
-
|
|
409
|
-
const result = await this.service.updateWorkLog(issueKey, worklogId, timeSpentSeconds, comment);
|
|
410
|
-
return successResponse(`Work log ${worklogId} updated\n\n${JSON.stringify(result, null, 2)}`);
|
|
411
|
-
}
|
|
412
|
-
catch (error) {
|
|
413
|
-
return errorResponse(error, {
|
|
414
|
-
operation: 'Failed to update work log',
|
|
415
|
-
params: { 'Issue Key': issueKey, 'Worklog ID': worklogId },
|
|
416
|
-
tip: ERROR_TIPS.JIRA_WORKLOG,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
232
|
+
return this.handle(() => this.service.updateWorkLog(issueKey, worklogId, timeSpentSeconds, comment), { operation: 'Failed to update work log', params: { 'Issue Key': issueKey, 'Worklog ID': worklogId }, tip: ERROR_TIPS.JIRA_WORKLOG }, (result) => successResponse(`Work log ${worklogId} updated\n\n${JSON.stringify(result, null, 2)}`));
|
|
419
233
|
}
|
|
420
234
|
async deleteWorklog(args) {
|
|
421
235
|
const { issueKey, worklogId } = args;
|
|
422
|
-
|
|
423
|
-
await this.service.deleteWorkLog(issueKey, worklogId);
|
|
424
|
-
return successResponse(`Work log ${worklogId} deleted from ${issueKey}`);
|
|
425
|
-
}
|
|
426
|
-
catch (error) {
|
|
427
|
-
return errorResponse(error, {
|
|
428
|
-
operation: 'Failed to delete work log',
|
|
429
|
-
params: { 'Issue Key': issueKey, 'Worklog ID': worklogId },
|
|
430
|
-
tip: ERROR_TIPS.JIRA_WORKLOG,
|
|
431
|
-
});
|
|
432
|
-
}
|
|
236
|
+
return this.handle(() => this.service.deleteWorkLog(issueKey, worklogId), { operation: 'Failed to delete work log', params: { 'Issue Key': issueKey, 'Worklog ID': worklogId }, tip: ERROR_TIPS.JIRA_WORKLOG }, () => successResponse(`Work log ${worklogId} deleted from ${issueKey}`));
|
|
433
237
|
}
|
|
434
238
|
async getWatchers(args) {
|
|
435
239
|
const { issueKey } = args;
|
|
436
|
-
|
|
437
|
-
const watchers = await this.service.getWatchers(issueKey);
|
|
438
|
-
return jsonResponse(watchers);
|
|
439
|
-
}
|
|
440
|
-
catch (error) {
|
|
441
|
-
return errorResponse(error, {
|
|
442
|
-
operation: 'Failed to get watchers',
|
|
443
|
-
params: { 'Issue Key': issueKey },
|
|
444
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
445
|
-
});
|
|
446
|
-
}
|
|
240
|
+
return this.handle(() => this.service.getWatchers(issueKey), { operation: 'Failed to get watchers', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
447
241
|
}
|
|
448
242
|
async addWatcher(args) {
|
|
449
243
|
const { issueKey, accountId } = args;
|
|
450
|
-
|
|
451
|
-
await this.service.addWatcher(issueKey, accountId);
|
|
452
|
-
return successResponse(`Watcher added to ${issueKey}`);
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
return errorResponse(error, {
|
|
456
|
-
operation: 'Failed to add watcher',
|
|
457
|
-
params: { 'Issue Key': issueKey, 'Account ID': accountId },
|
|
458
|
-
tip: ERROR_TIPS.JIRA_WATCHER,
|
|
459
|
-
});
|
|
460
|
-
}
|
|
244
|
+
return this.handle(() => this.service.addWatcher(issueKey, accountId), { operation: 'Failed to add watcher', params: { 'Issue Key': issueKey, 'Account ID': accountId }, tip: ERROR_TIPS.JIRA_WATCHER }, () => successResponse(`Watcher added to ${issueKey}`));
|
|
461
245
|
}
|
|
462
246
|
async removeWatcher(args) {
|
|
463
247
|
const { issueKey, accountId } = args;
|
|
464
|
-
|
|
465
|
-
await this.service.removeWatcher(issueKey, accountId);
|
|
466
|
-
return successResponse(`Watcher removed from ${issueKey}`);
|
|
467
|
-
}
|
|
468
|
-
catch (error) {
|
|
469
|
-
return errorResponse(error, {
|
|
470
|
-
operation: 'Failed to remove watcher',
|
|
471
|
-
params: { 'Issue Key': issueKey, 'Account ID': accountId },
|
|
472
|
-
tip: 'Check if the issue exists and you have permission to remove watchers.',
|
|
473
|
-
});
|
|
474
|
-
}
|
|
248
|
+
return this.handle(() => this.service.removeWatcher(issueKey, accountId), { operation: 'Failed to remove watcher', params: { 'Issue Key': issueKey, 'Account ID': accountId }, tip: 'Check if the issue exists and you have permission to remove watchers.' }, () => successResponse(`Watcher removed from ${issueKey}`));
|
|
475
249
|
}
|
|
476
250
|
async getSubtasks(args) {
|
|
477
251
|
const { issueKey } = args;
|
|
478
|
-
|
|
479
|
-
const subtasks = await this.service.getSubTasks(issueKey);
|
|
480
|
-
return jsonResponse(subtasks);
|
|
481
|
-
}
|
|
482
|
-
catch (error) {
|
|
483
|
-
return errorResponse(error, {
|
|
484
|
-
operation: 'Failed to get sub-tasks',
|
|
485
|
-
params: { 'Issue Key': issueKey },
|
|
486
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
487
|
-
});
|
|
488
|
-
}
|
|
252
|
+
return this.handle(() => this.service.getSubTasks(issueKey), { operation: 'Failed to get sub-tasks', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
489
253
|
}
|
|
490
254
|
async createSubtask(args) {
|
|
491
255
|
const { parentKey, summary, description } = args;
|
|
492
|
-
|
|
493
|
-
const subtask = await this.service.createSubTask(parentKey, summary, description);
|
|
494
|
-
return successResponse(`Sub-task created: ${subtask.key}\n\n${JSON.stringify(subtask, null, 2)}`);
|
|
495
|
-
}
|
|
496
|
-
catch (error) {
|
|
497
|
-
return errorResponse(error, {
|
|
498
|
-
operation: 'Failed to create sub-task',
|
|
499
|
-
params: { 'Parent Key': parentKey, Summary: summary },
|
|
500
|
-
tip: ERROR_TIPS.JIRA_SUBTASK,
|
|
501
|
-
});
|
|
502
|
-
}
|
|
256
|
+
return this.handle(() => this.service.createSubTask(parentKey, summary, description), { operation: 'Failed to create sub-task', params: { 'Parent Key': parentKey, Summary: summary }, tip: ERROR_TIPS.JIRA_SUBTASK }, (subtask) => successResponse(`Sub-task created: ${subtask.key}\n\n${JSON.stringify(subtask, null, 2)}`));
|
|
503
257
|
}
|
|
504
258
|
async getIssueHistory(args) {
|
|
505
259
|
const { issueKey } = args;
|
|
506
|
-
|
|
507
|
-
const history = await this.service.getIssueHistory(issueKey);
|
|
508
|
-
return jsonResponse(history);
|
|
509
|
-
}
|
|
510
|
-
catch (error) {
|
|
511
|
-
return errorResponse(error, {
|
|
512
|
-
operation: 'Failed to get issue history',
|
|
513
|
-
params: { 'Issue Key': issueKey },
|
|
514
|
-
tip: ERROR_TIPS.JIRA_ISSUE_VIEW,
|
|
515
|
-
});
|
|
516
|
-
}
|
|
260
|
+
return this.handle(() => this.service.getIssueHistory(issueKey), { operation: 'Failed to get issue history', params: { 'Issue Key': issueKey }, tip: ERROR_TIPS.JIRA_ISSUE_VIEW }, jsonResponse);
|
|
517
261
|
}
|
|
518
262
|
async getFields(args) {
|
|
519
|
-
const { type } = args;
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const fields = await this.service.getFields(filterType);
|
|
523
|
-
return jsonResponse(fields);
|
|
524
|
-
}
|
|
525
|
-
catch (error) {
|
|
526
|
-
return errorResponse(error, {
|
|
527
|
-
operation: 'Failed to get Jira fields',
|
|
528
|
-
tip: 'Check your Jira permissions and API access.',
|
|
529
|
-
});
|
|
530
|
-
}
|
|
263
|
+
const { type, query } = args;
|
|
264
|
+
const filterType = (type === 'standard' || type === 'custom') ? type : undefined;
|
|
265
|
+
return this.handle(() => this.service.getFields(filterType, query), { operation: 'Failed to get Jira fields', tip: 'Check your Jira permissions and API access.' }, jsonResponse);
|
|
531
266
|
}
|
|
532
267
|
async getLabels(args) {
|
|
533
268
|
const { query, maxResults } = args;
|
|
534
|
-
|
|
535
|
-
const labels = await this.service.getLabels(query, maxResults);
|
|
536
|
-
return jsonResponse(labels);
|
|
537
|
-
}
|
|
538
|
-
catch (error) {
|
|
539
|
-
return errorResponse(error, {
|
|
540
|
-
operation: 'Failed to get labels',
|
|
541
|
-
params: { Query: query || 'none', 'Max Results': maxResults || 50 },
|
|
542
|
-
tip: 'Check your Jira permissions and API access.',
|
|
543
|
-
});
|
|
544
|
-
}
|
|
269
|
+
return this.handle(() => this.service.getLabels(query, maxResults), { operation: 'Failed to get labels', params: { Query: query || 'none', 'Max Results': maxResults || 50 }, tip: 'Check your Jira permissions and API access.' }, jsonResponse);
|
|
545
270
|
}
|
|
546
271
|
async addLabels(args) {
|
|
547
272
|
const { issueKey, labels } = args;
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return successResponse(`${ICONS.SUCCESS} Labels added to ${issueKey}\n\n` +
|
|
551
|
-
`${ICONS.LABELS} **Added:** ${labels.join(', ')}`);
|
|
552
|
-
}
|
|
553
|
-
catch (error) {
|
|
554
|
-
return errorResponse(error, {
|
|
555
|
-
operation: `Failed to add labels to ${issueKey}`,
|
|
556
|
-
params: { 'Issue Key': issueKey, Labels: labels.join(', ') },
|
|
557
|
-
tip: ERROR_TIPS.JIRA_ISSUE_EDIT,
|
|
558
|
-
});
|
|
559
|
-
}
|
|
273
|
+
return this.handle(() => this.service.addLabels(issueKey, labels), { operation: `Failed to add labels to ${issueKey}`, params: { 'Issue Key': issueKey, Labels: labels.join(', ') }, tip: ERROR_TIPS.JIRA_ISSUE_EDIT }, () => successResponse(`${ICONS.SUCCESS} Labels added to ${issueKey}\n\n` +
|
|
274
|
+
`${ICONS.LABELS} **Added:** ${labels.join(', ')}`));
|
|
560
275
|
}
|
|
561
276
|
async removeLabels(args) {
|
|
562
277
|
const { issueKey, labels } = args;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
return successResponse(`${ICONS.SUCCESS} Labels removed from ${issueKey}\n\n` +
|
|
566
|
-
`${ICONS.LABELS} **Removed:** ${labels.join(', ')}`);
|
|
567
|
-
}
|
|
568
|
-
catch (error) {
|
|
569
|
-
return errorResponse(error, {
|
|
570
|
-
operation: `Failed to remove labels from ${issueKey}`,
|
|
571
|
-
params: { 'Issue Key': issueKey, Labels: labels.join(', ') },
|
|
572
|
-
tip: ERROR_TIPS.JIRA_ISSUE_EDIT,
|
|
573
|
-
});
|
|
574
|
-
}
|
|
278
|
+
return this.handle(() => this.service.removeLabels(issueKey, labels), { operation: `Failed to remove labels from ${issueKey}`, params: { 'Issue Key': issueKey, Labels: labels.join(', ') }, tip: ERROR_TIPS.JIRA_ISSUE_EDIT }, () => successResponse(`${ICONS.SUCCESS} Labels removed from ${issueKey}\n\n` +
|
|
279
|
+
`${ICONS.LABELS} **Removed:** ${labels.join(', ')}`));
|
|
575
280
|
}
|
|
576
281
|
async getAgileBoards(args) {
|
|
577
|
-
|
|
578
|
-
const boards = await this.service.getAgileBoards(args);
|
|
579
|
-
return jsonResponse(boards);
|
|
580
|
-
}
|
|
581
|
-
catch (error) {
|
|
582
|
-
return errorResponse(error, {
|
|
583
|
-
operation: 'Failed to get agile boards',
|
|
584
|
-
params: { Name: args.name || 'all', Type: args.type || 'all', Project: args.projectKeyOrId || 'all' },
|
|
585
|
-
tip: ERROR_TIPS.JIRA_AGILE,
|
|
586
|
-
});
|
|
587
|
-
}
|
|
282
|
+
return this.handle(() => this.service.getAgileBoards(args), { operation: 'Failed to get agile boards', params: { Name: args.name || 'all', Type: args.type || 'all', Project: args.projectKeyOrId || 'all' }, tip: ERROR_TIPS.JIRA_AGILE }, jsonResponse);
|
|
588
283
|
}
|
|
589
284
|
async getBoardIssues(args) {
|
|
590
285
|
const { boardId, jql, maxResults, startAt } = args;
|
|
591
|
-
|
|
592
|
-
const result = await this.service.getBoardIssues(boardId, { jql, maxResults, startAt });
|
|
593
|
-
return jsonResponse(result);
|
|
594
|
-
}
|
|
595
|
-
catch (error) {
|
|
596
|
-
return errorResponse(error, {
|
|
597
|
-
operation: 'Failed to get board issues',
|
|
598
|
-
params: { 'Board ID': boardId, JQL: jql || 'none' },
|
|
599
|
-
tip: ERROR_TIPS.JIRA_AGILE,
|
|
600
|
-
});
|
|
601
|
-
}
|
|
286
|
+
return this.handle(() => this.service.getBoardIssues(boardId, { jql, maxResults, startAt }), { operation: 'Failed to get board issues', params: { 'Board ID': boardId, JQL: jql || 'none' }, tip: ERROR_TIPS.JIRA_AGILE }, jsonResponse);
|
|
602
287
|
}
|
|
603
288
|
async getSprints(args) {
|
|
604
289
|
const { boardId, state } = args;
|
|
605
|
-
|
|
606
|
-
const sprints = await this.service.getSprints(boardId, state);
|
|
607
|
-
return jsonResponse(sprints);
|
|
608
|
-
}
|
|
609
|
-
catch (error) {
|
|
610
|
-
return errorResponse(error, {
|
|
611
|
-
operation: 'Failed to get sprints',
|
|
612
|
-
params: { 'Board ID': boardId, State: state || 'all' },
|
|
613
|
-
tip: ERROR_TIPS.JIRA_AGILE,
|
|
614
|
-
});
|
|
615
|
-
}
|
|
290
|
+
return this.handle(() => this.service.getSprints(boardId, state), { operation: 'Failed to get sprints', params: { 'Board ID': boardId, State: state || 'all' }, tip: ERROR_TIPS.JIRA_AGILE }, jsonResponse);
|
|
616
291
|
}
|
|
617
292
|
async getSprintIssues(args) {
|
|
618
293
|
const { sprintId, jql, maxResults, startAt } = args;
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
return errorResponse(
|
|
625
|
-
operation: 'Failed to
|
|
626
|
-
params: { 'Sprint ID': sprintId
|
|
627
|
-
tip:
|
|
294
|
+
return this.handle(() => this.service.getSprintIssues(sprintId, { jql, maxResults, startAt }), { operation: 'Failed to get sprint issues', params: { 'Sprint ID': sprintId, JQL: jql || 'none' }, tip: ERROR_TIPS.JIRA_AGILE }, jsonResponse);
|
|
295
|
+
}
|
|
296
|
+
async moveIssuesToSprint(args) {
|
|
297
|
+
const { sprintId, issueKeys } = args;
|
|
298
|
+
if (!Array.isArray(issueKeys)) {
|
|
299
|
+
return errorResponse(new Error('Invalid issueKeys parameter'), {
|
|
300
|
+
operation: 'Failed to move issues to sprint',
|
|
301
|
+
params: { 'Sprint ID': sprintId },
|
|
302
|
+
tip: 'The "issueKeys" parameter must be an array of strings, e.g. ["PROJ-123", "PROJ-456"]. Do not pass it as a string.',
|
|
628
303
|
});
|
|
629
304
|
}
|
|
305
|
+
return this.handle(() => this.service.moveIssuesToSprint(sprintId, issueKeys), { operation: 'Failed to move issues to sprint', params: { 'Sprint ID': sprintId, Issues: issueKeys.join(', ') }, tip: ERROR_TIPS.JIRA_AGILE }, () => successResponse(`${ICONS.SUCCESS} **Moved ${issueKeys.length} issue(s) to sprint ${sprintId}**\n\n` +
|
|
306
|
+
`**Issues:** ${issueKeys.join(', ')}`));
|
|
630
307
|
}
|
|
631
308
|
async batchCreateIssues(args) {
|
|
632
309
|
const { issues } = args;
|
|
633
|
-
|
|
634
|
-
const result = await this.service.batchCreateIssues(issues);
|
|
310
|
+
return this.handle(() => this.service.batchCreateIssues(issues), { operation: 'Failed to batch create issues', params: { 'Issue Count': issues.length }, tip: ERROR_TIPS.JIRA_BATCH }, (result) => {
|
|
635
311
|
const successCount = result.issues?.length || 0;
|
|
636
312
|
const errorCount = result.errors?.length || 0;
|
|
637
313
|
return successResponse(`${ICONS.SUCCESS} Batch create completed: ${successCount} created, ${errorCount} errors\n\n${JSON.stringify(result, null, 2)}`);
|
|
638
|
-
}
|
|
639
|
-
catch (error) {
|
|
640
|
-
return errorResponse(error, {
|
|
641
|
-
operation: 'Failed to batch create issues',
|
|
642
|
-
params: { 'Issue Count': issues.length },
|
|
643
|
-
tip: ERROR_TIPS.JIRA_BATCH,
|
|
644
|
-
});
|
|
645
|
-
}
|
|
314
|
+
});
|
|
646
315
|
}
|
|
647
316
|
async getDevStatus(args) {
|
|
648
317
|
const { issueId, applicationType, dataType } = args;
|
|
649
|
-
|
|
650
|
-
const result = await this.service.getDevStatus(issueId, applicationType, dataType);
|
|
651
|
-
return jsonResponse(result);
|
|
652
|
-
}
|
|
653
|
-
catch (error) {
|
|
654
|
-
return errorResponse(error, {
|
|
655
|
-
operation: 'Failed to get dev status',
|
|
656
|
-
params: { 'Issue ID': issueId, 'Application Type': applicationType || 'all', 'Data Type': dataType || 'all' },
|
|
657
|
-
tip: ERROR_TIPS.JIRA_DEV_STATUS,
|
|
658
|
-
});
|
|
659
|
-
}
|
|
318
|
+
return this.handle(() => this.service.getDevStatus(issueId, applicationType, dataType), { operation: 'Failed to get dev status', params: { 'Issue ID': issueId, 'Application Type': applicationType || 'all', 'Data Type': dataType || 'all' }, tip: ERROR_TIPS.JIRA_DEV_STATUS }, jsonResponse);
|
|
660
319
|
}
|
|
661
320
|
async deleteIssue(args) {
|
|
662
321
|
const { issueKey, deleteSubtasks } = args;
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
return successResponse(`${ICONS.SUCCESS} Issue ${issueKey} deleted successfully` +
|
|
666
|
-
(deleteSubtasks ? ' (including subtasks)' : ''));
|
|
667
|
-
}
|
|
668
|
-
catch (error) {
|
|
669
|
-
return errorResponse(error, {
|
|
670
|
-
operation: `Failed to delete issue ${issueKey}`,
|
|
671
|
-
params: { 'Issue Key': issueKey, 'Delete Subtasks': deleteSubtasks || false },
|
|
672
|
-
tip: ERROR_TIPS.JIRA_DELETE,
|
|
673
|
-
});
|
|
674
|
-
}
|
|
322
|
+
return this.handle(() => this.service.deleteIssue(issueKey, deleteSubtasks), { operation: `Failed to delete issue ${issueKey}`, params: { 'Issue Key': issueKey, 'Delete Subtasks': deleteSubtasks || false }, tip: ERROR_TIPS.JIRA_DELETE }, () => successResponse(`${ICONS.SUCCESS} Issue ${issueKey} deleted successfully` +
|
|
323
|
+
(deleteSubtasks ? ' (including subtasks)' : '')));
|
|
675
324
|
}
|
|
676
325
|
async lookupUser(args) {
|
|
677
326
|
const { query } = args;
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
active: u.active,
|
|
685
|
-
})));
|
|
686
|
-
}
|
|
687
|
-
catch (error) {
|
|
688
|
-
return errorResponse(error, {
|
|
689
|
-
operation: 'Failed to look up user',
|
|
690
|
-
params: { Query: query },
|
|
691
|
-
tip: 'Check your Jira permissions. Search by display name or email address.',
|
|
692
|
-
});
|
|
693
|
-
}
|
|
327
|
+
return this.handle(() => this.service.getUsersByQuery(query), { operation: 'Failed to look up user', params: { Query: query }, tip: 'Check your Jira permissions. Search by display name or email address.' }, (users) => jsonResponse(users.map((u) => ({
|
|
328
|
+
accountId: u.accountId,
|
|
329
|
+
displayName: u.displayName,
|
|
330
|
+
emailAddress: u.emailAddress || null,
|
|
331
|
+
active: u.active,
|
|
332
|
+
}))));
|
|
694
333
|
}
|
|
695
334
|
async getIssueTypes(args) {
|
|
696
335
|
const { projectKey } = args;
|
|
697
|
-
|
|
698
|
-
const issueTypes = await this.service.getIssueTypes(projectKey);
|
|
699
|
-
return jsonResponse(issueTypes);
|
|
700
|
-
}
|
|
701
|
-
catch (error) {
|
|
702
|
-
return errorResponse(error, {
|
|
703
|
-
operation: 'Failed to get issue types',
|
|
704
|
-
params: { 'Project Key': projectKey },
|
|
705
|
-
tip: 'Check if the project key exists and you have permission to view it.',
|
|
706
|
-
});
|
|
707
|
-
}
|
|
336
|
+
return this.handle(() => this.service.getIssueTypes(projectKey), { operation: 'Failed to get issue types', params: { 'Project Key': projectKey }, tip: 'Check if the project key exists and you have permission to view it.' }, jsonResponse);
|
|
708
337
|
}
|
|
709
338
|
async createSprint(args) {
|
|
710
339
|
const { boardId, name, startDate, endDate, goal } = args;
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
(sprint.endDate ? `**End:** ${sprint.endDate}\n` : '') +
|
|
719
|
-
(sprint.goal ? `**Goal:** ${sprint.goal}` : ''));
|
|
720
|
-
}
|
|
721
|
-
catch (error) {
|
|
722
|
-
return errorResponse(error, {
|
|
723
|
-
operation: 'Failed to create sprint',
|
|
724
|
-
params: { 'Board ID': boardId, Name: name },
|
|
725
|
-
tip: ERROR_TIPS.JIRA_AGILE,
|
|
726
|
-
});
|
|
727
|
-
}
|
|
340
|
+
return this.handle(() => this.service.createSprint(boardId, name, { startDate, endDate, goal }), { operation: 'Failed to create sprint', params: { 'Board ID': boardId, Name: name }, tip: ERROR_TIPS.JIRA_AGILE }, (sprint) => successResponse(`${ICONS.SUCCESS} Sprint created successfully\n\n` +
|
|
341
|
+
`**ID:** ${sprint.id}\n` +
|
|
342
|
+
`**Name:** ${sprint.name}\n` +
|
|
343
|
+
`**State:** ${sprint.state}\n` +
|
|
344
|
+
(sprint.startDate ? `**Start:** ${sprint.startDate}\n` : '') +
|
|
345
|
+
(sprint.endDate ? `**End:** ${sprint.endDate}\n` : '') +
|
|
346
|
+
(sprint.goal ? `**Goal:** ${sprint.goal}` : '')));
|
|
728
347
|
}
|
|
729
348
|
async updateSprint(args) {
|
|
730
349
|
const { sprintId, name, state, startDate, endDate, goal } = args;
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
(sprint.endDate ? `**End:** ${sprint.endDate}\n` : '') +
|
|
738
|
-
(sprint.goal ? `**Goal:** ${sprint.goal}` : ''));
|
|
739
|
-
}
|
|
740
|
-
catch (error) {
|
|
741
|
-
return errorResponse(error, {
|
|
742
|
-
operation: `Failed to update sprint ${sprintId}`,
|
|
743
|
-
params: { 'Sprint ID': sprintId, Name: name || 'unchanged', State: state || 'unchanged' },
|
|
744
|
-
tip: ERROR_TIPS.JIRA_AGILE,
|
|
745
|
-
});
|
|
746
|
-
}
|
|
350
|
+
return this.handle(() => this.service.updateSprint(sprintId, { name, state, startDate, endDate, goal }), { operation: `Failed to update sprint ${sprintId}`, params: { 'Sprint ID': sprintId, Name: name || 'unchanged', State: state || 'unchanged' }, tip: ERROR_TIPS.JIRA_AGILE }, (sprint) => successResponse(`${ICONS.SUCCESS} Sprint ${sprintId} updated successfully\n\n` +
|
|
351
|
+
`**Name:** ${sprint.name}\n` +
|
|
352
|
+
`**State:** ${sprint.state}\n` +
|
|
353
|
+
(sprint.startDate ? `**Start:** ${sprint.startDate}\n` : '') +
|
|
354
|
+
(sprint.endDate ? `**End:** ${sprint.endDate}\n` : '') +
|
|
355
|
+
(sprint.goal ? `**Goal:** ${sprint.goal}` : '')));
|
|
747
356
|
}
|
|
748
357
|
async getProjectVersions(args) {
|
|
749
358
|
const { projectKey } = args;
|
|
750
|
-
|
|
751
|
-
const versions = await this.service.getProjectVersions(projectKey);
|
|
752
|
-
return jsonResponse(versions);
|
|
753
|
-
}
|
|
754
|
-
catch (error) {
|
|
755
|
-
return errorResponse(error, {
|
|
756
|
-
operation: 'Failed to get project versions',
|
|
757
|
-
params: { 'Project Key': projectKey },
|
|
758
|
-
tip: 'Check if the project key exists and you have permission to view versions.',
|
|
759
|
-
});
|
|
760
|
-
}
|
|
359
|
+
return this.handle(() => this.service.getProjectVersions(projectKey), { operation: 'Failed to get project versions', params: { 'Project Key': projectKey }, tip: 'Check if the project key exists and you have permission to view versions.' }, jsonResponse);
|
|
761
360
|
}
|
|
762
361
|
async createVersion(args) {
|
|
763
362
|
const { projectKey, name, description, startDate, releaseDate, released } = args;
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
(version.startDate ? `**Start Date:** ${version.startDate}\n` : '') +
|
|
772
|
-
(version.releaseDate ? `**Release Date:** ${version.releaseDate}` : ''));
|
|
773
|
-
}
|
|
774
|
-
catch (error) {
|
|
775
|
-
return errorResponse(error, {
|
|
776
|
-
operation: 'Failed to create version',
|
|
777
|
-
params: { 'Project Key': projectKey, Name: name },
|
|
778
|
-
tip: 'Check if the project exists and you have permission to manage versions.',
|
|
779
|
-
});
|
|
780
|
-
}
|
|
363
|
+
return this.handle(() => this.service.createVersion(projectKey, name, { description, startDate, releaseDate, released }), { operation: 'Failed to create version', params: { 'Project Key': projectKey, Name: name }, tip: 'Check if the project exists and you have permission to manage versions.' }, (version) => successResponse(`${ICONS.SUCCESS} Version created successfully\n\n` +
|
|
364
|
+
`**ID:** ${version.id}\n` +
|
|
365
|
+
`**Name:** ${version.name}\n` +
|
|
366
|
+
`**Released:** ${version.released}\n` +
|
|
367
|
+
(version.description ? `**Description:** ${version.description}\n` : '') +
|
|
368
|
+
(version.startDate ? `**Start Date:** ${version.startDate}\n` : '') +
|
|
369
|
+
(version.releaseDate ? `**Release Date:** ${version.releaseDate}` : '')));
|
|
781
370
|
}
|
|
782
371
|
async updateVersion(args) {
|
|
783
372
|
const { versionId, name, description, startDate, releaseDate, released, archived } = args;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
(version.description ? `**Description:** ${version.description}\n` : '') +
|
|
791
|
-
(version.releaseDate ? `**Release Date:** ${version.releaseDate}` : ''));
|
|
792
|
-
}
|
|
793
|
-
catch (error) {
|
|
794
|
-
return errorResponse(error, {
|
|
795
|
-
operation: `Failed to update version ${versionId}`,
|
|
796
|
-
params: { 'Version ID': versionId },
|
|
797
|
-
tip: 'Check if the version exists and you have permission to manage versions.',
|
|
798
|
-
});
|
|
799
|
-
}
|
|
373
|
+
return this.handle(() => this.service.updateVersion(versionId, { name, description, startDate, releaseDate, released, archived }), { operation: `Failed to update version ${versionId}`, params: { 'Version ID': versionId }, tip: 'Check if the version exists and you have permission to manage versions.' }, (version) => successResponse(`${ICONS.SUCCESS} Version ${versionId} updated successfully\n\n` +
|
|
374
|
+
`**Name:** ${version.name}\n` +
|
|
375
|
+
`**Released:** ${version.released}\n` +
|
|
376
|
+
`**Archived:** ${version.archived}\n` +
|
|
377
|
+
(version.description ? `**Description:** ${version.description}\n` : '') +
|
|
378
|
+
(version.releaseDate ? `**Release Date:** ${version.releaseDate}` : '')));
|
|
800
379
|
}
|
|
801
380
|
async linkToEpic(args) {
|
|
802
381
|
const { issueKey, epicKey } = args;
|
|
803
|
-
|
|
804
|
-
await this.service.linkToEpic(issueKey, epicKey);
|
|
805
|
-
return successResponse(`${ICONS.SUCCESS} Issue ${issueKey} linked to epic ${epicKey}`);
|
|
806
|
-
}
|
|
807
|
-
catch (error) {
|
|
808
|
-
return errorResponse(error, {
|
|
809
|
-
operation: `Failed to link ${issueKey} to epic ${epicKey}`,
|
|
810
|
-
params: { 'Issue Key': issueKey, 'Epic Key': epicKey },
|
|
811
|
-
tip: 'Check that both issues exist, the epic is actually an Epic type, and you have edit permission.',
|
|
812
|
-
});
|
|
813
|
-
}
|
|
382
|
+
return this.handle(() => this.service.linkToEpic(issueKey, epicKey), { operation: `Failed to link ${issueKey} to epic ${epicKey}`, params: { 'Issue Key': issueKey, 'Epic Key': epicKey }, tip: 'Check that both issues exist, the epic is actually an Epic type, and you have edit permission.' }, () => successResponse(`${ICONS.SUCCESS} Issue ${issueKey} linked to epic ${epicKey}`));
|
|
814
383
|
}
|
|
815
384
|
}
|
|
816
385
|
//# sourceMappingURL=jira-handlers.js.map
|