claude-ide-bridge 2.4.2 → 2.4.4
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/README.md +8 -6
- package/dist/bridge.js +5 -0
- package/dist/bridge.js.map +1 -1
- package/dist/config.d.ts +8 -0
- package/dist/config.js +33 -1
- package/dist/config.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/oauth.d.ts +47 -32
- package/dist/oauth.js +331 -275
- package/dist/oauth.js.map +1 -1
- package/dist/server.d.ts +13 -0
- package/dist/server.js +93 -54
- package/dist/server.js.map +1 -1
- package/dist/tools/getBufferContent.js +2 -1
- package/dist/tools/getBufferContent.js.map +1 -1
- package/dist/tools/getDiagnostics.js +2 -1
- package/dist/tools/getDiagnostics.js.map +1 -1
- package/dist/tools/git-utils.js.map +1 -1
- package/dist/tools/handoffNote.d.ts +1 -0
- package/dist/tools/handoffNote.js +1 -1
- package/dist/tools/handoffNote.js.map +1 -1
- package/dist/tools/lsp.js +7 -2
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/openFile.js +3 -1
- package/dist/tools/openFile.js.map +1 -1
- package/dist/tools/openInBrowser.js +49 -0
- package/dist/tools/openInBrowser.js.map +1 -1
- package/dist/tools/searchAndReplace.js.map +1 -1
- package/dist/tools/searchWorkspace.js.map +1 -1
- package/dist/tools/terminal.js +8 -7
- package/dist/tools/terminal.js.map +1 -1
- package/package.json +3 -3
- package/dist/tools/aiComments.d.ts +0 -26
- package/dist/tools/aiComments.js +0 -196
- package/dist/tools/aiComments.js.map +0 -1
- package/dist/tools/diffDebugger.d.ts +0 -62
- package/dist/tools/diffDebugger.js +0 -245
- package/dist/tools/diffDebugger.js.map +0 -1
- package/dist/tools/flowGuardian.d.ts +0 -61
- package/dist/tools/flowGuardian.js +0 -311
- package/dist/tools/flowGuardian.js.map +0 -1
- package/dist/tools/formatFile.d.ts +0 -28
- package/dist/tools/formatFile.js +0 -110
- package/dist/tools/formatFile.js.map +0 -1
- package/dist/tools/github/review.d.ts +0 -101
- package/dist/tools/github/review.js +0 -292
- package/dist/tools/github/review.js.map +0 -1
- package/dist/tools/github.d.ts +0 -308
- package/dist/tools/github.js +0 -656
- package/dist/tools/github.js.map +0 -1
- package/dist/tools/notebook.d.ts +0 -93
- package/dist/tools/notebook.js +0 -207
- package/dist/tools/notebook.js.map +0 -1
- package/dist/tools/tasks.d.ts +0 -56
- package/dist/tools/tasks.js +0 -170
- package/dist/tools/tasks.js.map +0 -1
- package/dist/tools/workspaceSnapshots.d.ts +0 -174
- package/dist/tools/workspaceSnapshots.js +0 -474
- package/dist/tools/workspaceSnapshots.js.map +0 -1
package/dist/tools/github.js
DELETED
|
@@ -1,656 +0,0 @@
|
|
|
1
|
-
import { error, execSafe, optionalInt, optionalString, requireString, success, } from "./utils.js";
|
|
2
|
-
const GH_NOT_FOUND = "GitHub CLI (gh) not found. Install it from https://cli.github.com/ and run 'gh auth login'.";
|
|
3
|
-
const GH_NOT_AUTHED = "Not authenticated with GitHub. Run 'gh auth login' first.";
|
|
4
|
-
function isNotFound(msg) {
|
|
5
|
-
return msg.includes("ENOENT") || msg.includes("executable file not found");
|
|
6
|
-
}
|
|
7
|
-
function isNotAuthed(msg) {
|
|
8
|
-
return (msg.includes("not authenticated") ||
|
|
9
|
-
msg.includes("auth login") ||
|
|
10
|
-
msg.includes("GITHUB_TOKEN"));
|
|
11
|
-
}
|
|
12
|
-
export function createGithubCreatePRTool(workspace) {
|
|
13
|
-
return {
|
|
14
|
-
schema: {
|
|
15
|
-
name: "githubCreatePR",
|
|
16
|
-
description: "Create a GitHub pull request for the current branch using the GitHub CLI (gh). " +
|
|
17
|
-
"Requires gh to be installed (https://cli.github.com/) and authenticated via 'gh auth login'. " +
|
|
18
|
-
"When no body is provided, uses commit messages to fill the description (--fill). " +
|
|
19
|
-
"Returns the PR URL and number.",
|
|
20
|
-
annotations: { destructiveHint: true },
|
|
21
|
-
inputSchema: {
|
|
22
|
-
type: "object",
|
|
23
|
-
required: ["title"],
|
|
24
|
-
properties: {
|
|
25
|
-
title: {
|
|
26
|
-
type: "string",
|
|
27
|
-
description: "Pull request title",
|
|
28
|
-
},
|
|
29
|
-
body: {
|
|
30
|
-
type: "string",
|
|
31
|
-
description: "Pull request description. If omitted, uses commit messages to fill the body.",
|
|
32
|
-
},
|
|
33
|
-
base: {
|
|
34
|
-
type: "string",
|
|
35
|
-
description: "Base branch to merge into (default: repository default branch)",
|
|
36
|
-
},
|
|
37
|
-
draft: {
|
|
38
|
-
type: "boolean",
|
|
39
|
-
description: "Create as a draft pull request. Default: false.",
|
|
40
|
-
},
|
|
41
|
-
assignee: {
|
|
42
|
-
type: "string",
|
|
43
|
-
description: "GitHub username to assign the PR to",
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
additionalProperties: false,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
handler: async (args, signal) => {
|
|
50
|
-
const title = requireString(args, "title", 256);
|
|
51
|
-
const body = optionalString(args, "body", 65_536);
|
|
52
|
-
const base = optionalString(args, "base", 256);
|
|
53
|
-
const draft = args.draft ?? false;
|
|
54
|
-
const assignee = optionalString(args, "assignee", 256);
|
|
55
|
-
const prArgs = ["pr", "create", "--title", title];
|
|
56
|
-
if (body !== undefined) {
|
|
57
|
-
prArgs.push("--body", body);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
prArgs.push("--fill");
|
|
61
|
-
}
|
|
62
|
-
if (base)
|
|
63
|
-
prArgs.push("--base", base);
|
|
64
|
-
if (draft)
|
|
65
|
-
prArgs.push("--draft");
|
|
66
|
-
if (assignee)
|
|
67
|
-
prArgs.push("--assignee", assignee);
|
|
68
|
-
prArgs.push("--");
|
|
69
|
-
const result = await execSafe("gh", prArgs, {
|
|
70
|
-
cwd: workspace,
|
|
71
|
-
signal,
|
|
72
|
-
timeout: 60_000,
|
|
73
|
-
});
|
|
74
|
-
if (result.exitCode !== 0) {
|
|
75
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
76
|
-
if (isNotFound(msg))
|
|
77
|
-
return error(GH_NOT_FOUND);
|
|
78
|
-
if (isNotAuthed(msg))
|
|
79
|
-
return error(`${GH_NOT_AUTHED}\n${msg}`);
|
|
80
|
-
if (msg.includes("already exists")) {
|
|
81
|
-
return error(`A pull request already exists for this branch.\n${msg}`);
|
|
82
|
-
}
|
|
83
|
-
if (msg.includes("No commits between")) {
|
|
84
|
-
return error(`No commits between head branch and base — nothing to open a PR for.\n${msg}`);
|
|
85
|
-
}
|
|
86
|
-
return error(`gh pr create failed: ${msg}`);
|
|
87
|
-
}
|
|
88
|
-
// gh prints the PR URL on success
|
|
89
|
-
const url = result.stdout.trim();
|
|
90
|
-
const numberMatch = url.match(/\/pull\/(\d+)/);
|
|
91
|
-
const number = numberMatch ? Number.parseInt(numberMatch[1], 10) : null;
|
|
92
|
-
return success({ url, number, title });
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
export function createGithubListPRsTool(workspace) {
|
|
97
|
-
return {
|
|
98
|
-
schema: {
|
|
99
|
-
name: "githubListPRs",
|
|
100
|
-
description: "List pull requests for the current GitHub repository using the GitHub CLI (gh). " +
|
|
101
|
-
"Requires gh to be installed and authenticated.",
|
|
102
|
-
annotations: { readOnlyHint: true },
|
|
103
|
-
inputSchema: {
|
|
104
|
-
type: "object",
|
|
105
|
-
properties: {
|
|
106
|
-
state: {
|
|
107
|
-
type: "string",
|
|
108
|
-
enum: ["open", "closed", "merged", "all"],
|
|
109
|
-
description: "Filter by PR state. Default: open.",
|
|
110
|
-
},
|
|
111
|
-
limit: {
|
|
112
|
-
type: "integer",
|
|
113
|
-
description: "Maximum number of PRs to return (default: 20, max: 100)",
|
|
114
|
-
},
|
|
115
|
-
author: {
|
|
116
|
-
type: "string",
|
|
117
|
-
description: "Filter by author GitHub username",
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
additionalProperties: false,
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
handler: async (args, signal) => {
|
|
124
|
-
const state = optionalString(args, "state", 32) ?? "open";
|
|
125
|
-
const limit = optionalInt(args, "limit", 1, 100) ?? 20;
|
|
126
|
-
const author = optionalString(args, "author", 256);
|
|
127
|
-
if (!["open", "closed", "merged", "all"].includes(state)) {
|
|
128
|
-
return error(`Invalid state "${state}". Must be: open, closed, merged, or all.`);
|
|
129
|
-
}
|
|
130
|
-
const listArgs = [
|
|
131
|
-
"pr",
|
|
132
|
-
"list",
|
|
133
|
-
"--state",
|
|
134
|
-
state,
|
|
135
|
-
"--limit",
|
|
136
|
-
String(limit),
|
|
137
|
-
"--json",
|
|
138
|
-
"number,title,state,url,headRefName,baseRefName,author,createdAt,isDraft",
|
|
139
|
-
];
|
|
140
|
-
if (author)
|
|
141
|
-
listArgs.push("--author", author);
|
|
142
|
-
const result = await execSafe("gh", listArgs, {
|
|
143
|
-
cwd: workspace,
|
|
144
|
-
signal,
|
|
145
|
-
timeout: 30_000,
|
|
146
|
-
});
|
|
147
|
-
if (result.exitCode !== 0) {
|
|
148
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
149
|
-
if (isNotFound(msg))
|
|
150
|
-
return error(GH_NOT_FOUND);
|
|
151
|
-
if (isNotAuthed(msg))
|
|
152
|
-
return error(GH_NOT_AUTHED);
|
|
153
|
-
return error(`gh pr list failed: ${msg}`);
|
|
154
|
-
}
|
|
155
|
-
let prs;
|
|
156
|
-
try {
|
|
157
|
-
prs = JSON.parse(result.stdout.trim());
|
|
158
|
-
}
|
|
159
|
-
catch {
|
|
160
|
-
return error(`Failed to parse gh output: ${result.stdout.trim()}`);
|
|
161
|
-
}
|
|
162
|
-
return success({ prs, count: Array.isArray(prs) ? prs.length : 0 });
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
export function createGithubViewPRTool(workspace) {
|
|
167
|
-
return {
|
|
168
|
-
schema: {
|
|
169
|
-
name: "githubViewPR",
|
|
170
|
-
description: "View details of a GitHub pull request using the GitHub CLI (gh). " +
|
|
171
|
-
"Omit number to view the PR for the current branch. " +
|
|
172
|
-
"Requires gh to be installed and authenticated.",
|
|
173
|
-
annotations: { readOnlyHint: true },
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: "object",
|
|
176
|
-
properties: {
|
|
177
|
-
number: {
|
|
178
|
-
type: "integer",
|
|
179
|
-
description: "PR number to view. Omit to view the PR associated with the current branch.",
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
additionalProperties: false,
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
handler: async (args, signal) => {
|
|
186
|
-
const number = typeof args.number === "number" ? Math.floor(args.number) : undefined;
|
|
187
|
-
const viewArgs = [
|
|
188
|
-
"pr",
|
|
189
|
-
"view",
|
|
190
|
-
"--json",
|
|
191
|
-
"number,title,state,url,body,author,createdAt,updatedAt,baseRefName,headRefName,isDraft,mergeable,reviewDecision,reviews",
|
|
192
|
-
];
|
|
193
|
-
if (number !== undefined)
|
|
194
|
-
viewArgs.splice(2, 0, String(number));
|
|
195
|
-
const result = await execSafe("gh", viewArgs, {
|
|
196
|
-
cwd: workspace,
|
|
197
|
-
signal,
|
|
198
|
-
timeout: 30_000,
|
|
199
|
-
});
|
|
200
|
-
if (result.exitCode !== 0) {
|
|
201
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
202
|
-
if (isNotFound(msg))
|
|
203
|
-
return error(GH_NOT_FOUND);
|
|
204
|
-
if (isNotAuthed(msg))
|
|
205
|
-
return error(GH_NOT_AUTHED);
|
|
206
|
-
if (msg.includes("no pull requests found") ||
|
|
207
|
-
msg.includes("no open pull request")) {
|
|
208
|
-
return error(number
|
|
209
|
-
? `PR #${number} not found.`
|
|
210
|
-
: "No open pull request for the current branch. Create one with githubCreatePR.");
|
|
211
|
-
}
|
|
212
|
-
return error(`gh pr view failed: ${msg}`);
|
|
213
|
-
}
|
|
214
|
-
let pr;
|
|
215
|
-
try {
|
|
216
|
-
pr = JSON.parse(result.stdout.trim());
|
|
217
|
-
}
|
|
218
|
-
catch {
|
|
219
|
-
return error(`Failed to parse gh output: ${result.stdout.trim()}`);
|
|
220
|
-
}
|
|
221
|
-
return success(pr);
|
|
222
|
-
},
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
export function createGithubListIssuesTool(workspace) {
|
|
226
|
-
return {
|
|
227
|
-
schema: {
|
|
228
|
-
name: "githubListIssues",
|
|
229
|
-
description: "List issues for the current GitHub repository using the GitHub CLI (gh). " +
|
|
230
|
-
"Requires gh to be installed and authenticated.",
|
|
231
|
-
annotations: { readOnlyHint: true },
|
|
232
|
-
inputSchema: {
|
|
233
|
-
type: "object",
|
|
234
|
-
properties: {
|
|
235
|
-
state: {
|
|
236
|
-
type: "string",
|
|
237
|
-
enum: ["open", "closed", "all"],
|
|
238
|
-
description: "Filter by issue state. Default: open.",
|
|
239
|
-
},
|
|
240
|
-
limit: {
|
|
241
|
-
type: "integer",
|
|
242
|
-
description: "Maximum number of issues to return (default: 20, max: 100)",
|
|
243
|
-
},
|
|
244
|
-
assignee: {
|
|
245
|
-
type: "string",
|
|
246
|
-
description: "Filter by assignee GitHub username. Use '@me' for issues assigned to you.",
|
|
247
|
-
},
|
|
248
|
-
label: {
|
|
249
|
-
type: "string",
|
|
250
|
-
description: "Filter by label name",
|
|
251
|
-
},
|
|
252
|
-
milestone: {
|
|
253
|
-
type: "string",
|
|
254
|
-
description: "Filter by milestone title or number",
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
additionalProperties: false,
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
handler: async (args, signal) => {
|
|
261
|
-
const state = optionalString(args, "state", 32) ?? "open";
|
|
262
|
-
const limit = optionalInt(args, "limit", 1, 100) ?? 20;
|
|
263
|
-
const assignee = optionalString(args, "assignee", 256);
|
|
264
|
-
const label = optionalString(args, "label", 256);
|
|
265
|
-
const milestone = optionalString(args, "milestone", 256);
|
|
266
|
-
if (!["open", "closed", "all"].includes(state)) {
|
|
267
|
-
return error(`Invalid state "${state}". Must be: open, closed, or all.`);
|
|
268
|
-
}
|
|
269
|
-
const listArgs = [
|
|
270
|
-
"issue",
|
|
271
|
-
"list",
|
|
272
|
-
"--state",
|
|
273
|
-
state,
|
|
274
|
-
"--limit",
|
|
275
|
-
String(limit),
|
|
276
|
-
"--json",
|
|
277
|
-
"number,title,state,url,author,assignees,labels,createdAt,updatedAt",
|
|
278
|
-
];
|
|
279
|
-
if (assignee)
|
|
280
|
-
listArgs.push("--assignee", assignee);
|
|
281
|
-
if (label)
|
|
282
|
-
listArgs.push("--label", label);
|
|
283
|
-
if (milestone)
|
|
284
|
-
listArgs.push("--milestone", milestone);
|
|
285
|
-
const result = await execSafe("gh", listArgs, {
|
|
286
|
-
cwd: workspace,
|
|
287
|
-
signal,
|
|
288
|
-
timeout: 30_000,
|
|
289
|
-
});
|
|
290
|
-
if (result.exitCode !== 0) {
|
|
291
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
292
|
-
if (isNotFound(msg))
|
|
293
|
-
return error(GH_NOT_FOUND);
|
|
294
|
-
if (isNotAuthed(msg))
|
|
295
|
-
return error(GH_NOT_AUTHED);
|
|
296
|
-
return error(`gh issue list failed: ${msg}`);
|
|
297
|
-
}
|
|
298
|
-
let issues;
|
|
299
|
-
try {
|
|
300
|
-
issues = JSON.parse(result.stdout.trim());
|
|
301
|
-
}
|
|
302
|
-
catch {
|
|
303
|
-
return error(`Failed to parse gh output: ${result.stdout.trim()}`);
|
|
304
|
-
}
|
|
305
|
-
return success({
|
|
306
|
-
issues,
|
|
307
|
-
count: Array.isArray(issues) ? issues.length : 0,
|
|
308
|
-
});
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
export function createGithubGetIssueTool(workspace) {
|
|
313
|
-
return {
|
|
314
|
-
schema: {
|
|
315
|
-
name: "githubGetIssue",
|
|
316
|
-
description: "View full details of a GitHub issue including body and comments. " +
|
|
317
|
-
"Requires gh to be installed and authenticated.",
|
|
318
|
-
annotations: { readOnlyHint: true },
|
|
319
|
-
inputSchema: {
|
|
320
|
-
type: "object",
|
|
321
|
-
required: ["number"],
|
|
322
|
-
properties: {
|
|
323
|
-
number: {
|
|
324
|
-
type: "integer",
|
|
325
|
-
description: "Issue number to view",
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
additionalProperties: false,
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
handler: async (args, signal) => {
|
|
332
|
-
const number = typeof args.number === "number" ? Math.floor(args.number) : undefined;
|
|
333
|
-
if (!number || number < 1)
|
|
334
|
-
return error("number must be a positive integer");
|
|
335
|
-
const viewArgs = [
|
|
336
|
-
"issue",
|
|
337
|
-
"view",
|
|
338
|
-
String(number),
|
|
339
|
-
"--json",
|
|
340
|
-
"number,title,state,url,body,author,assignees,labels,createdAt,updatedAt,comments,milestone",
|
|
341
|
-
];
|
|
342
|
-
const result = await execSafe("gh", viewArgs, {
|
|
343
|
-
cwd: workspace,
|
|
344
|
-
signal,
|
|
345
|
-
timeout: 30_000,
|
|
346
|
-
});
|
|
347
|
-
if (result.exitCode !== 0) {
|
|
348
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
349
|
-
if (isNotFound(msg))
|
|
350
|
-
return error(GH_NOT_FOUND);
|
|
351
|
-
if (isNotAuthed(msg))
|
|
352
|
-
return error(GH_NOT_AUTHED);
|
|
353
|
-
if (msg.includes("Could not resolve") || msg.includes("not found")) {
|
|
354
|
-
return error(`Issue #${number} not found.`);
|
|
355
|
-
}
|
|
356
|
-
return error(`gh issue view failed: ${msg}`);
|
|
357
|
-
}
|
|
358
|
-
let issue;
|
|
359
|
-
try {
|
|
360
|
-
issue = JSON.parse(result.stdout.trim());
|
|
361
|
-
}
|
|
362
|
-
catch {
|
|
363
|
-
return error(`Failed to parse gh output: ${result.stdout.trim()}`);
|
|
364
|
-
}
|
|
365
|
-
return success(issue);
|
|
366
|
-
},
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
export function createGithubCreateIssueTool(workspace) {
|
|
370
|
-
return {
|
|
371
|
-
schema: {
|
|
372
|
-
name: "githubCreateIssue",
|
|
373
|
-
description: "Create a GitHub issue using the GitHub CLI (gh). " +
|
|
374
|
-
"Requires gh to be installed and authenticated. Returns the issue URL and number.",
|
|
375
|
-
annotations: { destructiveHint: true },
|
|
376
|
-
inputSchema: {
|
|
377
|
-
type: "object",
|
|
378
|
-
required: ["title"],
|
|
379
|
-
properties: {
|
|
380
|
-
title: {
|
|
381
|
-
type: "string",
|
|
382
|
-
description: "Issue title",
|
|
383
|
-
},
|
|
384
|
-
body: {
|
|
385
|
-
type: "string",
|
|
386
|
-
description: "Issue body / description (Markdown supported)",
|
|
387
|
-
},
|
|
388
|
-
assignee: {
|
|
389
|
-
type: "string",
|
|
390
|
-
description: "GitHub username to assign the issue to. Use '@me' to self-assign.",
|
|
391
|
-
},
|
|
392
|
-
label: {
|
|
393
|
-
type: "string",
|
|
394
|
-
description: "Label to apply to the issue",
|
|
395
|
-
},
|
|
396
|
-
milestone: {
|
|
397
|
-
type: "string",
|
|
398
|
-
description: "Milestone title or number to add the issue to",
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
additionalProperties: false,
|
|
402
|
-
},
|
|
403
|
-
},
|
|
404
|
-
handler: async (args, signal) => {
|
|
405
|
-
const title = requireString(args, "title", 256);
|
|
406
|
-
const body = optionalString(args, "body", 65_536);
|
|
407
|
-
const assignee = optionalString(args, "assignee", 256);
|
|
408
|
-
const label = optionalString(args, "label", 256);
|
|
409
|
-
const milestone = optionalString(args, "milestone", 256);
|
|
410
|
-
const issueArgs = ["issue", "create", "--title", title];
|
|
411
|
-
if (body !== undefined) {
|
|
412
|
-
issueArgs.push("--body", body);
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
issueArgs.push("--body", "");
|
|
416
|
-
}
|
|
417
|
-
if (assignee)
|
|
418
|
-
issueArgs.push("--assignee", assignee);
|
|
419
|
-
if (label)
|
|
420
|
-
issueArgs.push("--label", label);
|
|
421
|
-
if (milestone)
|
|
422
|
-
issueArgs.push("--milestone", milestone);
|
|
423
|
-
issueArgs.push("--");
|
|
424
|
-
const result = await execSafe("gh", issueArgs, {
|
|
425
|
-
cwd: workspace,
|
|
426
|
-
signal,
|
|
427
|
-
timeout: 30_000,
|
|
428
|
-
});
|
|
429
|
-
if (result.exitCode !== 0) {
|
|
430
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
431
|
-
if (isNotFound(msg))
|
|
432
|
-
return error(GH_NOT_FOUND);
|
|
433
|
-
if (isNotAuthed(msg))
|
|
434
|
-
return error(`${GH_NOT_AUTHED}\n${msg}`);
|
|
435
|
-
return error(`gh issue create failed: ${msg}`);
|
|
436
|
-
}
|
|
437
|
-
const url = result.stdout.trim();
|
|
438
|
-
const numberMatch = url.match(/\/issues\/(\d+)/);
|
|
439
|
-
const number = numberMatch ? Number.parseInt(numberMatch[1], 10) : null;
|
|
440
|
-
return success({ url, number, title });
|
|
441
|
-
},
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
export function createGithubCommentIssueTool(workspace) {
|
|
445
|
-
return {
|
|
446
|
-
schema: {
|
|
447
|
-
name: "githubCommentIssue",
|
|
448
|
-
description: "Add a comment to a GitHub issue using the GitHub CLI (gh). " +
|
|
449
|
-
"Requires gh to be installed and authenticated. Returns the comment URL.",
|
|
450
|
-
annotations: { destructiveHint: true },
|
|
451
|
-
inputSchema: {
|
|
452
|
-
type: "object",
|
|
453
|
-
required: ["number", "body"],
|
|
454
|
-
properties: {
|
|
455
|
-
number: {
|
|
456
|
-
type: "integer",
|
|
457
|
-
description: "Issue number to comment on",
|
|
458
|
-
},
|
|
459
|
-
body: {
|
|
460
|
-
type: "string",
|
|
461
|
-
description: "Comment body (Markdown supported)",
|
|
462
|
-
},
|
|
463
|
-
},
|
|
464
|
-
additionalProperties: false,
|
|
465
|
-
},
|
|
466
|
-
},
|
|
467
|
-
handler: async (args, signal) => {
|
|
468
|
-
const number = typeof args.number === "number" ? Math.floor(args.number) : undefined;
|
|
469
|
-
if (!number || number < 1)
|
|
470
|
-
return error("number must be a positive integer");
|
|
471
|
-
const body = requireString(args, "body", 65_536);
|
|
472
|
-
const commentArgs = [
|
|
473
|
-
"issue",
|
|
474
|
-
"comment",
|
|
475
|
-
String(number),
|
|
476
|
-
"--body",
|
|
477
|
-
body,
|
|
478
|
-
"--",
|
|
479
|
-
];
|
|
480
|
-
const result = await execSafe("gh", commentArgs, {
|
|
481
|
-
cwd: workspace,
|
|
482
|
-
signal,
|
|
483
|
-
timeout: 30_000,
|
|
484
|
-
});
|
|
485
|
-
if (result.exitCode !== 0) {
|
|
486
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
487
|
-
if (isNotFound(msg))
|
|
488
|
-
return error(GH_NOT_FOUND);
|
|
489
|
-
if (isNotAuthed(msg))
|
|
490
|
-
return error(`${GH_NOT_AUTHED}\n${msg}`);
|
|
491
|
-
if (msg.includes("Could not resolve") || msg.includes("not found")) {
|
|
492
|
-
return error(`Issue #${number} not found.`);
|
|
493
|
-
}
|
|
494
|
-
return error(`gh issue comment failed: ${msg}`);
|
|
495
|
-
}
|
|
496
|
-
// gh prints the comment URL on success
|
|
497
|
-
const url = result.stdout.trim();
|
|
498
|
-
return success({ url, issueNumber: number });
|
|
499
|
-
},
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
export function createGithubListRunsTool(workspace) {
|
|
503
|
-
return {
|
|
504
|
-
schema: {
|
|
505
|
-
name: "githubListRuns",
|
|
506
|
-
description: "List GitHub Actions workflow runs for the current repository using the GitHub CLI (gh). " +
|
|
507
|
-
"Use this to check CI status after a push or PR. The run ID (databaseId) can be passed to " +
|
|
508
|
-
"githubGetRunLogs to retrieve failure details. Requires gh to be installed and authenticated.",
|
|
509
|
-
annotations: { readOnlyHint: true },
|
|
510
|
-
inputSchema: {
|
|
511
|
-
type: "object",
|
|
512
|
-
properties: {
|
|
513
|
-
branch: {
|
|
514
|
-
type: "string",
|
|
515
|
-
description: "Filter by branch name. Omit to see runs across all branches.",
|
|
516
|
-
},
|
|
517
|
-
workflow: {
|
|
518
|
-
type: "string",
|
|
519
|
-
description: "Filter by workflow file name (e.g. 'ci.yml') or workflow name",
|
|
520
|
-
},
|
|
521
|
-
status: {
|
|
522
|
-
type: "string",
|
|
523
|
-
description: "Filter by run status: queued, in_progress, completed, failure, success, cancelled. " +
|
|
524
|
-
"Omit to see all statuses.",
|
|
525
|
-
},
|
|
526
|
-
limit: {
|
|
527
|
-
type: "integer",
|
|
528
|
-
description: "Maximum number of runs to return (default: 10, max: 50)",
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
additionalProperties: false,
|
|
532
|
-
},
|
|
533
|
-
},
|
|
534
|
-
handler: async (args, signal) => {
|
|
535
|
-
const branch = optionalString(args, "branch", 256);
|
|
536
|
-
const workflow = optionalString(args, "workflow", 256);
|
|
537
|
-
const status = optionalString(args, "status", 64);
|
|
538
|
-
const limit = optionalInt(args, "limit", 1, 50) ?? 10;
|
|
539
|
-
const listArgs = [
|
|
540
|
-
"run",
|
|
541
|
-
"list",
|
|
542
|
-
"--limit",
|
|
543
|
-
String(limit),
|
|
544
|
-
"--json",
|
|
545
|
-
"databaseId,name,status,conclusion,headBranch,headSha,url,createdAt,updatedAt,workflowName,event",
|
|
546
|
-
];
|
|
547
|
-
if (branch)
|
|
548
|
-
listArgs.push("--branch", branch);
|
|
549
|
-
if (workflow)
|
|
550
|
-
listArgs.push("--workflow", workflow);
|
|
551
|
-
if (status)
|
|
552
|
-
listArgs.push("--status", status);
|
|
553
|
-
const result = await execSafe("gh", listArgs, {
|
|
554
|
-
cwd: workspace,
|
|
555
|
-
signal,
|
|
556
|
-
timeout: 30_000,
|
|
557
|
-
});
|
|
558
|
-
if (result.exitCode !== 0) {
|
|
559
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
560
|
-
if (isNotFound(msg))
|
|
561
|
-
return error(GH_NOT_FOUND);
|
|
562
|
-
if (isNotAuthed(msg))
|
|
563
|
-
return error(GH_NOT_AUTHED);
|
|
564
|
-
return error(`gh run list failed: ${msg}`);
|
|
565
|
-
}
|
|
566
|
-
let runs;
|
|
567
|
-
try {
|
|
568
|
-
runs = JSON.parse(result.stdout.trim());
|
|
569
|
-
}
|
|
570
|
-
catch {
|
|
571
|
-
return error(`Failed to parse gh output: ${result.stdout.trim()}`);
|
|
572
|
-
}
|
|
573
|
-
return success({ runs, count: Array.isArray(runs) ? runs.length : 0 });
|
|
574
|
-
},
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
const MAX_RUN_LOG_BYTES = 100 * 1024; // 100 KB — gh run logs can be enormous
|
|
578
|
-
export function createGithubGetRunLogsTool(workspace) {
|
|
579
|
-
return {
|
|
580
|
-
schema: {
|
|
581
|
-
name: "githubGetRunLogs",
|
|
582
|
-
description: "Get logs from a GitHub Actions workflow run using the GitHub CLI (gh). " +
|
|
583
|
-
"By default returns only the failed steps' logs (most useful for diagnosing CI failures). " +
|
|
584
|
-
"Pass the databaseId from githubListRuns as the runId. " +
|
|
585
|
-
"Requires gh to be installed and authenticated.",
|
|
586
|
-
annotations: { readOnlyHint: true },
|
|
587
|
-
inputSchema: {
|
|
588
|
-
type: "object",
|
|
589
|
-
required: ["runId"],
|
|
590
|
-
properties: {
|
|
591
|
-
runId: {
|
|
592
|
-
type: "integer",
|
|
593
|
-
description: "Workflow run ID (databaseId from githubListRuns)",
|
|
594
|
-
},
|
|
595
|
-
failedOnly: {
|
|
596
|
-
type: "boolean",
|
|
597
|
-
description: "Return only logs from failed steps (default: true). Set false for full logs.",
|
|
598
|
-
},
|
|
599
|
-
},
|
|
600
|
-
additionalProperties: false,
|
|
601
|
-
},
|
|
602
|
-
},
|
|
603
|
-
handler: async (args, signal) => {
|
|
604
|
-
const runId = typeof args.runId === "number" ? Math.floor(args.runId) : undefined;
|
|
605
|
-
if (!runId || runId < 1)
|
|
606
|
-
return error("runId must be a positive integer");
|
|
607
|
-
const failedOnly = args.failedOnly ?? true;
|
|
608
|
-
const viewArgs = [
|
|
609
|
-
"run",
|
|
610
|
-
"view",
|
|
611
|
-
String(runId),
|
|
612
|
-
`--log${failedOnly ? "-failed" : ""}`,
|
|
613
|
-
];
|
|
614
|
-
const result = await execSafe("gh", viewArgs, {
|
|
615
|
-
cwd: workspace,
|
|
616
|
-
signal,
|
|
617
|
-
timeout: 60_000,
|
|
618
|
-
});
|
|
619
|
-
if (result.exitCode !== 0) {
|
|
620
|
-
const msg = result.stderr.trim() || result.stdout.trim();
|
|
621
|
-
if (isNotFound(msg))
|
|
622
|
-
return error(GH_NOT_FOUND);
|
|
623
|
-
if (isNotAuthed(msg))
|
|
624
|
-
return error(GH_NOT_AUTHED);
|
|
625
|
-
if (msg.includes("no failed") || msg.includes("no logs")) {
|
|
626
|
-
return success({
|
|
627
|
-
logs: "",
|
|
628
|
-
note: "No failed step logs found — the run may have succeeded or logs may have expired.",
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
if (msg.includes("Could not find") || msg.includes("not found")) {
|
|
632
|
-
return error(`Run #${runId} not found.`);
|
|
633
|
-
}
|
|
634
|
-
// gh exits non-zero when run is still in progress for --log-failed
|
|
635
|
-
if (msg.includes("in progress") || msg.includes("still running")) {
|
|
636
|
-
return error(`Run #${runId} is still in progress. Wait for it to complete before fetching logs.`);
|
|
637
|
-
}
|
|
638
|
-
return error(`gh run view failed: ${msg}`);
|
|
639
|
-
}
|
|
640
|
-
let logs = result.stdout;
|
|
641
|
-
let truncated = false;
|
|
642
|
-
if (Buffer.byteLength(logs, "utf8") > MAX_RUN_LOG_BYTES) {
|
|
643
|
-
// Keep the tail — failure details are usually at the end
|
|
644
|
-
logs = `...[truncated — showing last portion]\n${logs.slice(-MAX_RUN_LOG_BYTES)}`;
|
|
645
|
-
truncated = true;
|
|
646
|
-
}
|
|
647
|
-
return success({
|
|
648
|
-
runId,
|
|
649
|
-
failedOnly,
|
|
650
|
-
logs,
|
|
651
|
-
truncated: truncated || undefined,
|
|
652
|
-
});
|
|
653
|
-
},
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
//# sourceMappingURL=github.js.map
|