@happyvertical/github-actions 0.74.8
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/AGENT.md +33 -0
- package/LICENSE +7 -0
- package/README.md +147 -0
- package/dist/cli/claude-context.d.ts +3 -0
- package/dist/cli/claude-context.d.ts.map +1 -0
- package/dist/cli/claude-context.js +21 -0
- package/dist/cli/claude-context.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +325 -0
- package/dist/cli.js.map +1 -0
- package/dist/index-v2-CqFKwTm8.js +687 -0
- package/dist/index-v2-CqFKwTm8.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +637 -0
- package/dist/index.js.map +1 -0
- package/dist/labels-cli.d.ts +20 -0
- package/dist/labels-cli.d.ts.map +1 -0
- package/dist/planning/analyze.d.ts +13 -0
- package/dist/planning/analyze.d.ts.map +1 -0
- package/dist/planning/comment.d.ts +15 -0
- package/dist/planning/comment.d.ts.map +1 -0
- package/dist/planning/definition-of-ready.d.ts +15 -0
- package/dist/planning/definition-of-ready.d.ts.map +1 -0
- package/dist/planning/index.d.ts +14 -0
- package/dist/planning/index.d.ts.map +1 -0
- package/dist/planning/types.d.ts +71 -0
- package/dist/planning/types.d.ts.map +1 -0
- package/dist/shared/adapters.d.ts +11 -0
- package/dist/shared/adapters.d.ts.map +1 -0
- package/dist/shared/ai.d.ts +43 -0
- package/dist/shared/ai.d.ts.map +1 -0
- package/dist/shared/github.d.ts +52 -0
- package/dist/shared/github.d.ts.map +1 -0
- package/dist/shared/index.d.ts +9 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/labels.d.ts +35 -0
- package/dist/shared/labels.d.ts.map +1 -0
- package/dist/shared/projects.d.ts +34 -0
- package/dist/shared/projects.d.ts.map +1 -0
- package/dist/triage/analyze.d.ts +12 -0
- package/dist/triage/analyze.d.ts.map +1 -0
- package/dist/triage/comment.d.ts +17 -0
- package/dist/triage/comment.d.ts.map +1 -0
- package/dist/triage/duplicates.d.ts +12 -0
- package/dist/triage/duplicates.d.ts.map +1 -0
- package/dist/triage/github.d.ts +5 -0
- package/dist/triage/github.d.ts.map +1 -0
- package/dist/triage/index-v2.d.ts +19 -0
- package/dist/triage/index-v2.d.ts.map +1 -0
- package/dist/triage/index.d.ts +13 -0
- package/dist/triage/index.d.ts.map +1 -0
- package/dist/triage/label-v2.d.ts +20 -0
- package/dist/triage/label-v2.d.ts.map +1 -0
- package/dist/triage/label.d.ts +16 -0
- package/dist/triage/label.d.ts.map +1 -0
- package/dist/triage/project-v2.d.ts +12 -0
- package/dist/triage/project-v2.d.ts.map +1 -0
- package/dist/triage/project.d.ts +7 -0
- package/dist/triage/project.d.ts.map +1 -0
- package/dist/triage/types.d.ts +64 -0
- package/dist/triage/types.d.ts.map +1 -0
- package/metadata.json +30 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
import { c as createRepository, a as createProject, g as githubAPI, b as analyzeIssue, s as searchDuplicates, p as postTriageComment, d as postErrorComment } from "./index-v2-CqFKwTm8.js";
|
|
2
|
+
import { A, L, S, e, f, h, i, j, k, l, m, n, o, q, r, t, u, v, w } from "./index-v2-CqFKwTm8.js";
|
|
3
|
+
async function analyzePlanning(context) {
|
|
4
|
+
const systemPrompt = `You are a software development planning assistant for the ${context.owner}/${context.repo} repository.
|
|
5
|
+
|
|
6
|
+
Repository: ${context.config.repoDescription}
|
|
7
|
+
${context.config.packagePattern ? `Package pattern: ${context.config.packagePattern}` : ""}
|
|
8
|
+
${context.config.packageExamples ? `Example packages: ${context.config.packageExamples.join(", ")}` : ""}
|
|
9
|
+
|
|
10
|
+
Your task is to create a detailed implementation plan for issues. Analyze the requirements and provide:
|
|
11
|
+
1. A clear summary of what needs to be done
|
|
12
|
+
2. A step-by-step task breakdown
|
|
13
|
+
3. Complexity assessment (simple, moderate, complex)
|
|
14
|
+
4. Technical considerations
|
|
15
|
+
5. Files likely to be affected
|
|
16
|
+
6. Dependencies or blockers`;
|
|
17
|
+
const userPrompt = `Issue #${context.issueNumber}: ${context.issueTitle}
|
|
18
|
+
|
|
19
|
+
${context.issueBody || "No description provided"}
|
|
20
|
+
|
|
21
|
+
Please create a detailed implementation plan following this JSON structure:
|
|
22
|
+
{
|
|
23
|
+
"summary": "Brief summary of the implementation",
|
|
24
|
+
"tasks": ["Step 1", "Step 2", "Step 3"],
|
|
25
|
+
"complexity": "simple|moderate|complex",
|
|
26
|
+
"considerations": ["Technical consideration 1", "Technical consideration 2"],
|
|
27
|
+
"affected_files": ["file1.ts", "file2.ts"],
|
|
28
|
+
"dependencies": ["Optional blocker or dependency"]
|
|
29
|
+
}`;
|
|
30
|
+
console.log("AI Planning Analysis would be called here");
|
|
31
|
+
console.log("System:", systemPrompt);
|
|
32
|
+
console.log("User:", userPrompt);
|
|
33
|
+
return {
|
|
34
|
+
summary: `Implementation plan for: ${context.issueTitle}`,
|
|
35
|
+
tasks: [
|
|
36
|
+
"Analyze requirements and design approach",
|
|
37
|
+
"Implement core functionality",
|
|
38
|
+
"Add tests",
|
|
39
|
+
"Update documentation"
|
|
40
|
+
],
|
|
41
|
+
complexity: "moderate",
|
|
42
|
+
considerations: [
|
|
43
|
+
"Ensure backward compatibility",
|
|
44
|
+
"Follow existing code patterns",
|
|
45
|
+
"Add comprehensive error handling"
|
|
46
|
+
],
|
|
47
|
+
affected_files: [],
|
|
48
|
+
dependencies: []
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function validateDefinitionOfReady(issue, hasPlanComment) {
|
|
52
|
+
const labels = issue.labels.map((l2) => typeof l2 === "string" ? l2 : l2.name);
|
|
53
|
+
return {
|
|
54
|
+
hasClearDescription: !!issue.body && issue.body.trim().length > 50,
|
|
55
|
+
hasTypeLabel: labels.some((l2) => l2.startsWith("type:")),
|
|
56
|
+
hasPriorityLabel: labels.some((l2) => l2.startsWith("priority:")),
|
|
57
|
+
hasSizeLabel: labels.some((l2) => l2.startsWith("size:")),
|
|
58
|
+
hasPlan: hasPlanComment,
|
|
59
|
+
noBlockers: !labels.some((l2) => l2 === "status: blocked")
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function isReady(dor) {
|
|
63
|
+
return dor.hasClearDescription && dor.hasTypeLabel && dor.hasPriorityLabel && dor.hasSizeLabel && dor.hasPlan && dor.noBlockers;
|
|
64
|
+
}
|
|
65
|
+
function formatDefinitionOfReady(dor) {
|
|
66
|
+
const checkbox = (checked) => checked ? "[x]" : "[ ]";
|
|
67
|
+
return `## Definition of Ready
|
|
68
|
+
|
|
69
|
+
${checkbox(dor.hasClearDescription)} Clear, actionable description
|
|
70
|
+
${checkbox(dor.hasTypeLabel)} Type label applied
|
|
71
|
+
${checkbox(dor.hasPriorityLabel)} Priority label applied
|
|
72
|
+
${checkbox(dor.hasSizeLabel)} Size label applied
|
|
73
|
+
${checkbox(dor.hasPlan)} Implementation plan documented
|
|
74
|
+
${checkbox(dor.noBlockers)} No blocking dependencies
|
|
75
|
+
|
|
76
|
+
${isReady(dor) ? "✅ **This issue is ready for implementation!**" : "⚠️ **This issue needs more work before it's ready.**"}`;
|
|
77
|
+
}
|
|
78
|
+
async function postPlanComment(repo, issueNumber, plan) {
|
|
79
|
+
const comment = `## 🤖 Implementation Plan
|
|
80
|
+
|
|
81
|
+
### Summary
|
|
82
|
+
${plan.summary}
|
|
83
|
+
|
|
84
|
+
### Tasks
|
|
85
|
+
${plan.tasks.map((task, i2) => `${i2 + 1}. ${task}`).join("\n")}
|
|
86
|
+
|
|
87
|
+
### Complexity
|
|
88
|
+
**${plan.complexity.charAt(0).toUpperCase() + plan.complexity.slice(1)}**
|
|
89
|
+
|
|
90
|
+
### Technical Considerations
|
|
91
|
+
${plan.considerations.map((c) => `- ${c}`).join("\n")}
|
|
92
|
+
|
|
93
|
+
${plan.affected_files && plan.affected_files.length > 0 ? `### Affected Files
|
|
94
|
+
${plan.affected_files.map((f2) => `- \`${f2}\``).join("\n")}` : ""}
|
|
95
|
+
|
|
96
|
+
${plan.dependencies && plan.dependencies.length > 0 ? `### Dependencies
|
|
97
|
+
${plan.dependencies.map((d) => `- ${d}`).join("\n")}` : ""}
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
*This plan was generated by AI. Please review and provide feedback in the comments.*`;
|
|
101
|
+
await repo.addComment(issueNumber, comment);
|
|
102
|
+
}
|
|
103
|
+
async function postReadyCheckComment(repo, issueNumber, dor) {
|
|
104
|
+
const comment = formatDefinitionOfReady(dor);
|
|
105
|
+
await repo.addComment(issueNumber, comment);
|
|
106
|
+
}
|
|
107
|
+
async function postPlanningErrorComment(repo, issueNumber, error) {
|
|
108
|
+
const comment = `## ⚠️ Planning Failed
|
|
109
|
+
|
|
110
|
+
An error occurred while creating the implementation plan:
|
|
111
|
+
|
|
112
|
+
\`\`\`
|
|
113
|
+
${error.message}
|
|
114
|
+
\`\`\`
|
|
115
|
+
|
|
116
|
+
Please try again or create a manual implementation plan in the comments.`;
|
|
117
|
+
await repo.addComment(issueNumber, comment);
|
|
118
|
+
}
|
|
119
|
+
async function startPlanning(context) {
|
|
120
|
+
console.log(
|
|
121
|
+
`Starting planning for issue #${context.issueNumber}: ${context.issueTitle}`
|
|
122
|
+
);
|
|
123
|
+
try {
|
|
124
|
+
const repo = await createRepository(
|
|
125
|
+
context.token,
|
|
126
|
+
context.owner,
|
|
127
|
+
context.repo
|
|
128
|
+
);
|
|
129
|
+
await repo.addLabels(context.issueNumber, ["agent: planning"]);
|
|
130
|
+
console.log("Generating implementation plan...");
|
|
131
|
+
const plan = await analyzePlanning(context);
|
|
132
|
+
await postPlanComment(repo, context.issueNumber, plan);
|
|
133
|
+
console.log("✅ Planning started successfully");
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
plan
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("❌ Planning failed:", error.message);
|
|
140
|
+
try {
|
|
141
|
+
const repo = await createRepository(
|
|
142
|
+
context.token,
|
|
143
|
+
context.owner,
|
|
144
|
+
context.repo
|
|
145
|
+
);
|
|
146
|
+
await postPlanningErrorComment(repo, context.issueNumber, error);
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: error.message
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function completePlanning(context) {
|
|
156
|
+
console.log(
|
|
157
|
+
`Completing planning for issue #${context.issueNumber}: ${context.issueTitle}`
|
|
158
|
+
);
|
|
159
|
+
try {
|
|
160
|
+
const repo = await createRepository(
|
|
161
|
+
context.token,
|
|
162
|
+
context.owner,
|
|
163
|
+
context.repo
|
|
164
|
+
);
|
|
165
|
+
const issue = await repo.getIssue(context.issueNumber);
|
|
166
|
+
const comments = await repo.listComments(context.issueNumber);
|
|
167
|
+
const hasPlanComment = comments.some(
|
|
168
|
+
(c) => c.body?.includes("Implementation Plan")
|
|
169
|
+
);
|
|
170
|
+
const dor = validateDefinitionOfReady(issue, hasPlanComment);
|
|
171
|
+
await postReadyCheckComment(repo, context.issueNumber, dor);
|
|
172
|
+
if (!isReady(dor)) {
|
|
173
|
+
console.log("⚠️ Issue does not meet Definition of Ready criteria");
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
error: "Issue does not meet Definition of Ready criteria"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
await repo.removeLabel(context.issueNumber, "agent: planning");
|
|
180
|
+
if (context.config.projectId && context.config.statusFieldId && context.config.statusOptions) {
|
|
181
|
+
const project = await createProject(
|
|
182
|
+
context.token,
|
|
183
|
+
context.config.projectId,
|
|
184
|
+
context.config.statusFieldId,
|
|
185
|
+
context.config.statusOptions
|
|
186
|
+
);
|
|
187
|
+
const items = await project.listItems();
|
|
188
|
+
const itemId = items.find(
|
|
189
|
+
(item) => item.contentId === issue.id
|
|
190
|
+
)?.id;
|
|
191
|
+
if (itemId) {
|
|
192
|
+
await project.updateItemStatus(itemId, "Ready");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
console.log("✅ Planning completed, moved to Ready");
|
|
196
|
+
return {
|
|
197
|
+
success: true
|
|
198
|
+
};
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error("❌ Complete planning failed:", error.message);
|
|
201
|
+
return {
|
|
202
|
+
success: false,
|
|
203
|
+
error: error.message
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function githubRequest(context, method, path, body) {
|
|
208
|
+
const url = `https://api.github.com${path}`;
|
|
209
|
+
const headers = {
|
|
210
|
+
Authorization: `Bearer ${context.token}`,
|
|
211
|
+
Accept: "application/vnd.github+json",
|
|
212
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
213
|
+
"Content-Type": "application/json"
|
|
214
|
+
};
|
|
215
|
+
const response = await fetch(url, {
|
|
216
|
+
method,
|
|
217
|
+
headers,
|
|
218
|
+
body: body ? JSON.stringify(body) : void 0
|
|
219
|
+
});
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
const error = await response.text();
|
|
222
|
+
throw new Error(
|
|
223
|
+
`GitHub API error: ${response.status} ${response.statusText}
|
|
224
|
+
${error}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return response.json();
|
|
228
|
+
}
|
|
229
|
+
async function githubGraphQL(token, query, variables) {
|
|
230
|
+
const response = await fetch("https://api.github.com/graphql", {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: `Bearer ${token}`,
|
|
234
|
+
"Content-Type": "application/json"
|
|
235
|
+
},
|
|
236
|
+
body: JSON.stringify({ query, variables })
|
|
237
|
+
});
|
|
238
|
+
if (!response.ok) {
|
|
239
|
+
const error = await response.text();
|
|
240
|
+
throw new Error(
|
|
241
|
+
`GitHub GraphQL error: ${response.status} ${response.statusText}
|
|
242
|
+
${error}`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
const result = await response.json();
|
|
246
|
+
if (result.errors) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
`GitHub GraphQL errors: ${result.errors.map((e2) => e2.message).join(", ")}`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return result.data;
|
|
252
|
+
}
|
|
253
|
+
async function addLabels(context, issueNumber, labels) {
|
|
254
|
+
await githubRequest(
|
|
255
|
+
context,
|
|
256
|
+
"POST",
|
|
257
|
+
`/repos/${context.owner}/${context.repo}/issues/${issueNumber}/labels`,
|
|
258
|
+
{ labels }
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
async function removeLabel(context, issueNumber, label) {
|
|
262
|
+
const encodedLabel = encodeURIComponent(label);
|
|
263
|
+
await githubRequest(
|
|
264
|
+
context,
|
|
265
|
+
"DELETE",
|
|
266
|
+
`/repos/${context.owner}/${context.repo}/issues/${issueNumber}/labels/${encodedLabel}`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
async function postComment(context, issueNumber, body) {
|
|
270
|
+
await githubRequest(
|
|
271
|
+
context,
|
|
272
|
+
"POST",
|
|
273
|
+
`/repos/${context.owner}/${context.repo}/issues/${issueNumber}/comments`,
|
|
274
|
+
{ body }
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
async function assignIssue(context, issueNumber, assignees) {
|
|
278
|
+
await githubRequest(
|
|
279
|
+
context,
|
|
280
|
+
"POST",
|
|
281
|
+
`/repos/${context.owner}/${context.repo}/issues/${issueNumber}/assignees`,
|
|
282
|
+
{ assignees }
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
async function createOrUpdateLabel(context, name, color, description) {
|
|
286
|
+
try {
|
|
287
|
+
await githubRequest(
|
|
288
|
+
context,
|
|
289
|
+
"PATCH",
|
|
290
|
+
`/repos/${context.owner}/${context.repo}/labels/${encodeURIComponent(name)}`,
|
|
291
|
+
{ color, description }
|
|
292
|
+
);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
if (error.message.includes("404")) {
|
|
295
|
+
await githubRequest(
|
|
296
|
+
context,
|
|
297
|
+
"POST",
|
|
298
|
+
`/repos/${context.owner}/${context.repo}/labels`,
|
|
299
|
+
{ name, color, description }
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
throw error;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function getIssue(context, issueNumber) {
|
|
307
|
+
return await githubRequest(
|
|
308
|
+
context,
|
|
309
|
+
"GET",
|
|
310
|
+
`/repos/${context.owner}/${context.repo}/issues/${issueNumber}`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
async function addIssueToProject(token, projectId, issueId) {
|
|
314
|
+
const mutation = `
|
|
315
|
+
mutation($projectId: ID!, $contentId: ID!) {
|
|
316
|
+
addProjectV2ItemById(input: {
|
|
317
|
+
projectId: $projectId
|
|
318
|
+
contentId: $contentId
|
|
319
|
+
}) {
|
|
320
|
+
item {
|
|
321
|
+
id
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
`;
|
|
326
|
+
const result = await githubGraphQL(token, mutation, {
|
|
327
|
+
projectId,
|
|
328
|
+
contentId: issueId
|
|
329
|
+
});
|
|
330
|
+
return result.addProjectV2ItemById.item.id;
|
|
331
|
+
}
|
|
332
|
+
async function updateProjectItemStatus(token, projectId, itemId, statusFieldId, statusOptionId) {
|
|
333
|
+
const mutation = `
|
|
334
|
+
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: ProjectV2FieldValue!) {
|
|
335
|
+
updateProjectV2ItemFieldValue(input: {
|
|
336
|
+
projectId: $projectId
|
|
337
|
+
itemId: $itemId
|
|
338
|
+
fieldId: $fieldId
|
|
339
|
+
value: $value
|
|
340
|
+
}) {
|
|
341
|
+
projectV2Item {
|
|
342
|
+
id
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
`;
|
|
347
|
+
await githubGraphQL(token, mutation, {
|
|
348
|
+
projectId,
|
|
349
|
+
itemId,
|
|
350
|
+
fieldId: statusFieldId,
|
|
351
|
+
value: {
|
|
352
|
+
singleSelectOptionId: statusOptionId
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
async function getIssueNodeId(token, owner, repo, issueNumber) {
|
|
357
|
+
const query = `
|
|
358
|
+
query($owner: String!, $repo: String!, $issueNumber: Int!) {
|
|
359
|
+
repository(owner: $owner, name: $repo) {
|
|
360
|
+
issue(number: $issueNumber) {
|
|
361
|
+
id
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
`;
|
|
366
|
+
const result = await githubGraphQL(token, query, {
|
|
367
|
+
owner,
|
|
368
|
+
repo,
|
|
369
|
+
issueNumber
|
|
370
|
+
});
|
|
371
|
+
return result.repository.issue.id;
|
|
372
|
+
}
|
|
373
|
+
async function getProjectItemId(token, projectId, issueId) {
|
|
374
|
+
const query = `
|
|
375
|
+
query($projectId: ID!) {
|
|
376
|
+
node(id: $projectId) {
|
|
377
|
+
... on ProjectV2 {
|
|
378
|
+
items(first: 100) {
|
|
379
|
+
nodes {
|
|
380
|
+
id
|
|
381
|
+
content {
|
|
382
|
+
... on Issue {
|
|
383
|
+
id
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
`;
|
|
392
|
+
const result = await githubGraphQL(token, query, {
|
|
393
|
+
projectId
|
|
394
|
+
});
|
|
395
|
+
const item = result.node.items.nodes.find(
|
|
396
|
+
(node) => node.content.id === issueId
|
|
397
|
+
);
|
|
398
|
+
return item ? item.id : null;
|
|
399
|
+
}
|
|
400
|
+
async function moveIssueToStatus(token, owner, repo, issueNumber, config, statusName) {
|
|
401
|
+
const statusOptionId = config.statusOptions[statusName];
|
|
402
|
+
if (!statusOptionId) {
|
|
403
|
+
throw new Error(
|
|
404
|
+
`Status "${statusName}" not found in project configuration. Available: ${Object.keys(config.statusOptions).join(", ")}`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
console.log(`Moving issue #${issueNumber} to status "${statusName}"...`);
|
|
408
|
+
const issueId = await getIssueNodeId(token, owner, repo, issueNumber);
|
|
409
|
+
let itemId = await getProjectItemId(token, config.projectId, issueId);
|
|
410
|
+
if (!itemId) {
|
|
411
|
+
console.log("Issue not in project, adding...");
|
|
412
|
+
itemId = await addIssueToProject(token, config.projectId, issueId);
|
|
413
|
+
}
|
|
414
|
+
console.log(`Updating status to "${statusName}"...`);
|
|
415
|
+
await updateProjectItemStatus(
|
|
416
|
+
token,
|
|
417
|
+
config.projectId,
|
|
418
|
+
itemId,
|
|
419
|
+
config.statusFieldId,
|
|
420
|
+
statusOptionId
|
|
421
|
+
);
|
|
422
|
+
console.log(`✅ Issue moved to "${statusName}"`);
|
|
423
|
+
}
|
|
424
|
+
async function applyLabels(context, labels) {
|
|
425
|
+
if (labels.length === 0) {
|
|
426
|
+
console.log("No labels to apply");
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
await addLabels(context, context.issueNumber, labels);
|
|
431
|
+
console.log(`Applied labels: ${labels.join(", ")}`);
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error("Error applying labels:", error.message);
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function removeAgentLabel(context, agentType) {
|
|
438
|
+
const label = `agent: ${agentType}`;
|
|
439
|
+
try {
|
|
440
|
+
await removeLabel(context, context.issueNumber, label);
|
|
441
|
+
console.log(`Removed label: ${label}`);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (!error.message.includes("404")) {
|
|
444
|
+
console.error("Error removing label:", error.message);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function getTypeLabel(type) {
|
|
449
|
+
return `type: ${type}`;
|
|
450
|
+
}
|
|
451
|
+
function getPriorityLabel(priority) {
|
|
452
|
+
return `priority: ${priority}`;
|
|
453
|
+
}
|
|
454
|
+
function getSizeLabel(size) {
|
|
455
|
+
return `size: ${size}`;
|
|
456
|
+
}
|
|
457
|
+
function getAgentLabel(agentType) {
|
|
458
|
+
return `agent: ${agentType}`;
|
|
459
|
+
}
|
|
460
|
+
async function updateProjectStatus(context, statusName) {
|
|
461
|
+
if (!context.config.projectEnabled) {
|
|
462
|
+
console.log("Project board integration disabled");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (!context.config.projectId || !context.config.statusFieldId) {
|
|
466
|
+
console.log("Project configuration missing");
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (!context.config.statusOptions || !context.config.statusOptions[statusName]) {
|
|
470
|
+
console.log(`Status "${statusName}" not configured`);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const query = `
|
|
474
|
+
query($owner: String!, $repo: String!, $number: Int!) {
|
|
475
|
+
repository(owner: $owner, name: $repo) {
|
|
476
|
+
issue(number: $number) {
|
|
477
|
+
projectItems(first: 10) {
|
|
478
|
+
nodes {
|
|
479
|
+
id
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
`;
|
|
486
|
+
try {
|
|
487
|
+
const variables = {
|
|
488
|
+
owner: context.owner,
|
|
489
|
+
repo: context.repo,
|
|
490
|
+
number: context.issueNumber
|
|
491
|
+
};
|
|
492
|
+
const result = await githubAPI(context.token, "POST", "/graphql", {
|
|
493
|
+
query,
|
|
494
|
+
variables
|
|
495
|
+
});
|
|
496
|
+
const itemId = result.data.repository.issue.projectItems.nodes[0]?.id;
|
|
497
|
+
if (!itemId) {
|
|
498
|
+
console.log("Issue not in project board, skipping status update");
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const updateQuery = `
|
|
502
|
+
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
|
503
|
+
updateProjectV2ItemFieldValue(
|
|
504
|
+
input: {
|
|
505
|
+
projectId: $projectId
|
|
506
|
+
itemId: $itemId
|
|
507
|
+
fieldId: $fieldId
|
|
508
|
+
value: { singleSelectOptionId: $optionId }
|
|
509
|
+
}
|
|
510
|
+
) {
|
|
511
|
+
projectV2Item {
|
|
512
|
+
id
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
`;
|
|
517
|
+
const updateVars = {
|
|
518
|
+
projectId: context.config.projectId,
|
|
519
|
+
itemId,
|
|
520
|
+
fieldId: context.config.statusFieldId,
|
|
521
|
+
optionId: context.config.statusOptions[statusName]
|
|
522
|
+
};
|
|
523
|
+
await githubAPI(context.token, "POST", "/graphql", {
|
|
524
|
+
query: updateQuery,
|
|
525
|
+
variables: updateVars
|
|
526
|
+
});
|
|
527
|
+
console.log(`Updated project status to: ${statusName}`);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
console.error("Error updating project status:", error.message);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
async function triageIssue(context) {
|
|
533
|
+
console.log(`Triaging issue #${context.issueNumber}: ${context.issueTitle}`);
|
|
534
|
+
try {
|
|
535
|
+
await applyLabels(context, [getAgentLabel("triage")]);
|
|
536
|
+
console.log("Calling AI for analysis...");
|
|
537
|
+
const analysis = await analyzeIssue(context);
|
|
538
|
+
console.log("AI Analysis:", JSON.stringify(analysis, null, 2));
|
|
539
|
+
console.log("Searching for potential duplicates...");
|
|
540
|
+
const duplicates = await searchDuplicates(context);
|
|
541
|
+
console.log(`Found ${duplicates.length} potential duplicates`);
|
|
542
|
+
const labels = [
|
|
543
|
+
getTypeLabel(analysis.type),
|
|
544
|
+
getPriorityLabel(analysis.priority),
|
|
545
|
+
getSizeLabel(analysis.size)
|
|
546
|
+
];
|
|
547
|
+
await applyLabels(context, labels);
|
|
548
|
+
await postTriageComment(context, analysis, duplicates);
|
|
549
|
+
await removeAgentLabel(context, "triage");
|
|
550
|
+
if (context.config.projectEnabled) {
|
|
551
|
+
console.log("Moving issue to Backlog...");
|
|
552
|
+
await updateProjectStatus(context, "Backlog");
|
|
553
|
+
}
|
|
554
|
+
console.log("✅ Triage complete!");
|
|
555
|
+
return {
|
|
556
|
+
success: true,
|
|
557
|
+
analysis,
|
|
558
|
+
duplicates
|
|
559
|
+
};
|
|
560
|
+
} catch (error) {
|
|
561
|
+
console.error("❌ Triage failed:", error.message);
|
|
562
|
+
console.error(error.stack);
|
|
563
|
+
await postErrorComment(context, error);
|
|
564
|
+
return {
|
|
565
|
+
success: false,
|
|
566
|
+
error: error.message
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
571
|
+
__proto__: null,
|
|
572
|
+
analyzeIssue,
|
|
573
|
+
applyLabels,
|
|
574
|
+
getAgentLabel,
|
|
575
|
+
getPriorityLabel,
|
|
576
|
+
getSizeLabel,
|
|
577
|
+
getTypeLabel,
|
|
578
|
+
postErrorComment,
|
|
579
|
+
postTriageComment,
|
|
580
|
+
removeAgentLabel,
|
|
581
|
+
searchDuplicates,
|
|
582
|
+
triageIssue,
|
|
583
|
+
updateProjectStatus
|
|
584
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
585
|
+
const PACKAGE_VERSION_INITIALIZED = true;
|
|
586
|
+
export {
|
|
587
|
+
A as AREA_LABEL_TEMPLATE,
|
|
588
|
+
L as LABEL_MIGRATIONS,
|
|
589
|
+
PACKAGE_VERSION_INITIALIZED,
|
|
590
|
+
S as STANDARD_LABELS,
|
|
591
|
+
addIssueToProject,
|
|
592
|
+
addLabels,
|
|
593
|
+
analyzeIssue,
|
|
594
|
+
analyzePlanning,
|
|
595
|
+
e as applyLabels,
|
|
596
|
+
assignIssue,
|
|
597
|
+
f as callAnthropic,
|
|
598
|
+
h as callGitHubModels,
|
|
599
|
+
i as callOpenAI,
|
|
600
|
+
completePlanning,
|
|
601
|
+
createOrUpdateLabel,
|
|
602
|
+
createProject,
|
|
603
|
+
createRepository,
|
|
604
|
+
formatDefinitionOfReady,
|
|
605
|
+
j as getAICompletion,
|
|
606
|
+
k as getAgentLabel,
|
|
607
|
+
l as getAllStandardLabels,
|
|
608
|
+
getIssue,
|
|
609
|
+
getIssueNodeId,
|
|
610
|
+
m as getLabelsByCategory,
|
|
611
|
+
n as getPriorityLabel,
|
|
612
|
+
getProjectItemId,
|
|
613
|
+
o as getSizeLabel,
|
|
614
|
+
q as getTypeLabel,
|
|
615
|
+
githubGraphQL,
|
|
616
|
+
githubRequest,
|
|
617
|
+
isReady,
|
|
618
|
+
index as legacy,
|
|
619
|
+
r as migrateLabel,
|
|
620
|
+
moveIssueToStatus,
|
|
621
|
+
t as parseAIJson,
|
|
622
|
+
postComment,
|
|
623
|
+
postErrorComment,
|
|
624
|
+
postPlanComment,
|
|
625
|
+
postPlanningErrorComment,
|
|
626
|
+
postReadyCheckComment,
|
|
627
|
+
postTriageComment,
|
|
628
|
+
u as removeAgentLabel,
|
|
629
|
+
removeLabel,
|
|
630
|
+
searchDuplicates,
|
|
631
|
+
startPlanning,
|
|
632
|
+
v as triageIssue,
|
|
633
|
+
updateProjectItemStatus,
|
|
634
|
+
w as updateProjectStatus,
|
|
635
|
+
validateDefinitionOfReady
|
|
636
|
+
};
|
|
637
|
+
//# sourceMappingURL=index.js.map
|