fraim-framework 2.0.64 → 2.0.65
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/bin/fraim-mcp.js +52 -19
- package/bin/fraim.js +23 -0
- package/dist/src/cli/commands/add-ide.js +53 -14
- package/dist/src/cli/commands/doctor.js +12 -24
- package/dist/src/cli/commands/init-project.js +0 -3
- package/dist/src/cli/commands/init.js +0 -2
- package/dist/src/cli/commands/mcp.js +65 -0
- package/dist/src/cli/commands/setup.js +17 -1
- package/dist/src/cli/commands/sync.js +173 -104
- package/dist/src/cli/setup/auto-mcp-setup.js +6 -4
- package/dist/src/cli/setup/mcp-config-generator.js +65 -41
- package/dist/src/fraim/issue-tracking/ado-provider.js +304 -0
- package/dist/src/fraim/issue-tracking/factory.js +63 -0
- package/dist/src/fraim/issue-tracking/github-provider.js +200 -0
- package/dist/src/fraim/issue-tracking/types.js +7 -0
- package/dist/src/fraim/issue-tracking-config.js +83 -0
- package/dist/src/local-mcp-server/stdio-server.js +23 -3
- package/dist/src/utils/remote-sync.js +130 -0
- package/package.json +2 -3
- package/dist/src/utils/enforcement-utils.js +0 -239
- package/dist/src/utils/validate-workflows.js +0 -101
- package/registry/scripts/cleanup-branch.ts +0 -341
- package/registry/scripts/code-quality-check.sh +0 -566
- package/registry/scripts/comprehensive-explorer.py +0 -297
- package/registry/scripts/create-git-labels.sh +0 -49
- package/registry/scripts/create-website-structure.js +0 -562
- package/registry/scripts/detect-tautological-tests.sh +0 -38
- package/registry/scripts/evaluate-code-quality.ts +0 -36
- package/registry/scripts/exec-with-timeout.ts +0 -122
- package/registry/scripts/generate-engagement-emails.ts +0 -830
- package/registry/scripts/interactive-explorer.py +0 -270
- package/registry/scripts/markdown-to-pdf.js +0 -395
- package/registry/scripts/newsletter-helpers.ts +0 -777
- package/registry/scripts/pdf-styles.css +0 -172
- package/registry/scripts/prep-issue.sh +0 -548
- package/registry/scripts/productivity/build-productivity-csv.mjs +0 -242
- package/registry/scripts/productivity/fetch-pr-details.mjs +0 -144
- package/registry/scripts/productivity/productivity-report.sh +0 -147
- package/registry/scripts/profile-server.ts +0 -426
- package/registry/scripts/run-thank-you-workflow.ts +0 -122
- package/registry/scripts/scrape-site.py +0 -302
- package/registry/scripts/send-newsletter-simple.ts +0 -102
- package/registry/scripts/send-thank-you-emails.ts +0 -57
- package/registry/scripts/validate-openapi-limits.ts +0 -366
- package/registry/scripts/validate-test-coverage.ts +0 -280
- package/registry/scripts/verify-pr-comments.sh +0 -74
- package/registry/scripts/verify-test-coverage.ts +0 -36
- package/registry/stubs/workflows/azure/cost-optimization.md +0 -11
- package/registry/stubs/workflows/bootstrap/create-architecture.md +0 -11
- package/registry/stubs/workflows/bootstrap/detect-broken-windows.md +0 -11
- package/registry/stubs/workflows/bootstrap/evaluate-code-quality.md +0 -11
- package/registry/stubs/workflows/bootstrap/verify-test-coverage.md +0 -11
- package/registry/stubs/workflows/brainstorming/blue-sky-brainstorming.md +0 -11
- package/registry/stubs/workflows/brainstorming/codebase-brainstorming.md +0 -11
- package/registry/stubs/workflows/business-development/create-business-plan.md +0 -11
- package/registry/stubs/workflows/business-development/ideate-business-opportunity.md +0 -11
- package/registry/stubs/workflows/business-development/price-product.md +0 -18
- package/registry/stubs/workflows/compliance/detect-compliance-requirements.md +0 -11
- package/registry/stubs/workflows/compliance/generate-audit-evidence.md +0 -11
- package/registry/stubs/workflows/compliance/soc2-evidence-generator.md +0 -11
- package/registry/stubs/workflows/customer-development/insight-analysis.md +0 -11
- package/registry/stubs/workflows/customer-development/insight-triage.md +0 -11
- package/registry/stubs/workflows/customer-development/interview-preparation.md +0 -11
- package/registry/stubs/workflows/customer-development/linkedin-outreach.md +0 -11
- package/registry/stubs/workflows/customer-development/strategic-brainstorming.md +0 -11
- package/registry/stubs/workflows/customer-development/thank-customers.md +0 -11
- package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +0 -11
- package/registry/stubs/workflows/customer-development/users-to-target.md +0 -11
- package/registry/stubs/workflows/customer-development/weekly-newsletter.md +0 -11
- package/registry/stubs/workflows/deploy/cloud-deployment.md +0 -11
- package/registry/stubs/workflows/improve-fraim/contribute.md +0 -11
- package/registry/stubs/workflows/improve-fraim/file-issue.md +0 -11
- package/registry/stubs/workflows/learning/build-skillset.md +0 -11
- package/registry/stubs/workflows/learning/synthesize-learnings.md +0 -11
- package/registry/stubs/workflows/legal/contract-review-analysis.md +0 -11
- package/registry/stubs/workflows/legal/nda.md +0 -11
- package/registry/stubs/workflows/legal/patent-filing.md +0 -11
- package/registry/stubs/workflows/legal/saas-contract-development.md +0 -11
- package/registry/stubs/workflows/legal/trademark-filing.md +0 -11
- package/registry/stubs/workflows/marketing/content-creation.md +0 -11
- package/registry/stubs/workflows/marketing/convert-to-pdf.md +0 -11
- package/registry/stubs/workflows/marketing/create-modern-website.md +0 -11
- package/registry/stubs/workflows/marketing/domain-registration.md +0 -11
- package/registry/stubs/workflows/marketing/hbr-article.md +0 -11
- package/registry/stubs/workflows/marketing/launch-checklist.md +0 -11
- package/registry/stubs/workflows/marketing/marketing-strategy.md +0 -11
- package/registry/stubs/workflows/marketing/storytelling.md +0 -11
- package/registry/stubs/workflows/performance/analyze-performance.md +0 -11
- package/registry/stubs/workflows/product-building/design.md +0 -11
- package/registry/stubs/workflows/product-building/implement.md +0 -11
- package/registry/stubs/workflows/product-building/iterate-on-pr-comments.md +0 -11
- package/registry/stubs/workflows/product-building/prep-issue.md +0 -11
- package/registry/stubs/workflows/product-building/prototype.md +0 -11
- package/registry/stubs/workflows/product-building/resolve.md +0 -11
- package/registry/stubs/workflows/product-building/retrospect.md +0 -11
- package/registry/stubs/workflows/product-building/spec.md +0 -11
- package/registry/stubs/workflows/product-building/test.md +0 -11
- package/registry/stubs/workflows/productivity-report/productivity-report.md +0 -11
- package/registry/stubs/workflows/quality-assurance/browser-validation.md +0 -11
- package/registry/stubs/workflows/quality-assurance/iterative-improvement-cycle.md +0 -11
- package/registry/stubs/workflows/replicate/replicate-discovery.md +0 -11
- package/registry/stubs/workflows/replicate/replicate-to-issues.md +0 -11
- package/registry/stubs/workflows/reviewer/review-implementation-vs-design-spec.md +0 -11
- package/registry/stubs/workflows/reviewer/review-implementation-vs-feature-spec.md +0 -11
- package/registry/stubs/workflows/startup-credits/aws-activate-application.md +0 -11
- package/registry/stubs/workflows/startup-credits/google-cloud-application.md +0 -11
- package/registry/stubs/workflows/startup-credits/microsoft-azure-application.md +0 -11
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Azure DevOps (ADO) Issue Tracking Provider
|
|
4
|
+
*
|
|
5
|
+
* Implements the IssueTrackingProvider interface for Azure DevOps Work Items
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.AdoIssueProvider = void 0;
|
|
12
|
+
const axios_1 = __importDefault(require("axios"));
|
|
13
|
+
class AdoIssueProvider {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
// Support both cloud and on-premise ADO
|
|
17
|
+
this.baseUrl = config.baseUrl || `https://dev.azure.com/${config.organization}`;
|
|
18
|
+
}
|
|
19
|
+
async createIssue(params) {
|
|
20
|
+
const { title, body, labels, assignee, priority, dryRun } = params;
|
|
21
|
+
// ADO uses PATCH with JSON Patch format for work item creation
|
|
22
|
+
const patchDocument = [
|
|
23
|
+
{
|
|
24
|
+
op: 'add',
|
|
25
|
+
path: '/fields/System.Title',
|
|
26
|
+
value: title
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
op: 'add',
|
|
30
|
+
path: '/fields/System.Description',
|
|
31
|
+
value: body
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
// Add assignee if provided
|
|
35
|
+
if (assignee) {
|
|
36
|
+
patchDocument.push({
|
|
37
|
+
op: 'add',
|
|
38
|
+
path: '/fields/System.AssignedTo',
|
|
39
|
+
value: assignee
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Map priority to ADO priority values
|
|
43
|
+
if (priority) {
|
|
44
|
+
const adoPriority = this.mapPriorityToAdo(priority);
|
|
45
|
+
patchDocument.push({
|
|
46
|
+
op: 'add',
|
|
47
|
+
path: '/fields/Microsoft.VSTS.Common.Priority',
|
|
48
|
+
value: adoPriority
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// Add tags (ADO equivalent of labels)
|
|
52
|
+
if (labels && labels.length > 0) {
|
|
53
|
+
patchDocument.push({
|
|
54
|
+
op: 'add',
|
|
55
|
+
path: '/fields/System.Tags',
|
|
56
|
+
value: labels.join('; ')
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (dryRun) {
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
dryRun: true,
|
|
63
|
+
provider: 'ado',
|
|
64
|
+
message: `[DRY RUN] Would create ADO work item: "${title}" in ${this.config.organization}/${this.config.project}`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const url = `${this.baseUrl}/${this.config.project}/_apis/wit/workitems/$Bug?api-version=7.0`;
|
|
69
|
+
const response = await axios_1.default.post(url, patchDocument, {
|
|
70
|
+
headers: {
|
|
71
|
+
'Authorization': `Basic ${Buffer.from(`:${this.config.token}`).toString('base64')}`,
|
|
72
|
+
'Content-Type': 'application/json-patch+json',
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
issueId: response.data.id,
|
|
78
|
+
issueNumber: response.data.id,
|
|
79
|
+
htmlUrl: response.data._links.html.href,
|
|
80
|
+
provider: 'ado'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
provider: 'ado',
|
|
87
|
+
message: this.formatError(error)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async getIssue(issueId) {
|
|
92
|
+
try {
|
|
93
|
+
const url = `${this.baseUrl}/${this.config.project}/_apis/wit/workitems/${issueId}?api-version=7.0`;
|
|
94
|
+
const response = await axios_1.default.get(url, {
|
|
95
|
+
headers: {
|
|
96
|
+
'Authorization': `Basic ${Buffer.from(`:${this.config.token}`).toString('base64')}`,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
const workItem = response.data;
|
|
100
|
+
const fields = workItem.fields;
|
|
101
|
+
return {
|
|
102
|
+
id: workItem.id,
|
|
103
|
+
title: fields['System.Title'] || '',
|
|
104
|
+
body: fields['System.Description'] || '',
|
|
105
|
+
state: this.mapAdoStateToGeneric(fields['System.State']),
|
|
106
|
+
assignee: fields['System.AssignedTo']?.displayName,
|
|
107
|
+
labels: fields['System.Tags'] ? fields['System.Tags'].split('; ') : [],
|
|
108
|
+
createdAt: new Date(fields['System.CreatedDate']),
|
|
109
|
+
updatedAt: new Date(fields['System.ChangedDate']),
|
|
110
|
+
htmlUrl: workItem._links.html.href
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error.response?.status === 404) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(this.formatError(error));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async updateIssue(issueId, updates) {
|
|
121
|
+
const patchDocument = [];
|
|
122
|
+
if (updates.title) {
|
|
123
|
+
patchDocument.push({
|
|
124
|
+
op: 'replace',
|
|
125
|
+
path: '/fields/System.Title',
|
|
126
|
+
value: updates.title
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (updates.body) {
|
|
130
|
+
patchDocument.push({
|
|
131
|
+
op: 'replace',
|
|
132
|
+
path: '/fields/System.Description',
|
|
133
|
+
value: updates.body
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (updates.assignee) {
|
|
137
|
+
patchDocument.push({
|
|
138
|
+
op: 'replace',
|
|
139
|
+
path: '/fields/System.AssignedTo',
|
|
140
|
+
value: updates.assignee
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (updates.priority) {
|
|
144
|
+
patchDocument.push({
|
|
145
|
+
op: 'replace',
|
|
146
|
+
path: '/fields/Microsoft.VSTS.Common.Priority',
|
|
147
|
+
value: this.mapPriorityToAdo(updates.priority)
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (updates.labels) {
|
|
151
|
+
patchDocument.push({
|
|
152
|
+
op: 'replace',
|
|
153
|
+
path: '/fields/System.Tags',
|
|
154
|
+
value: updates.labels.join('; ')
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (updates.dryRun) {
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
dryRun: true,
|
|
161
|
+
provider: 'ado',
|
|
162
|
+
message: `[DRY RUN] Would update ADO work item #${issueId} in ${this.config.organization}/${this.config.project}`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const url = `${this.baseUrl}/${this.config.project}/_apis/wit/workitems/${issueId}?api-version=7.0`;
|
|
167
|
+
const response = await axios_1.default.patch(url, patchDocument, {
|
|
168
|
+
headers: {
|
|
169
|
+
'Authorization': `Basic ${Buffer.from(`:${this.config.token}`).toString('base64')}`,
|
|
170
|
+
'Content-Type': 'application/json-patch+json',
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
issueId: response.data.id,
|
|
176
|
+
issueNumber: response.data.id,
|
|
177
|
+
htmlUrl: response.data._links.html.href,
|
|
178
|
+
provider: 'ado'
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
provider: 'ado',
|
|
185
|
+
message: this.formatError(error)
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async listIssues(filters) {
|
|
190
|
+
let wiql = `SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo], [System.CreatedDate], [System.ChangedDate] FROM WorkItems WHERE [System.TeamProject] = '${this.config.project}'`;
|
|
191
|
+
// Add state filter
|
|
192
|
+
if (filters?.state && filters.state !== 'all') {
|
|
193
|
+
const adoState = filters.state === 'open' ? 'Active' : 'Closed';
|
|
194
|
+
wiql += ` AND [System.State] = '${adoState}'`;
|
|
195
|
+
}
|
|
196
|
+
// Add assignee filter
|
|
197
|
+
if (filters?.assignee) {
|
|
198
|
+
wiql += ` AND [System.AssignedTo] = '${filters.assignee}'`;
|
|
199
|
+
}
|
|
200
|
+
// Add label filter (tags in ADO)
|
|
201
|
+
if (filters?.labels && filters.labels.length > 0) {
|
|
202
|
+
const tagConditions = filters.labels.map(label => `[System.Tags] CONTAINS '${label}'`).join(' OR ');
|
|
203
|
+
wiql += ` AND (${tagConditions})`;
|
|
204
|
+
}
|
|
205
|
+
wiql += ' ORDER BY [System.CreatedDate] DESC';
|
|
206
|
+
try {
|
|
207
|
+
// First, execute WIQL query to get work item IDs
|
|
208
|
+
const queryUrl = `${this.baseUrl}/${this.config.project}/_apis/wit/wiql?api-version=7.0`;
|
|
209
|
+
const queryResponse = await axios_1.default.post(queryUrl, { query: wiql }, {
|
|
210
|
+
headers: {
|
|
211
|
+
'Authorization': `Basic ${Buffer.from(`:${this.config.token}`).toString('base64')}`,
|
|
212
|
+
'Content-Type': 'application/json',
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
const workItemIds = queryResponse.data.workItems.map((wi) => wi.id);
|
|
216
|
+
if (workItemIds.length === 0) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
// Limit results
|
|
220
|
+
const limitedIds = workItemIds.slice(0, filters?.limit || 30);
|
|
221
|
+
// Get detailed work item information
|
|
222
|
+
const detailsUrl = `${this.baseUrl}/${this.config.project}/_apis/wit/workitems?ids=${limitedIds.join(',')}&api-version=7.0`;
|
|
223
|
+
const detailsResponse = await axios_1.default.get(detailsUrl, {
|
|
224
|
+
headers: {
|
|
225
|
+
'Authorization': `Basic ${Buffer.from(`:${this.config.token}`).toString('base64')}`,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
return detailsResponse.data.value.map((workItem) => {
|
|
229
|
+
const fields = workItem.fields;
|
|
230
|
+
return {
|
|
231
|
+
id: workItem.id,
|
|
232
|
+
title: fields['System.Title'] || '',
|
|
233
|
+
body: fields['System.Description'] || '',
|
|
234
|
+
state: this.mapAdoStateToGeneric(fields['System.State']),
|
|
235
|
+
assignee: fields['System.AssignedTo']?.displayName,
|
|
236
|
+
labels: fields['System.Tags'] ? fields['System.Tags'].split('; ') : [],
|
|
237
|
+
createdAt: new Date(fields['System.CreatedDate']),
|
|
238
|
+
updatedAt: new Date(fields['System.ChangedDate']),
|
|
239
|
+
htmlUrl: workItem._links.html.href
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
throw new Error(this.formatError(error));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async validateConfig() {
|
|
248
|
+
try {
|
|
249
|
+
// Test by getting project info
|
|
250
|
+
const url = `${this.baseUrl}/_apis/projects/${this.config.project}?api-version=7.0`;
|
|
251
|
+
const response = await axios_1.default.get(url, {
|
|
252
|
+
headers: {
|
|
253
|
+
'Authorization': `Basic ${Buffer.from(`:${this.config.token}`).toString('base64')}`,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
return {
|
|
257
|
+
valid: true,
|
|
258
|
+
message: `Connected to ADO project: ${response.data.name}`
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
return {
|
|
263
|
+
valid: false,
|
|
264
|
+
message: this.formatError(error)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
mapPriorityToAdo(priority) {
|
|
269
|
+
switch (priority) {
|
|
270
|
+
case 'critical': return 1;
|
|
271
|
+
case 'high': return 2;
|
|
272
|
+
case 'medium': return 3;
|
|
273
|
+
case 'low': return 4;
|
|
274
|
+
default: return 3;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
mapAdoStateToGeneric(adoState) {
|
|
278
|
+
switch (adoState?.toLowerCase()) {
|
|
279
|
+
case 'active':
|
|
280
|
+
case 'new':
|
|
281
|
+
case 'approved':
|
|
282
|
+
return 'open';
|
|
283
|
+
case 'resolved':
|
|
284
|
+
case 'closed':
|
|
285
|
+
case 'done':
|
|
286
|
+
return 'closed';
|
|
287
|
+
case 'committed':
|
|
288
|
+
case 'in progress':
|
|
289
|
+
return 'in-progress';
|
|
290
|
+
default:
|
|
291
|
+
return 'open';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
formatError(error) {
|
|
295
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
296
|
+
return `ADO API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}`;
|
|
297
|
+
}
|
|
298
|
+
else if (error instanceof Error) {
|
|
299
|
+
return `ADO Error: ${error.message}`;
|
|
300
|
+
}
|
|
301
|
+
return 'Unknown ADO error';
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
exports.AdoIssueProvider = AdoIssueProvider;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Issue Tracking Provider Factory
|
|
4
|
+
*
|
|
5
|
+
* Creates the appropriate issue tracking provider based on configuration
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.IssueTrackingFactory = void 0;
|
|
9
|
+
const github_provider_1 = require("./github-provider");
|
|
10
|
+
const ado_provider_1 = require("./ado-provider");
|
|
11
|
+
class IssueTrackingFactory {
|
|
12
|
+
/**
|
|
13
|
+
* Create an issue tracking provider based on configuration
|
|
14
|
+
*/
|
|
15
|
+
static createProvider(config) {
|
|
16
|
+
switch (config.provider) {
|
|
17
|
+
case 'github':
|
|
18
|
+
if (!config.github) {
|
|
19
|
+
throw new Error('GitHub configuration is required when provider is "github"');
|
|
20
|
+
}
|
|
21
|
+
return new github_provider_1.GitHubIssueProvider(config.github);
|
|
22
|
+
case 'ado':
|
|
23
|
+
if (!config.ado) {
|
|
24
|
+
throw new Error('ADO configuration is required when provider is "ado"');
|
|
25
|
+
}
|
|
26
|
+
return new ado_provider_1.AdoIssueProvider(config.ado);
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`Unsupported issue tracking provider: ${config.provider}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get available providers
|
|
33
|
+
*/
|
|
34
|
+
static getAvailableProviders() {
|
|
35
|
+
return ['github', 'ado'];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate configuration without creating provider
|
|
39
|
+
*/
|
|
40
|
+
static validateConfig(config) {
|
|
41
|
+
switch (config.provider) {
|
|
42
|
+
case 'github':
|
|
43
|
+
if (!config.github) {
|
|
44
|
+
return { valid: false, message: 'GitHub configuration is required' };
|
|
45
|
+
}
|
|
46
|
+
if (!config.github.owner || !config.github.repo || !config.github.token) {
|
|
47
|
+
return { valid: false, message: 'GitHub configuration must include owner, repo, and token' };
|
|
48
|
+
}
|
|
49
|
+
return { valid: true };
|
|
50
|
+
case 'ado':
|
|
51
|
+
if (!config.ado) {
|
|
52
|
+
return { valid: false, message: 'ADO configuration is required' };
|
|
53
|
+
}
|
|
54
|
+
if (!config.ado.organization || !config.ado.project || !config.ado.token) {
|
|
55
|
+
return { valid: false, message: 'ADO configuration must include organization, project, and token' };
|
|
56
|
+
}
|
|
57
|
+
return { valid: true };
|
|
58
|
+
default:
|
|
59
|
+
return { valid: false, message: `Unsupported provider: ${config.provider}` };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.IssueTrackingFactory = IssueTrackingFactory;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GitHub Issue Tracking Provider
|
|
4
|
+
*
|
|
5
|
+
* Implements the IssueTrackingProvider interface for GitHub Issues
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.GitHubIssueProvider = void 0;
|
|
12
|
+
const axios_1 = __importDefault(require("axios"));
|
|
13
|
+
class GitHubIssueProvider {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.baseUrl = `https://api.github.com/repos/${config.owner}/${config.repo}`;
|
|
17
|
+
}
|
|
18
|
+
async createIssue(params) {
|
|
19
|
+
const { title, body, labels, assignee, dryRun } = params;
|
|
20
|
+
const payload = {
|
|
21
|
+
title,
|
|
22
|
+
body
|
|
23
|
+
};
|
|
24
|
+
if (labels && labels.length > 0) {
|
|
25
|
+
payload.labels = labels;
|
|
26
|
+
}
|
|
27
|
+
if (assignee) {
|
|
28
|
+
payload.assignee = assignee;
|
|
29
|
+
}
|
|
30
|
+
if (dryRun) {
|
|
31
|
+
return {
|
|
32
|
+
success: true,
|
|
33
|
+
dryRun: true,
|
|
34
|
+
provider: 'github',
|
|
35
|
+
message: `[DRY RUN] Would create GitHub issue: "${title}" in ${this.config.owner}/${this.config.repo}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const response = await axios_1.default.post(`${this.baseUrl}/issues`, payload, {
|
|
40
|
+
headers: {
|
|
41
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
42
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
issueNumber: response.data.number,
|
|
49
|
+
issueId: response.data.number,
|
|
50
|
+
htmlUrl: response.data.html_url,
|
|
51
|
+
provider: 'github'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
provider: 'github',
|
|
58
|
+
message: this.formatError(error)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async getIssue(issueId) {
|
|
63
|
+
try {
|
|
64
|
+
const response = await axios_1.default.get(`${this.baseUrl}/issues/${issueId}`, {
|
|
65
|
+
headers: {
|
|
66
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
67
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const issue = response.data;
|
|
71
|
+
return {
|
|
72
|
+
id: issue.number,
|
|
73
|
+
title: issue.title,
|
|
74
|
+
body: issue.body || '',
|
|
75
|
+
state: issue.state === 'open' ? 'open' : 'closed',
|
|
76
|
+
assignee: issue.assignee?.login,
|
|
77
|
+
labels: issue.labels.map((label) => label.name),
|
|
78
|
+
createdAt: new Date(issue.created_at),
|
|
79
|
+
updatedAt: new Date(issue.updated_at),
|
|
80
|
+
htmlUrl: issue.html_url
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error.response?.status === 404) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
throw new Error(this.formatError(error));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async updateIssue(issueId, updates) {
|
|
91
|
+
const payload = {};
|
|
92
|
+
if (updates.title)
|
|
93
|
+
payload.title = updates.title;
|
|
94
|
+
if (updates.body)
|
|
95
|
+
payload.body = updates.body;
|
|
96
|
+
if (updates.labels)
|
|
97
|
+
payload.labels = updates.labels;
|
|
98
|
+
if (updates.assignee)
|
|
99
|
+
payload.assignee = updates.assignee;
|
|
100
|
+
if (updates.dryRun) {
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
dryRun: true,
|
|
104
|
+
provider: 'github',
|
|
105
|
+
message: `[DRY RUN] Would update GitHub issue #${issueId} in ${this.config.owner}/${this.config.repo}`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const response = await axios_1.default.patch(`${this.baseUrl}/issues/${issueId}`, payload, {
|
|
110
|
+
headers: {
|
|
111
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
112
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
113
|
+
'Content-Type': 'application/json',
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
success: true,
|
|
118
|
+
issueNumber: response.data.number,
|
|
119
|
+
issueId: response.data.number,
|
|
120
|
+
htmlUrl: response.data.html_url,
|
|
121
|
+
provider: 'github'
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
provider: 'github',
|
|
128
|
+
message: this.formatError(error)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async listIssues(filters) {
|
|
133
|
+
const params = {
|
|
134
|
+
per_page: filters?.limit || 30
|
|
135
|
+
};
|
|
136
|
+
if (filters?.state && filters.state !== 'all') {
|
|
137
|
+
params.state = filters.state;
|
|
138
|
+
}
|
|
139
|
+
if (filters?.assignee) {
|
|
140
|
+
params.assignee = filters.assignee;
|
|
141
|
+
}
|
|
142
|
+
if (filters?.labels && filters.labels.length > 0) {
|
|
143
|
+
params.labels = filters.labels.join(',');
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const response = await axios_1.default.get(`${this.baseUrl}/issues`, {
|
|
147
|
+
headers: {
|
|
148
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
149
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
150
|
+
},
|
|
151
|
+
params
|
|
152
|
+
});
|
|
153
|
+
return response.data.map((issue) => ({
|
|
154
|
+
id: issue.number,
|
|
155
|
+
title: issue.title,
|
|
156
|
+
body: issue.body || '',
|
|
157
|
+
state: issue.state === 'open' ? 'open' : 'closed',
|
|
158
|
+
assignee: issue.assignee?.login,
|
|
159
|
+
labels: issue.labels.map((label) => label.name),
|
|
160
|
+
createdAt: new Date(issue.created_at),
|
|
161
|
+
updatedAt: new Date(issue.updated_at),
|
|
162
|
+
htmlUrl: issue.html_url
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
throw new Error(this.formatError(error));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async validateConfig() {
|
|
170
|
+
try {
|
|
171
|
+
// Test by getting repository info
|
|
172
|
+
const response = await axios_1.default.get(this.baseUrl, {
|
|
173
|
+
headers: {
|
|
174
|
+
'Authorization': `Bearer ${this.config.token}`,
|
|
175
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
valid: true,
|
|
180
|
+
message: `Connected to ${response.data.full_name}`
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
return {
|
|
185
|
+
valid: false,
|
|
186
|
+
message: this.formatError(error)
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
formatError(error) {
|
|
191
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
192
|
+
return `GitHub API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}`;
|
|
193
|
+
}
|
|
194
|
+
else if (error instanceof Error) {
|
|
195
|
+
return `GitHub Error: ${error.message}`;
|
|
196
|
+
}
|
|
197
|
+
return 'Unknown GitHub error';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
exports.GitHubIssueProvider = GitHubIssueProvider;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Issue Tracking Configuration Helper
|
|
4
|
+
*
|
|
5
|
+
* Provides guidance on which MCP tools to use based on configuration.
|
|
6
|
+
* This replaces custom providers with MCP tool recommendations.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getIssueTrackingGuidance = getIssueTrackingGuidance;
|
|
10
|
+
exports.getRepositoryInfo = getRepositoryInfo;
|
|
11
|
+
const config_loader_1 = require("./config-loader");
|
|
12
|
+
/**
|
|
13
|
+
* Get guidance on which MCP tools to use for issue tracking
|
|
14
|
+
*/
|
|
15
|
+
function getIssueTrackingGuidance() {
|
|
16
|
+
const config = (0, config_loader_1.loadFraimConfig)();
|
|
17
|
+
// Check for ADO configuration
|
|
18
|
+
if (config.repository?.provider === 'ado') {
|
|
19
|
+
return {
|
|
20
|
+
provider: 'ado',
|
|
21
|
+
mcpTools: {
|
|
22
|
+
createIssue: 'mcp_ado_create_work_item',
|
|
23
|
+
getIssue: 'mcp_ado_get_work_item',
|
|
24
|
+
updateIssue: 'mcp_ado_update_work_item',
|
|
25
|
+
listIssues: 'mcp_ado_list_work_items',
|
|
26
|
+
createPR: 'mcp_ado_create_pull_request',
|
|
27
|
+
getPR: 'mcp_ado_get_pull_request',
|
|
28
|
+
mergePR: 'mcp_ado_merge_pull_request'
|
|
29
|
+
},
|
|
30
|
+
config: {
|
|
31
|
+
organization: config.repository.organization,
|
|
32
|
+
project: config.repository.project
|
|
33
|
+
},
|
|
34
|
+
environmentVars: ['ADO_TOKEN', 'AZURE_DEVOPS_TOKEN'],
|
|
35
|
+
setupInstructions: `Configure ADO MCP server in your IDE with:
|
|
36
|
+
- Organization: ${config.repository.organization}
|
|
37
|
+
- Project: ${config.repository.project}
|
|
38
|
+
- Token: Set ADO_TOKEN or AZURE_DEVOPS_TOKEN environment variable`
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Default to GitHub
|
|
42
|
+
const owner = config.repository?.owner || config.git?.repoOwner || 'mathursrus';
|
|
43
|
+
const repo = config.repository?.name || config.git?.repoName || 'FRAIM';
|
|
44
|
+
return {
|
|
45
|
+
provider: 'github',
|
|
46
|
+
mcpTools: {
|
|
47
|
+
createIssue: 'mcp_github_issue_write',
|
|
48
|
+
getIssue: 'mcp_github_issue_read',
|
|
49
|
+
updateIssue: 'mcp_github_issue_write',
|
|
50
|
+
listIssues: 'mcp_github_list_issues',
|
|
51
|
+
createPR: 'mcp_github_create_pull_request',
|
|
52
|
+
getPR: 'mcp_github_pull_request_read',
|
|
53
|
+
mergePR: 'mcp_github_merge_pull_request'
|
|
54
|
+
},
|
|
55
|
+
config: {
|
|
56
|
+
owner,
|
|
57
|
+
repo
|
|
58
|
+
},
|
|
59
|
+
environmentVars: ['GITHUB_TOKEN'],
|
|
60
|
+
setupInstructions: `GitHub MCP tools are configured for:
|
|
61
|
+
- Repository: ${owner}/${repo}
|
|
62
|
+
- Token: Set GITHUB_TOKEN environment variable
|
|
63
|
+
- MCP Server: Ensure GitHub MCP server is configured in your IDE`
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get repository information from .fraim/config.json
|
|
68
|
+
*/
|
|
69
|
+
function getRepositoryInfo() {
|
|
70
|
+
try {
|
|
71
|
+
const config = (0, config_loader_1.loadFraimConfig)();
|
|
72
|
+
const owner = config.repository?.owner || config.git?.repoOwner;
|
|
73
|
+
const repo = config.repository?.name || config.git?.repoName;
|
|
74
|
+
return {
|
|
75
|
+
owner,
|
|
76
|
+
repo,
|
|
77
|
+
url: owner && repo ? `https://github.com/${owner}/${repo}.git` : undefined
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
}
|