@trim21/personal-pi-extensions 0.1.678 → 0.1.680
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 +25 -12
- package/package.json +1 -1
- package/src/claude-code/files.ts +12 -2
- package/src/codemode/declarations.ts +35 -12
- package/src/codemode/fs.ts +138 -0
- package/src/codemode/prelude.ts +44 -17
- package/src/codemode/protocol.ts +17 -2
- package/src/codemode/tool.ts +69 -11
- package/src/codemode/worker.js +48 -30
- package/src/codemode/worker.ts +3 -13
- package/src/gh/base.ts +62 -12
- package/src/gh/schemas.ts +230 -0
- package/src/gh/tools/download-release-assets.ts +51 -44
- package/src/gh/tools/get-workflow-jobs.ts +29 -18
- package/src/gh/tools/read-ci-logs.ts +37 -23
- package/src/gh/tools/read-issue-comments.ts +31 -24
- package/src/gh/tools/read-issue.ts +30 -22
- package/src/gh/tools/read-pr-comments.ts +62 -58
- package/src/gh/tools/read-pr-status.ts +31 -20
- package/src/gh/tools/read-pr.ts +30 -22
- package/src/gh/tools/wait-commit-checks.ts +35 -31
- package/src/gh/tools/wait-pr-checks.ts +30 -23
- package/src/index.ts +5 -2
- package/src/lib/tool-bus.ts +128 -8
- package/src/opencode/files.ts +12 -2
|
@@ -6,7 +6,7 @@ import { Type } from "typebox";
|
|
|
6
6
|
import { Value } from "typebox/value";
|
|
7
7
|
|
|
8
8
|
import { type ToolPendant } from "../../lib/pendant.js";
|
|
9
|
-
import type
|
|
9
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
10
10
|
import {
|
|
11
11
|
type GhClient,
|
|
12
12
|
GhError,
|
|
@@ -14,10 +14,14 @@ import {
|
|
|
14
14
|
repoArgs,
|
|
15
15
|
resolveRepo,
|
|
16
16
|
splitRepo,
|
|
17
|
+
structuredFailure,
|
|
18
|
+
type StructuredFailureResult,
|
|
19
|
+
type StructuredResultOf,
|
|
17
20
|
subtitlePendant,
|
|
18
21
|
type ToolCall,
|
|
19
|
-
|
|
22
|
+
withStructuredResult,
|
|
20
23
|
} from "../base.js";
|
|
24
|
+
import { releaseDownloadSchema } from "../schemas.js";
|
|
21
25
|
|
|
22
26
|
interface ReleaseDownloadParams {
|
|
23
27
|
repo?: string;
|
|
@@ -120,7 +124,7 @@ const GH_NO_ASSET_MATCH = "no assets match the file pattern";
|
|
|
120
124
|
*/
|
|
121
125
|
export async function downloadReleaseAssets(
|
|
122
126
|
call: ToolCall<ReleaseDownloadParams>,
|
|
123
|
-
): Promise<
|
|
127
|
+
): Promise<StructuredResultOf<typeof releaseDownloadSchema> | StructuredFailureResult> {
|
|
124
128
|
const { params, ctx, signal } = call;
|
|
125
129
|
const patterns = releasePatterns(params.pattern);
|
|
126
130
|
if (params.archive !== undefined && patterns.length > 0) {
|
|
@@ -181,59 +185,62 @@ export async function downloadReleaseAssets(
|
|
|
181
185
|
);
|
|
182
186
|
|
|
183
187
|
if (files.length === 0 && params.archive === undefined) {
|
|
188
|
+
const text = `Nothing to download from ${effectiveRepo}@${view.tagName}: the release has no assets (try archive for the source tarball)`;
|
|
184
189
|
return {
|
|
185
|
-
content: [
|
|
186
|
-
{
|
|
187
|
-
type: "text",
|
|
188
|
-
text: `Nothing to download from ${effectiveRepo}@${view.tagName}: the release has no assets (try archive for the source tarball)`,
|
|
189
|
-
},
|
|
190
|
-
],
|
|
190
|
+
content: [{ type: "text", text }],
|
|
191
191
|
details: {
|
|
192
192
|
...payload,
|
|
193
193
|
available_assets: assetNames,
|
|
194
194
|
input: params,
|
|
195
195
|
...(pendant && { pendant }),
|
|
196
196
|
},
|
|
197
|
+
structuredResult: structuredFailure(text),
|
|
197
198
|
};
|
|
198
199
|
}
|
|
199
200
|
|
|
200
|
-
return
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
return withStructuredResult(
|
|
202
|
+
{
|
|
203
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
204
|
+
details: { ...payload, input: params, ...(pendant && { pendant }) },
|
|
205
|
+
},
|
|
206
|
+
payload,
|
|
207
|
+
);
|
|
204
208
|
}
|
|
205
209
|
|
|
206
210
|
export function addDownloadReleaseAssetsTool(_gh: GhClient, bus: ToolBus) {
|
|
207
|
-
bus.register(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
Type.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
Type.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
211
|
+
bus.register(
|
|
212
|
+
defineStructuredTool({
|
|
213
|
+
name: "download-github-release-assets",
|
|
214
|
+
label: "GitHub Release Download",
|
|
215
|
+
description:
|
|
216
|
+
"Download a GitHub release's assets (or its source archive) into " +
|
|
217
|
+
"~/.cache/pi/github/releases/<owner>/<repo>/<tag>/ using the gh CLI's credentials, " +
|
|
218
|
+
"so private repositories and large binaries work where a plain HTTP fetch cannot. " +
|
|
219
|
+
"Files already in that directory are kept, never re-fetched. The result is the JSON " +
|
|
220
|
+
"summary {repo, tag, dir, files:[{name, path, bytes}]} listing everything now in the " +
|
|
221
|
+
"directory; file contents are not echoed. Read the entries you need from `path`.",
|
|
222
|
+
promptSnippet: "Download GitHub release assets",
|
|
223
|
+
parameters: Type.Object({
|
|
224
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
|
|
225
|
+
tag: Type.Optional(
|
|
226
|
+
Type.String({ description: "Release tag (defaults to the latest release)" }),
|
|
227
|
+
),
|
|
228
|
+
pattern: Type.Optional(
|
|
229
|
+
Type.String({
|
|
230
|
+
description:
|
|
231
|
+
'Comma-separated glob patterns for asset names, e.g. "*.tar.gz,*.deb" (default: every asset)',
|
|
232
|
+
}),
|
|
233
|
+
),
|
|
234
|
+
archive: Type.Optional(
|
|
235
|
+
Type.Union([Type.Literal("zip"), Type.Literal("tar.gz")], {
|
|
236
|
+
description: "Download the release's source archive instead of its assets",
|
|
237
|
+
}),
|
|
238
|
+
),
|
|
239
|
+
}),
|
|
240
|
+
structuredSchema: releaseDownloadSchema,
|
|
241
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
242
|
+
return downloadReleaseAssets({ params, ctx, signal });
|
|
243
|
+
},
|
|
234
244
|
}),
|
|
235
|
-
|
|
236
|
-
return downloadReleaseAssets({ params, ctx, signal });
|
|
237
|
-
},
|
|
238
|
-
});
|
|
245
|
+
);
|
|
239
246
|
}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
4
|
import {
|
|
5
5
|
type GhClient,
|
|
6
6
|
resolveRepo,
|
|
7
7
|
splitRepo,
|
|
8
|
+
type StructuredResultOf,
|
|
8
9
|
subtitlePendant,
|
|
9
10
|
type ToolCall,
|
|
10
|
-
type ToolResult,
|
|
11
11
|
toPositiveId,
|
|
12
|
-
|
|
12
|
+
toStructuredJsonResult,
|
|
13
13
|
} from "../base.js";
|
|
14
|
+
import { workflowJobsSchema } from "../schemas.js";
|
|
14
15
|
|
|
15
16
|
interface RunIdParams {
|
|
16
17
|
run_id: number | string;
|
|
@@ -18,31 +19,41 @@ interface RunIdParams {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/** The toolcall handler behind `get-github-workflow-jobs`: every job of a run, all pages. */
|
|
21
|
-
async function workflowJobs(
|
|
22
|
+
async function workflowJobs(
|
|
23
|
+
gh: GhClient,
|
|
24
|
+
call: ToolCall<RunIdParams>,
|
|
25
|
+
): Promise<StructuredResultOf<typeof workflowJobsSchema>> {
|
|
22
26
|
const { params, ctx, signal } = call;
|
|
23
27
|
const runId = toPositiveId(params.run_id, "run_id");
|
|
24
28
|
const effectiveRepo = await resolveRepo(params.repo, signal, ctx.cwd, params);
|
|
25
29
|
const { owner, repo: name } = splitRepo(effectiveRepo);
|
|
26
30
|
|
|
27
31
|
const jobs = await gh.checks.runJobs(owner, name, runId, signal);
|
|
28
|
-
const result =
|
|
32
|
+
const result = toStructuredJsonResult(
|
|
33
|
+
JSON.stringify({ total_count: jobs.length, jobs }),
|
|
34
|
+
params,
|
|
35
|
+
workflowJobsSchema,
|
|
36
|
+
);
|
|
29
37
|
result.details.pendant = subtitlePendant(params, "run_id");
|
|
30
38
|
return result;
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export function addGetWorkflowJobsTool(gh: GhClient, bus: ToolBus) {
|
|
34
|
-
bus.register(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
bus.register(
|
|
43
|
+
defineStructuredTool({
|
|
44
|
+
name: "get-github-workflow-jobs",
|
|
45
|
+
label: "GitHub Workflow Jobs",
|
|
46
|
+
description:
|
|
47
|
+
"Get every job of a workflow run as JSON {total_count, jobs:[{id, run_id, run_url, name, status, conclusion, html_url, steps:[{name, number, status, conclusion, started_at}]}]}. Paginated server-side, so runs with more than 30 jobs return all of them. Use the `id` with read-github-ci-logs after read-github-pr-status / wait-github-commit-checks did not already give you a job id.",
|
|
48
|
+
promptSnippet: "Get GitHub workflow run jobs",
|
|
49
|
+
parameters: Type.Object({
|
|
50
|
+
run_id: Type.Union([Type.Number(), Type.String()], { description: "Workflow run ID" }),
|
|
51
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
|
|
52
|
+
}),
|
|
53
|
+
structuredSchema: workflowJobsSchema,
|
|
54
|
+
async execute(_id, params, signal, onUpdate, ctx) {
|
|
55
|
+
return workflowJobs(gh, { params, ctx, signal, onUpdate });
|
|
56
|
+
},
|
|
43
57
|
}),
|
|
44
|
-
|
|
45
|
-
return workflowJobs(gh, { params, ctx, signal, onUpdate });
|
|
46
|
-
},
|
|
47
|
-
});
|
|
58
|
+
);
|
|
48
59
|
}
|
|
@@ -11,17 +11,21 @@ import { Type } from "typebox";
|
|
|
11
11
|
|
|
12
12
|
import type { RunJob } from "../../lib/github.js";
|
|
13
13
|
import { createSeqState } from "../../lib/seq-state.js";
|
|
14
|
-
import type
|
|
14
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
15
15
|
import {
|
|
16
16
|
type GhClient,
|
|
17
17
|
ghExec,
|
|
18
18
|
resolveRepo,
|
|
19
19
|
splitRepo,
|
|
20
|
+
structuredFailure,
|
|
21
|
+
type StructuredFailureResult,
|
|
22
|
+
type StructuredResultOf,
|
|
20
23
|
subtitlePendant,
|
|
21
24
|
type ToolCall,
|
|
22
|
-
type ToolResult,
|
|
23
25
|
toPositiveId,
|
|
26
|
+
withStructuredResult,
|
|
24
27
|
} from "../base.js";
|
|
28
|
+
import { ciLogsSchema } from "../schemas.js";
|
|
25
29
|
|
|
26
30
|
// 模块级串行状态:同一资源(如 CI 日志)的请求排队执行,配合函数内部的
|
|
27
31
|
// 缓存检查避免重复网络请求。闭包状态不与其他扩展共享,key 无需全局前缀。
|
|
@@ -373,8 +377,11 @@ interface JobIdParams {
|
|
|
373
377
|
repo?: string;
|
|
374
378
|
}
|
|
375
379
|
|
|
380
|
+
/** 「没找到 / 还没日志」是结构化失败,不是异常:脚本能按 ok === false 分辨。 */
|
|
381
|
+
type CiLogsResult = StructuredResultOf<typeof ciLogsSchema> | StructuredFailureResult;
|
|
382
|
+
|
|
376
383
|
/** The toolcall handler behind `read-github-ci-logs`. */
|
|
377
|
-
async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<
|
|
384
|
+
async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<CiLogsResult> {
|
|
378
385
|
const { params, ctx, signal, onUpdate } = call;
|
|
379
386
|
const { job_id, repo } = params;
|
|
380
387
|
const jobId = toPositiveId(job_id, "job_id");
|
|
@@ -383,9 +390,10 @@ async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<ToolRe
|
|
|
383
390
|
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
384
391
|
const { owner, repo: name } = splitRepo(effectiveRepo);
|
|
385
392
|
|
|
386
|
-
const failure = (text: string):
|
|
393
|
+
const failure = (text: string): StructuredFailureResult => ({
|
|
387
394
|
content: [{ type: "text", text }],
|
|
388
395
|
details: { input: params, ...(pendant && { pendant }) },
|
|
396
|
+
structuredResult: structuredFailure(text),
|
|
389
397
|
});
|
|
390
398
|
|
|
391
399
|
let target: RunJob;
|
|
@@ -415,28 +423,34 @@ async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<ToolRe
|
|
|
415
423
|
const rawLog = await getJobLog(target, signal, ctx.cwd, params);
|
|
416
424
|
const index = jobLogIndex(target, rawLog);
|
|
417
425
|
|
|
418
|
-
return
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
426
|
+
return withStructuredResult(
|
|
427
|
+
{
|
|
428
|
+
content: [{ type: "text", text: JSON.stringify(index, null, 2) }],
|
|
429
|
+
details: { ...index, input: params, ...(pendant && { pendant }) },
|
|
430
|
+
},
|
|
431
|
+
index,
|
|
432
|
+
);
|
|
422
433
|
}
|
|
423
434
|
|
|
424
435
|
export function addReadCiLogsTool(gh: GhClient, bus: ToolBus) {
|
|
425
|
-
bus.register(
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
436
|
+
bus.register(
|
|
437
|
+
defineStructuredTool({
|
|
438
|
+
name: "read-github-ci-logs",
|
|
439
|
+
label: "GitHub CI Logs",
|
|
440
|
+
description:
|
|
441
|
+
"Download one GitHub Actions job's CI log by job ID and index its steps. Returns JSON {name, id, status, conclusion, log_file, steps:[{number, name, conclusion, start_line?, end_line?}]}: `log_file` is the job's complete raw log on disk (runner timestamps and ANSI kept, exactly as GitHub delivers it) and each step carries the 1-based inclusive line range of its block inside that file. Read the content out of the file yourself (read/grep with offset/limit) — it is not echoed back. Get the job IDs from get-github-workflow-jobs, then call this once per job you need." +
|
|
442
|
+
" Note: queued jobs have no logs yet; use watch-github-run to wait for completion.",
|
|
443
|
+
promptSnippet: "Read GitHub CI logs",
|
|
444
|
+
parameters: Type.Object({
|
|
445
|
+
job_id: Type.Union([Type.Number(), Type.String()], {
|
|
446
|
+
description: "Job ID, from get-github-workflow-jobs.",
|
|
447
|
+
}),
|
|
448
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
|
|
435
449
|
}),
|
|
436
|
-
|
|
450
|
+
structuredSchema: ciLogsSchema,
|
|
451
|
+
async execute(_id, params, signal, onUpdate, ctx) {
|
|
452
|
+
return ciLogs(gh, { params, ctx, signal, onUpdate });
|
|
453
|
+
},
|
|
437
454
|
}),
|
|
438
|
-
|
|
439
|
-
return ciLogs(gh, { params, ctx, signal, onUpdate });
|
|
440
|
-
},
|
|
441
|
-
});
|
|
455
|
+
);
|
|
442
456
|
}
|
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
|
-
import type
|
|
4
|
-
import {
|
|
3
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
|
+
import {
|
|
5
|
+
type GhClient,
|
|
6
|
+
ghExec,
|
|
7
|
+
repoArgs,
|
|
8
|
+
subtitlePendant,
|
|
9
|
+
toStructuredJsonResult,
|
|
10
|
+
} from "../base.js";
|
|
11
|
+
import { issueCommentsSchema } from "../schemas.js";
|
|
5
12
|
|
|
6
13
|
export function addReadIssueCommentsTool(_gh: GhClient, bus: ToolBus) {
|
|
7
|
-
bus.register(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
bus.register(
|
|
15
|
+
defineStructuredTool({
|
|
16
|
+
name: "read-github-issue-comments",
|
|
17
|
+
label: "GitHub Issue Comments",
|
|
18
|
+
description: "Get comments on a GitHub issue.",
|
|
19
|
+
promptSnippet: "Read GitHub issue comments",
|
|
20
|
+
parameters: Type.Object({
|
|
21
|
+
number: Type.Union([Type.Number(), Type.String()], { description: "Issue number" }),
|
|
22
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
|
|
23
|
+
}),
|
|
24
|
+
structuredSchema: issueCommentsSchema,
|
|
25
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
26
|
+
const { number, repo } = params;
|
|
27
|
+
const out = await ghExec(
|
|
28
|
+
["issue", "view", String(number), ...repoArgs(repo), "--json", "comments"],
|
|
29
|
+
{ cwd: ctx.cwd, signal, input: params },
|
|
30
|
+
);
|
|
31
|
+
const result = toStructuredJsonResult(out, params, issueCommentsSchema);
|
|
32
|
+
result.details.pendant = subtitlePendant(params, "number");
|
|
33
|
+
return result;
|
|
34
|
+
},
|
|
15
35
|
}),
|
|
16
|
-
|
|
17
|
-
const { number, repo } = params;
|
|
18
|
-
const result = toToolResultJson(
|
|
19
|
-
await ghExec(["issue", "view", String(number), ...repoArgs(repo), "--json", "comments"], {
|
|
20
|
-
cwd: ctx.cwd,
|
|
21
|
-
signal,
|
|
22
|
-
input: params,
|
|
23
|
-
}),
|
|
24
|
-
params,
|
|
25
|
-
);
|
|
26
|
-
result.details.pendant = subtitlePendant(params, "number");
|
|
27
|
-
return result;
|
|
28
|
-
},
|
|
29
|
-
});
|
|
36
|
+
);
|
|
30
37
|
}
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
|
-
import type
|
|
4
|
-
import {
|
|
3
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
|
+
import {
|
|
5
|
+
type GhClient,
|
|
6
|
+
ghExec,
|
|
7
|
+
repoArgs,
|
|
8
|
+
subtitlePendant,
|
|
9
|
+
toStructuredJsonResult,
|
|
10
|
+
} from "../base.js";
|
|
11
|
+
import { issueViewSchema } from "../schemas.js";
|
|
5
12
|
|
|
6
13
|
export function addReadIssueTool(_gh: GhClient, bus: ToolBus) {
|
|
7
|
-
bus.register(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
bus.register(
|
|
15
|
+
defineStructuredTool({
|
|
16
|
+
name: "read-github-issue",
|
|
17
|
+
label: "GitHub Issue",
|
|
18
|
+
description: "Get details of a GitHub issue by number.",
|
|
19
|
+
promptSnippet: "Read a GitHub issue",
|
|
20
|
+
parameters: Type.Object({
|
|
21
|
+
number: Type.Union([Type.Number(), Type.String()], { description: "Issue number" }),
|
|
22
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
|
|
23
|
+
}),
|
|
24
|
+
structuredSchema: issueViewSchema,
|
|
25
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
26
|
+
const { number, repo } = params;
|
|
27
|
+
const out = await ghExec(
|
|
20
28
|
[
|
|
21
29
|
"issue",
|
|
22
30
|
"view",
|
|
@@ -26,11 +34,11 @@ export function addReadIssueTool(_gh: GhClient, bus: ToolBus) {
|
|
|
26
34
|
"title,state,body,author,createdAt,updatedAt,closedAt,url,labels,assignees,comments,milestone,number",
|
|
27
35
|
],
|
|
28
36
|
{ cwd: ctx.cwd, signal, input: params },
|
|
29
|
-
)
|
|
30
|
-
params,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
|
|
37
|
+
);
|
|
38
|
+
const result = toStructuredJsonResult(out, params, issueViewSchema);
|
|
39
|
+
result.details.pendant = subtitlePendant(params, "number");
|
|
40
|
+
return result;
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
36
44
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
4
|
import {
|
|
5
5
|
ghApiList,
|
|
6
6
|
type GhClient,
|
|
@@ -8,66 +8,70 @@ import {
|
|
|
8
8
|
repoArgs,
|
|
9
9
|
resolveRepo,
|
|
10
10
|
subtitlePendant,
|
|
11
|
-
|
|
11
|
+
toStructuredJsonResult,
|
|
12
12
|
} from "../base.js";
|
|
13
|
+
import { prCommentsSchema } from "../schemas.js";
|
|
13
14
|
|
|
14
15
|
export function addReadPrCommentsTool(_gh: GhClient, bus: ToolBus) {
|
|
15
|
-
bus.register(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Type.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
),
|
|
30
|
-
}),
|
|
31
|
-
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
32
|
-
const { number, repo, reviews } = params;
|
|
33
|
-
let out: string;
|
|
34
|
-
if (reviews) {
|
|
35
|
-
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
36
|
-
|
|
37
|
-
const [reviewComments, reviewSummaries] = await Promise.all([
|
|
38
|
-
ghApiList(`/repos/${effectiveRepo}/pulls/${String(number)}/comments`, {
|
|
39
|
-
cwd: ctx.cwd,
|
|
40
|
-
signal,
|
|
41
|
-
input: params,
|
|
42
|
-
}),
|
|
43
|
-
ghApiList(`/repos/${effectiveRepo}/pulls/${String(number)}/reviews`, {
|
|
44
|
-
cwd: ctx.cwd,
|
|
45
|
-
signal,
|
|
46
|
-
input: params,
|
|
16
|
+
bus.register(
|
|
17
|
+
defineStructuredTool({
|
|
18
|
+
name: "read-github-pr-comments",
|
|
19
|
+
label: "GitHub PR Comments",
|
|
20
|
+
description:
|
|
21
|
+
"Get review comments on a GitHub pull request. Set reviews=true for inline code review comments with diff_hunk.",
|
|
22
|
+
promptSnippet: "Read GitHub PR comments",
|
|
23
|
+
parameters: Type.Object({
|
|
24
|
+
number: Type.Union([Type.Number(), Type.String()], { description: "PR number" }),
|
|
25
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
|
|
26
|
+
reviews: Type.Optional(
|
|
27
|
+
Type.Boolean({
|
|
28
|
+
description:
|
|
29
|
+
"If true, returns inline code review comments (with diff_hunk, path, line) via API. Default: false (returns issue comments).",
|
|
47
30
|
}),
|
|
48
|
-
|
|
31
|
+
),
|
|
32
|
+
}),
|
|
33
|
+
structuredSchema: prCommentsSchema,
|
|
34
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
35
|
+
const { number, repo, reviews } = params;
|
|
36
|
+
let out: string;
|
|
37
|
+
if (reviews) {
|
|
38
|
+
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
40
|
+
const [reviewComments, reviewSummaries] = await Promise.all([
|
|
41
|
+
ghApiList(`/repos/${effectiveRepo}/pulls/${String(number)}/comments`, {
|
|
42
|
+
cwd: ctx.cwd,
|
|
43
|
+
signal,
|
|
44
|
+
input: params,
|
|
45
|
+
}),
|
|
46
|
+
ghApiList(`/repos/${effectiveRepo}/pulls/${String(number)}/reviews`, {
|
|
47
|
+
cwd: ctx.cwd,
|
|
48
|
+
signal,
|
|
49
|
+
input: params,
|
|
50
|
+
}),
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
out = JSON.stringify(
|
|
54
|
+
{
|
|
55
|
+
reviews: reviewSummaries,
|
|
56
|
+
comments: reviewComments,
|
|
57
|
+
},
|
|
58
|
+
null,
|
|
59
|
+
2,
|
|
60
|
+
);
|
|
61
|
+
} else {
|
|
62
|
+
out = await ghExec(
|
|
63
|
+
["pr", "view", String(number), ...repoArgs(repo), "--json", "comments"],
|
|
64
|
+
{
|
|
65
|
+
cwd: ctx.cwd,
|
|
66
|
+
signal,
|
|
67
|
+
input: params,
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const result = toStructuredJsonResult(out, params, prCommentsSchema);
|
|
72
|
+
result.details.pendant = subtitlePendant(params, "number");
|
|
73
|
+
return result;
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
73
77
|
}
|