prjct-cli 0.55.1 → 0.55.3
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/CHANGELOG.md +35 -3
- package/core/__tests__/services/project-index.test.ts +3 -1
- package/core/infrastructure/setup.ts +55 -0
- package/core/integrations/jira/index.ts +0 -15
- package/core/services/sync-service.ts +6 -0
- package/dist/bin/prjct.mjs +38 -2
- package/dist/core/infrastructure/setup.js +33 -0
- package/package.json +2 -2
- package/templates/commands/bug.md +143 -18
- package/templates/commands/dash.md +74 -18
- package/templates/commands/done.md +125 -26
- package/templates/commands/idea.md +64 -9
- package/templates/commands/jira.md +3 -1
- package/templates/commands/linear.md +16 -24
- package/templates/commands/next.md +49 -11
- package/templates/commands/p.md +30 -183
- package/templates/commands/pause.md +115 -18
- package/templates/commands/resume.md +151 -14
- package/templates/commands/sync.md +0 -10
- package/templates/commands/task.md +86 -38
- package/templates/commands/test.md +60 -17
- package/templates/commands/workflow.md +70 -70
- package/templates/mcp-config.json +4 -32
- package/core/integrations/jira/mcp-adapter.ts +0 -446
- package/templates/_bases/tracker-base.md +0 -321
- package/templates/commands/github.md +0 -298
- package/templates/commands/monday.md +0 -243
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JIRA MCP Adapter
|
|
3
|
-
*
|
|
4
|
-
* Provides JIRA integration via Atlassian's official MCP Server.
|
|
5
|
-
* Used when API tokens are not available (e.g., corporate SSO environments).
|
|
6
|
-
*
|
|
7
|
-
* The MCP Server uses OAuth 2.1 via browser, compatible with corporate SSO.
|
|
8
|
-
* See: https://www.atlassian.com/blog/announcements/remote-mcp-server
|
|
9
|
-
*
|
|
10
|
-
* Setup: Add to ~/.claude/mcp.json:
|
|
11
|
-
* {
|
|
12
|
-
* "mcpServers": {
|
|
13
|
-
* "Atlassian": {
|
|
14
|
-
* "command": "npx",
|
|
15
|
-
* "args": ["-y", "mcp-remote@latest", "https://mcp.atlassian.com/v1/sse"]
|
|
16
|
-
* }
|
|
17
|
-
* }
|
|
18
|
-
* }
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import type {
|
|
22
|
-
CreateIssueInput,
|
|
23
|
-
FetchOptions,
|
|
24
|
-
Issue,
|
|
25
|
-
IssuePriority,
|
|
26
|
-
IssueStatus,
|
|
27
|
-
IssueTrackerProvider,
|
|
28
|
-
IssueType,
|
|
29
|
-
JiraConfig,
|
|
30
|
-
} from '../issue-tracker/types'
|
|
31
|
-
|
|
32
|
-
// =============================================================================
|
|
33
|
-
// MCP Response Types (from Atlassian MCP Server)
|
|
34
|
-
// =============================================================================
|
|
35
|
-
|
|
36
|
-
interface MCPJiraIssue {
|
|
37
|
-
id: string
|
|
38
|
-
key: string
|
|
39
|
-
fields: {
|
|
40
|
-
summary: string
|
|
41
|
-
description?: string
|
|
42
|
-
status: {
|
|
43
|
-
name: string
|
|
44
|
-
statusCategory?: {
|
|
45
|
-
key: string
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
priority?: {
|
|
49
|
-
name: string
|
|
50
|
-
}
|
|
51
|
-
issuetype: {
|
|
52
|
-
name: string
|
|
53
|
-
}
|
|
54
|
-
assignee?: {
|
|
55
|
-
accountId: string
|
|
56
|
-
displayName: string
|
|
57
|
-
emailAddress?: string
|
|
58
|
-
}
|
|
59
|
-
project: {
|
|
60
|
-
id: string
|
|
61
|
-
key: string
|
|
62
|
-
name: string
|
|
63
|
-
}
|
|
64
|
-
labels?: string[]
|
|
65
|
-
created: string
|
|
66
|
-
updated: string
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// =============================================================================
|
|
71
|
-
// Status/Priority Mapping (same as REST client)
|
|
72
|
-
// =============================================================================
|
|
73
|
-
|
|
74
|
-
const JIRA_STATUS_CATEGORY_MAP: Record<string, IssueStatus> = {
|
|
75
|
-
new: 'todo',
|
|
76
|
-
indeterminate: 'in_progress',
|
|
77
|
-
done: 'done',
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const JIRA_STATUS_NAME_MAP: Record<string, IssueStatus> = {
|
|
81
|
-
backlog: 'backlog',
|
|
82
|
-
open: 'backlog',
|
|
83
|
-
'to do': 'todo',
|
|
84
|
-
todo: 'todo',
|
|
85
|
-
new: 'todo',
|
|
86
|
-
'in progress': 'in_progress',
|
|
87
|
-
'in development': 'in_progress',
|
|
88
|
-
'in review': 'in_review',
|
|
89
|
-
'code review': 'in_review',
|
|
90
|
-
review: 'in_review',
|
|
91
|
-
done: 'done',
|
|
92
|
-
closed: 'done',
|
|
93
|
-
resolved: 'done',
|
|
94
|
-
complete: 'done',
|
|
95
|
-
completed: 'done',
|
|
96
|
-
cancelled: 'cancelled',
|
|
97
|
-
canceled: 'cancelled',
|
|
98
|
-
"won't do": 'cancelled',
|
|
99
|
-
'wont do': 'cancelled',
|
|
100
|
-
rejected: 'cancelled',
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const JIRA_PRIORITY_MAP: Record<string, IssuePriority> = {
|
|
104
|
-
highest: 'urgent',
|
|
105
|
-
high: 'high',
|
|
106
|
-
medium: 'medium',
|
|
107
|
-
low: 'low',
|
|
108
|
-
lowest: 'low',
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// =============================================================================
|
|
112
|
-
// MCP Instruction Generator
|
|
113
|
-
// =============================================================================
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Represents an MCP instruction for Claude to execute.
|
|
117
|
-
* Instead of making direct API calls, we generate instructions
|
|
118
|
-
* that Claude will execute using the Atlassian MCP tools.
|
|
119
|
-
*/
|
|
120
|
-
export interface MCPInstruction {
|
|
121
|
-
tool: string
|
|
122
|
-
params: Record<string, unknown>
|
|
123
|
-
description: string
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Generate MCP instruction for searching JIRA issues
|
|
128
|
-
*/
|
|
129
|
-
export function createSearchInstruction(jql: string, maxResults = 50): MCPInstruction {
|
|
130
|
-
return {
|
|
131
|
-
tool: 'mcp__atlassian__jira_search_issues',
|
|
132
|
-
params: {
|
|
133
|
-
jql,
|
|
134
|
-
maxResults,
|
|
135
|
-
},
|
|
136
|
-
description: `Search JIRA issues: ${jql}`,
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Generate MCP instruction for fetching a single issue
|
|
142
|
-
*/
|
|
143
|
-
export function createGetIssueInstruction(issueKey: string): MCPInstruction {
|
|
144
|
-
return {
|
|
145
|
-
tool: 'mcp__atlassian__jira_get_issue',
|
|
146
|
-
params: {
|
|
147
|
-
issueKey,
|
|
148
|
-
},
|
|
149
|
-
description: `Get JIRA issue: ${issueKey}`,
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Generate MCP instruction for transitioning an issue
|
|
155
|
-
*/
|
|
156
|
-
export function createTransitionInstruction(
|
|
157
|
-
issueKey: string,
|
|
158
|
-
transitionName: string
|
|
159
|
-
): MCPInstruction {
|
|
160
|
-
return {
|
|
161
|
-
tool: 'mcp__atlassian__jira_transition_issue',
|
|
162
|
-
params: {
|
|
163
|
-
issueKey,
|
|
164
|
-
transitionName,
|
|
165
|
-
},
|
|
166
|
-
description: `Transition ${issueKey} to: ${transitionName}`,
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Generate MCP instruction for updating an issue
|
|
172
|
-
*/
|
|
173
|
-
export function createUpdateInstruction(
|
|
174
|
-
issueKey: string,
|
|
175
|
-
fields: Record<string, unknown>
|
|
176
|
-
): MCPInstruction {
|
|
177
|
-
return {
|
|
178
|
-
tool: 'mcp__atlassian__jira_update_issue',
|
|
179
|
-
params: {
|
|
180
|
-
issueKey,
|
|
181
|
-
fields,
|
|
182
|
-
},
|
|
183
|
-
description: `Update JIRA issue: ${issueKey}`,
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Generate MCP instruction for creating an issue
|
|
189
|
-
*/
|
|
190
|
-
export function createCreateIssueInstruction(
|
|
191
|
-
projectKey: string,
|
|
192
|
-
issueType: string,
|
|
193
|
-
summary: string,
|
|
194
|
-
description?: string
|
|
195
|
-
): MCPInstruction {
|
|
196
|
-
return {
|
|
197
|
-
tool: 'mcp__atlassian__jira_create_issue',
|
|
198
|
-
params: {
|
|
199
|
-
projectKey,
|
|
200
|
-
issueType,
|
|
201
|
-
summary,
|
|
202
|
-
description,
|
|
203
|
-
},
|
|
204
|
-
description: `Create JIRA issue in ${projectKey}: ${summary}`,
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// =============================================================================
|
|
209
|
-
// MCP Adapter Implementation
|
|
210
|
-
// =============================================================================
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* JIRA MCP Adapter
|
|
214
|
-
*
|
|
215
|
-
* This adapter doesn't make direct API calls. Instead, it provides:
|
|
216
|
-
* 1. MCPInstruction objects that Claude executes using MCP tools
|
|
217
|
-
* 2. Mapping functions to convert MCP responses to normalized Issue types
|
|
218
|
-
*
|
|
219
|
-
* Usage in templates:
|
|
220
|
-
* - Check if MCP mode is active
|
|
221
|
-
* - Call adapter methods to get instructions
|
|
222
|
-
* - Claude executes the MCP tools
|
|
223
|
-
* - Parse results with mapMCPIssue()
|
|
224
|
-
*/
|
|
225
|
-
export class JiraMCPAdapter implements Partial<IssueTrackerProvider> {
|
|
226
|
-
readonly name = 'jira' as const
|
|
227
|
-
readonly displayName = 'JIRA (MCP)'
|
|
228
|
-
|
|
229
|
-
private config: JiraConfig | null = null
|
|
230
|
-
private baseUrl: string = ''
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Check if adapter is ready (config loaded)
|
|
234
|
-
*/
|
|
235
|
-
isConfigured(): boolean {
|
|
236
|
-
return this.config?.enabled === true
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Initialize with config (no API verification needed - MCP handles auth)
|
|
241
|
-
*/
|
|
242
|
-
async initialize(config: JiraConfig): Promise<void> {
|
|
243
|
-
this.config = config
|
|
244
|
-
this.baseUrl = config.baseUrl || ''
|
|
245
|
-
console.log('[jira-mcp] Initialized MCP adapter')
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Get instruction to fetch assigned issues
|
|
250
|
-
*/
|
|
251
|
-
getAssignedIssuesInstruction(options?: FetchOptions): MCPInstruction {
|
|
252
|
-
const maxResults = options?.limit || 50
|
|
253
|
-
const jql = options?.includeCompleted
|
|
254
|
-
? 'assignee = currentUser() ORDER BY updated DESC'
|
|
255
|
-
: 'assignee = currentUser() AND statusCategory != Done ORDER BY updated DESC'
|
|
256
|
-
|
|
257
|
-
return createSearchInstruction(jql, maxResults)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Get instruction to fetch team issues
|
|
262
|
-
*/
|
|
263
|
-
getTeamIssuesInstruction(projectKey: string, options?: FetchOptions): MCPInstruction {
|
|
264
|
-
const maxResults = options?.limit || 50
|
|
265
|
-
const jql = options?.includeCompleted
|
|
266
|
-
? `project = ${projectKey} ORDER BY updated DESC`
|
|
267
|
-
: `project = ${projectKey} AND statusCategory != Done ORDER BY updated DESC`
|
|
268
|
-
|
|
269
|
-
return createSearchInstruction(jql, maxResults)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Get instruction to fetch a single issue
|
|
274
|
-
*/
|
|
275
|
-
getIssueInstruction(issueKey: string): MCPInstruction {
|
|
276
|
-
return createGetIssueInstruction(issueKey)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Get instruction to mark issue in progress
|
|
281
|
-
*/
|
|
282
|
-
getMarkInProgressInstruction(issueKey: string): MCPInstruction {
|
|
283
|
-
return createTransitionInstruction(issueKey, 'In Progress')
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Get instruction to mark issue done
|
|
288
|
-
*/
|
|
289
|
-
getMarkDoneInstruction(issueKey: string): MCPInstruction {
|
|
290
|
-
return createTransitionInstruction(issueKey, 'Done')
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Get instruction to update issue description
|
|
295
|
-
*/
|
|
296
|
-
getUpdateDescriptionInstruction(issueKey: string, description: string): MCPInstruction {
|
|
297
|
-
return createUpdateInstruction(issueKey, { description })
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Get instruction to create a new issue
|
|
302
|
-
*/
|
|
303
|
-
getCreateIssueInstruction(input: CreateIssueInput): MCPInstruction {
|
|
304
|
-
const projectKey = input.teamId || this.config?.projectKey
|
|
305
|
-
if (!projectKey) {
|
|
306
|
-
throw new Error('Project key required for creating issues')
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return createCreateIssueInstruction(
|
|
310
|
-
projectKey,
|
|
311
|
-
this.mapTypeToJira(input.type),
|
|
312
|
-
input.title,
|
|
313
|
-
input.description
|
|
314
|
-
)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Map MCP response to normalized Issue
|
|
319
|
-
*/
|
|
320
|
-
mapMCPIssue(mcpIssue: MCPJiraIssue): Issue {
|
|
321
|
-
const statusName = mcpIssue.fields.status.name.toLowerCase()
|
|
322
|
-
const statusCategory = mcpIssue.fields.status.statusCategory?.key || ''
|
|
323
|
-
|
|
324
|
-
const status: IssueStatus =
|
|
325
|
-
JIRA_STATUS_NAME_MAP[statusName] || JIRA_STATUS_CATEGORY_MAP[statusCategory] || 'backlog'
|
|
326
|
-
|
|
327
|
-
const priorityName = mcpIssue.fields.priority?.name?.toLowerCase() || 'medium'
|
|
328
|
-
const priority: IssuePriority = JIRA_PRIORITY_MAP[priorityName] || 'medium'
|
|
329
|
-
|
|
330
|
-
return {
|
|
331
|
-
id: mcpIssue.id,
|
|
332
|
-
externalId: mcpIssue.key,
|
|
333
|
-
provider: 'jira',
|
|
334
|
-
title: mcpIssue.fields.summary,
|
|
335
|
-
description: mcpIssue.fields.description,
|
|
336
|
-
status,
|
|
337
|
-
priority,
|
|
338
|
-
type: this.inferType(mcpIssue.fields.issuetype.name, mcpIssue.fields.labels || []),
|
|
339
|
-
assignee: mcpIssue.fields.assignee
|
|
340
|
-
? {
|
|
341
|
-
id: mcpIssue.fields.assignee.accountId,
|
|
342
|
-
name: mcpIssue.fields.assignee.displayName,
|
|
343
|
-
email: mcpIssue.fields.assignee.emailAddress,
|
|
344
|
-
}
|
|
345
|
-
: undefined,
|
|
346
|
-
labels: mcpIssue.fields.labels || [],
|
|
347
|
-
team: {
|
|
348
|
-
id: mcpIssue.fields.project.id,
|
|
349
|
-
name: mcpIssue.fields.project.name,
|
|
350
|
-
key: mcpIssue.fields.project.key,
|
|
351
|
-
},
|
|
352
|
-
project: {
|
|
353
|
-
id: mcpIssue.fields.project.id,
|
|
354
|
-
name: mcpIssue.fields.project.name,
|
|
355
|
-
},
|
|
356
|
-
url: this.baseUrl
|
|
357
|
-
? `${this.baseUrl}/browse/${mcpIssue.key}`
|
|
358
|
-
: `https://jira.atlassian.com/browse/${mcpIssue.key}`,
|
|
359
|
-
createdAt: mcpIssue.fields.created,
|
|
360
|
-
updatedAt: mcpIssue.fields.updated,
|
|
361
|
-
raw: mcpIssue,
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Map array of MCP issues
|
|
367
|
-
*/
|
|
368
|
-
mapMCPIssues(mcpIssues: MCPJiraIssue[]): Issue[] {
|
|
369
|
-
return mcpIssues.map((issue) => this.mapMCPIssue(issue))
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// ===========================================================================
|
|
373
|
-
// Private Helpers
|
|
374
|
-
// ===========================================================================
|
|
375
|
-
|
|
376
|
-
private inferType(issueTypeName: string, labels: string[]): IssueType {
|
|
377
|
-
const typeLower = issueTypeName.toLowerCase()
|
|
378
|
-
const labelsLower = labels.map((l) => l.toLowerCase())
|
|
379
|
-
|
|
380
|
-
if (typeLower === 'bug' || labelsLower.includes('bug')) return 'bug'
|
|
381
|
-
if (typeLower === 'story' || typeLower === 'feature' || labelsLower.includes('feature'))
|
|
382
|
-
return 'feature'
|
|
383
|
-
if (typeLower === 'improvement' || labelsLower.includes('improvement')) return 'improvement'
|
|
384
|
-
if (typeLower === 'epic') return 'epic'
|
|
385
|
-
if (typeLower === 'sub-task' || typeLower === 'subtask') return 'task'
|
|
386
|
-
|
|
387
|
-
return 'task'
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
private mapTypeToJira(type?: IssueType): string {
|
|
391
|
-
switch (type) {
|
|
392
|
-
case 'bug':
|
|
393
|
-
return 'Bug'
|
|
394
|
-
case 'feature':
|
|
395
|
-
return 'Story'
|
|
396
|
-
case 'improvement':
|
|
397
|
-
return 'Improvement'
|
|
398
|
-
case 'epic':
|
|
399
|
-
return 'Epic'
|
|
400
|
-
default:
|
|
401
|
-
return 'Task'
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Singleton instance
|
|
407
|
-
export const jiraMCPAdapter = new JiraMCPAdapter()
|
|
408
|
-
|
|
409
|
-
// =============================================================================
|
|
410
|
-
// Utility: Check if MCP is available
|
|
411
|
-
// =============================================================================
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Check if Atlassian MCP tools are available in the current session.
|
|
415
|
-
* This is determined by checking the MCP configuration.
|
|
416
|
-
*/
|
|
417
|
-
export function isMCPAvailable(): boolean {
|
|
418
|
-
// In template context, Claude can detect available MCP tools
|
|
419
|
-
// This function serves as documentation for the check
|
|
420
|
-
// Actual detection happens in the template execution
|
|
421
|
-
return true
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Get MCP setup instructions for users
|
|
426
|
-
*/
|
|
427
|
-
export function getMCPSetupInstructions(): string {
|
|
428
|
-
return `
|
|
429
|
-
## JIRA MCP Setup
|
|
430
|
-
|
|
431
|
-
Add to ~/.claude/mcp.json:
|
|
432
|
-
|
|
433
|
-
\`\`\`json
|
|
434
|
-
{
|
|
435
|
-
"mcpServers": {
|
|
436
|
-
"Atlassian": {
|
|
437
|
-
"command": "npx",
|
|
438
|
-
"args": ["-y", "mcp-remote@latest", "https://mcp.atlassian.com/v1/sse"]
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
\`\`\`
|
|
443
|
-
|
|
444
|
-
Then restart Claude Code and authenticate via browser when prompted.
|
|
445
|
-
`.trim()
|
|
446
|
-
}
|