@trim21/personal-pi-extensions 0.1.684 → 0.1.687
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 +10 -8
- package/package.json +1 -1
- package/src/codemode/declarations.ts +3 -3
- package/src/codemode/tool.ts +13 -26
- package/src/gh/base.ts +125 -60
- package/src/gh/render.ts +259 -0
- package/src/gh/schemas.ts +233 -47
- package/src/gh/tools/download-release-assets.ts +58 -80
- package/src/gh/tools/list-issues.ts +66 -47
- package/src/gh/tools/list-prs.ts +66 -47
- package/src/gh/tools/list-releases.ts +43 -25
- package/src/gh/tools/list-workflow-runs.ts +41 -35
- package/src/gh/tools/read-ci-logs.ts +6 -19
- package/src/gh/tools/read-issue-comments.ts +10 -7
- package/src/gh/tools/read-issue.ts +10 -14
- package/src/gh/tools/read-pr-comments.ts +19 -38
- package/src/gh/tools/read-pr-diff.ts +34 -22
- package/src/gh/tools/read-pr.ts +7 -15
- package/src/gh/tools/read-release.ts +37 -25
- package/src/gh/tools/read-repo.ts +35 -24
- package/src/gh/tools/wait-pr-checks.ts +6 -7
- package/src/gh/tools/watch-run.ts +64 -38
- package/src/lib/github-reads.ts +405 -0
- package/src/lib/github.ts +95 -3
- package/src/lib/lsp/inspect-schemas.ts +44 -0
- package/src/lib/lsp/inspect-tool.ts +188 -137
|
@@ -1,64 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `list-github-issues`:两条取数分支(关键词搜索走 octokit,浏览走 `gh issue list --json`)
|
|
3
|
+
* 归一成同一套 `SearchHit`,因此文本渲染与结构化载荷只有一份实现。
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import { Type } from "typebox";
|
|
2
7
|
|
|
3
|
-
import type
|
|
8
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
9
|
import {
|
|
10
|
+
browseList,
|
|
5
11
|
type GhClient,
|
|
6
12
|
type ListFilters,
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
renderHitList,
|
|
14
|
+
type StructuredResultOf,
|
|
9
15
|
subtitlePendant,
|
|
10
16
|
type ToolCall,
|
|
11
|
-
type ToolResult,
|
|
12
17
|
toToolResult,
|
|
18
|
+
withStructuredResult,
|
|
13
19
|
} from "../base.js";
|
|
20
|
+
import { ghHitListPayloadSchema } from "../schemas.js";
|
|
14
21
|
|
|
15
|
-
async function listIssues(
|
|
22
|
+
async function listIssues(
|
|
23
|
+
gh: GhClient,
|
|
24
|
+
call: ToolCall<ListFilters>,
|
|
25
|
+
): Promise<StructuredResultOf<typeof ghHitListPayloadSchema>> {
|
|
16
26
|
const { params, ctx, signal } = call;
|
|
17
|
-
const
|
|
18
|
-
params.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
);
|
|
27
|
+
const { repo, hits } = params.keywords
|
|
28
|
+
? { repo: params.repo, hits: await gh.search.search("issue", params) }
|
|
29
|
+
: await browseList(gh, "issue", params, { cwd: ctx.cwd, signal, input: params });
|
|
30
|
+
const text = renderHitList("issue", hits, { repo, fields: params.fields });
|
|
31
|
+
const result = toToolResult(text, params);
|
|
23
32
|
result.details.pendant = subtitlePendant(params);
|
|
24
|
-
|
|
33
|
+
// 文本可能被行数/字节预算截断,载荷给全部命中
|
|
34
|
+
return withStructuredResult(result, { text, items: hits });
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
export function addListIssuesTool(gh: GhClient, bus: ToolBus) {
|
|
28
|
-
bus.register(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Type.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
38
|
+
bus.register(
|
|
39
|
+
defineStructuredTool({
|
|
40
|
+
name: "list-github-issues",
|
|
41
|
+
label: "GitHub Issues List",
|
|
42
|
+
description:
|
|
43
|
+
'List GitHub issues with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword search defaults to open issues — pass state="all" to include closed ones. Set fields to choose the columns of each result row.',
|
|
44
|
+
promptSnippet: "List or search GitHub issues",
|
|
45
|
+
parameters: Type.Object({
|
|
46
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
|
|
47
|
+
keywords: Type.Optional(Type.String({ description: "Search keywords (free text)" })),
|
|
48
|
+
state: Type.Optional(
|
|
49
|
+
Type.String({
|
|
50
|
+
description:
|
|
51
|
+
"open, closed, all (default: open; all applies to keyword search and covers closed too)",
|
|
52
|
+
}),
|
|
53
|
+
),
|
|
54
|
+
label: Type.Optional(Type.String({ description: "Filter by label" })),
|
|
55
|
+
// `@me` 只在关键词搜索分支可用:搜索 API 自己把它解析成当前登录用户。浏览分支走
|
|
56
|
+
// REST 列表端点,`@me` 按字面量转发,不会匹配到当前用户(本仓库不做展开)。
|
|
57
|
+
author: Type.Optional(
|
|
58
|
+
Type.String({
|
|
59
|
+
description: "Filter by author ('@me' works with keywords; browse takes it literally)",
|
|
60
|
+
}),
|
|
61
|
+
),
|
|
62
|
+
assignee: Type.Optional(
|
|
63
|
+
Type.String({
|
|
64
|
+
description:
|
|
65
|
+
"Filter by assignee ('@me' works with keywords; browse takes it literally)",
|
|
66
|
+
}),
|
|
67
|
+
),
|
|
68
|
+
milestone: Type.Optional(Type.String({ description: "Filter by milestone" })),
|
|
69
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 30, max 100)" })),
|
|
70
|
+
fields: Type.Optional(
|
|
71
|
+
Type.String({
|
|
72
|
+
description:
|
|
73
|
+
"Comma-separated columns for the rows (default: number,state,title,labels,updatedAt; adds repo when no repo given). Valid: number,state,title,url,author,labels,milestone,assignees,comments,repo,createdAt,updatedAt,closedAt",
|
|
74
|
+
}),
|
|
75
|
+
),
|
|
76
|
+
}),
|
|
77
|
+
structuredSchema: ghHitListPayloadSchema,
|
|
78
|
+
async execute(_id, params, signal, onUpdate, ctx) {
|
|
79
|
+
return listIssues(gh, { params, ctx, signal, onUpdate });
|
|
80
|
+
},
|
|
59
81
|
}),
|
|
60
|
-
|
|
61
|
-
return listIssues(gh, { params, ctx, signal, onUpdate });
|
|
62
|
-
},
|
|
63
|
-
});
|
|
82
|
+
);
|
|
64
83
|
}
|
package/src/gh/tools/list-prs.ts
CHANGED
|
@@ -1,64 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `list-github-prs`:与 `list-github-issues` 同一套两条分支(关键词搜索走 octokit,
|
|
3
|
+
* 浏览走 `gh pr list --json`),归一成同一套 `SearchHit`。
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import { Type } from "typebox";
|
|
2
7
|
|
|
3
|
-
import type
|
|
8
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
9
|
import {
|
|
10
|
+
browseList,
|
|
5
11
|
type GhClient,
|
|
6
12
|
type ListFilters,
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
renderHitList,
|
|
14
|
+
type StructuredResultOf,
|
|
9
15
|
subtitlePendant,
|
|
10
16
|
type ToolCall,
|
|
11
|
-
type ToolResult,
|
|
12
17
|
toToolResult,
|
|
18
|
+
withStructuredResult,
|
|
13
19
|
} from "../base.js";
|
|
20
|
+
import { ghHitListPayloadSchema } from "../schemas.js";
|
|
14
21
|
|
|
15
|
-
async function listPrs(
|
|
22
|
+
async function listPrs(
|
|
23
|
+
gh: GhClient,
|
|
24
|
+
call: ToolCall<ListFilters>,
|
|
25
|
+
): Promise<StructuredResultOf<typeof ghHitListPayloadSchema>> {
|
|
16
26
|
const { params, ctx, signal } = call;
|
|
17
|
-
const
|
|
18
|
-
params.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
);
|
|
27
|
+
const { repo, hits } = params.keywords
|
|
28
|
+
? { repo: params.repo, hits: await gh.search.search("pr", params) }
|
|
29
|
+
: await browseList(gh, "pr", params, { cwd: ctx.cwd, signal, input: params });
|
|
30
|
+
const text = renderHitList("pr", hits, { repo, fields: params.fields });
|
|
31
|
+
const result = toToolResult(text, params);
|
|
23
32
|
result.details.pendant = subtitlePendant(params);
|
|
24
|
-
|
|
33
|
+
// 文本可能被行数/字节预算截断,载荷给全部命中
|
|
34
|
+
return withStructuredResult(result, { text, items: hits });
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
export function addListPrsTool(gh: GhClient, bus: ToolBus) {
|
|
28
|
-
bus.register(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Type.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
38
|
+
bus.register(
|
|
39
|
+
defineStructuredTool({
|
|
40
|
+
name: "list-github-prs",
|
|
41
|
+
label: "GitHub PRs List",
|
|
42
|
+
description:
|
|
43
|
+
'List GitHub pull requests with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword search defaults to open PRs — pass state="merged", state="closed" (merged excluded) or state="all" to broaden. Set fields to choose the columns of each result row.',
|
|
44
|
+
promptSnippet: "List or search GitHub PRs",
|
|
45
|
+
parameters: Type.Object({
|
|
46
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
|
|
47
|
+
keywords: Type.Optional(Type.String({ description: "Search keywords (free text)" })),
|
|
48
|
+
state: Type.Optional(
|
|
49
|
+
Type.String({
|
|
50
|
+
description:
|
|
51
|
+
"open, closed, merged, all (default: open; all applies to keyword search and covers open + closed + merged)",
|
|
52
|
+
}),
|
|
53
|
+
),
|
|
54
|
+
label: Type.Optional(Type.String({ description: "Filter by label" })),
|
|
55
|
+
// `@me` 只在关键词搜索分支可用:搜索 API 自己把它解析成当前登录用户。浏览分支走
|
|
56
|
+
// REST 列表端点,`@me` 按字面量转发,不会匹配到当前用户(本仓库不做展开)。
|
|
57
|
+
author: Type.Optional(
|
|
58
|
+
Type.String({
|
|
59
|
+
description: "Filter by author ('@me' works with keywords; browse takes it literally)",
|
|
60
|
+
}),
|
|
61
|
+
),
|
|
62
|
+
assignee: Type.Optional(
|
|
63
|
+
Type.String({
|
|
64
|
+
description:
|
|
65
|
+
"Filter by assignee ('@me' works with keywords; browse takes it literally)",
|
|
66
|
+
}),
|
|
67
|
+
),
|
|
68
|
+
milestone: Type.Optional(Type.String({ description: "Filter by milestone" })),
|
|
69
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 30, max 100)" })),
|
|
70
|
+
fields: Type.Optional(
|
|
71
|
+
Type.String({
|
|
72
|
+
description:
|
|
73
|
+
"Comma-separated columns for the rows (default: number,state,title,labels,updatedAt; adds repo when no repo given). Valid: number,state,title,url,author,labels,milestone,assignees,comments,repo,createdAt,updatedAt,closedAt,mergedAt",
|
|
74
|
+
}),
|
|
75
|
+
),
|
|
76
|
+
}),
|
|
77
|
+
structuredSchema: ghHitListPayloadSchema,
|
|
78
|
+
async execute(_id, params, signal, onUpdate, ctx) {
|
|
79
|
+
return listPrs(gh, { params, ctx, signal, onUpdate });
|
|
80
|
+
},
|
|
59
81
|
}),
|
|
60
|
-
|
|
61
|
-
return listPrs(gh, { params, ctx, signal, onUpdate });
|
|
62
|
-
},
|
|
63
|
-
});
|
|
82
|
+
);
|
|
64
83
|
}
|
|
@@ -1,30 +1,48 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import { parseWithSchema } from "../../lib/parse-with-schema.js";
|
|
4
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
5
|
+
import {
|
|
6
|
+
type GhClient,
|
|
7
|
+
resolveRepo,
|
|
8
|
+
splitRepo,
|
|
9
|
+
subtitlePendant,
|
|
10
|
+
toToolResult,
|
|
11
|
+
withStructuredResult,
|
|
12
|
+
} from "../base.js";
|
|
13
|
+
import { renderReleaseList } from "../render.js";
|
|
14
|
+
import { ghReleaseListPayloadSchema, ghReleaseSummarySchema } from "../schemas.js";
|
|
5
15
|
|
|
6
|
-
export function addListReleasesTool(
|
|
7
|
-
bus.register(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
export function addListReleasesTool(gh: GhClient, bus: ToolBus) {
|
|
17
|
+
bus.register(
|
|
18
|
+
defineStructuredTool({
|
|
19
|
+
name: "list-github-releases",
|
|
20
|
+
label: "GitHub Releases List",
|
|
21
|
+
description:
|
|
22
|
+
"List GitHub releases. Each row is: tag, flags (latest/prerelease/draft), published date, name.",
|
|
23
|
+
promptSnippet: "List GitHub releases",
|
|
24
|
+
parameters: Type.Object({
|
|
25
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
|
|
26
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 10)" })),
|
|
27
|
+
}),
|
|
28
|
+
structuredSchema: ghReleaseListPayloadSchema,
|
|
29
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
30
|
+
const { repo, limit } = params;
|
|
31
|
+
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
32
|
+
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
33
|
+
const items = await gh.reads.listReleases(owner, repoName, limit, signal);
|
|
34
|
+
const releases = parseWithSchema(Type.Array(ghReleaseSummarySchema), items);
|
|
35
|
+
// REST 没有 isLatest:按列表顺序取第一个非 draft / prerelease 的条目
|
|
36
|
+
const latestIndex = releases.findIndex(
|
|
37
|
+
(release) => release.draft !== true && release.prerelease !== true,
|
|
38
|
+
);
|
|
39
|
+
const text = renderReleaseList(
|
|
40
|
+
releases.map((release, index) => ({ ...release, latest: index === latestIndex })),
|
|
41
|
+
);
|
|
42
|
+
const result = toToolResult(text, params);
|
|
43
|
+
result.details.pendant = subtitlePendant(params);
|
|
44
|
+
return withStructuredResult(result, { text, releases });
|
|
45
|
+
},
|
|
15
46
|
}),
|
|
16
|
-
|
|
17
|
-
const { repo, limit } = params;
|
|
18
|
-
const args = ["release", "list", ...repoArgs(repo)];
|
|
19
|
-
if (limit) {
|
|
20
|
-
args.push("--limit", String(limit));
|
|
21
|
-
}
|
|
22
|
-
const result = toToolResult(
|
|
23
|
-
await ghExec(args, { cwd: ctx.cwd, signal, input: params }),
|
|
24
|
-
params,
|
|
25
|
-
);
|
|
26
|
-
result.details.pendant = subtitlePendant(params);
|
|
27
|
-
return result;
|
|
28
|
-
},
|
|
29
|
-
});
|
|
47
|
+
);
|
|
30
48
|
}
|
|
@@ -1,40 +1,46 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import { parseWithSchema } from "../../lib/parse-with-schema.js";
|
|
4
|
+
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
5
|
+
import {
|
|
6
|
+
type GhClient,
|
|
7
|
+
resolveRepo,
|
|
8
|
+
splitRepo,
|
|
9
|
+
subtitlePendant,
|
|
10
|
+
toToolResult,
|
|
11
|
+
withStructuredResult,
|
|
12
|
+
} from "../base.js";
|
|
13
|
+
import { renderRunList } from "../render.js";
|
|
14
|
+
import { ghRunListPayloadSchema, ghRunSummarySchema } from "../schemas.js";
|
|
5
15
|
|
|
6
|
-
export function addListWorkflowRunsTool(
|
|
7
|
-
bus.register(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Type.
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
export function addListWorkflowRunsTool(gh: GhClient, bus: ToolBus) {
|
|
17
|
+
bus.register(
|
|
18
|
+
defineStructuredTool({
|
|
19
|
+
name: "list-github-workflow-runs",
|
|
20
|
+
label: "GitHub Workflow Runs",
|
|
21
|
+
description:
|
|
22
|
+
"List GitHub Actions workflow runs. Each row is: id, status, conclusion, workflow, branch, event, created date, url.",
|
|
23
|
+
promptSnippet: "List GitHub workflow runs",
|
|
24
|
+
parameters: Type.Object({
|
|
25
|
+
repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
|
|
26
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 20)" })),
|
|
27
|
+
status: Type.Optional(
|
|
28
|
+
Type.String({ description: "Filter by status: success, failure, cancelled, etc." }),
|
|
29
|
+
),
|
|
30
|
+
workflow: Type.Optional(Type.String({ description: "Filter by workflow name or file" })),
|
|
31
|
+
}),
|
|
32
|
+
structuredSchema: ghRunListPayloadSchema,
|
|
33
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
34
|
+
const { repo, limit, status, workflow } = params;
|
|
35
|
+
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
36
|
+
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
37
|
+
const items = await gh.reads.listRuns(owner, repoName, { workflow, status, limit }, signal);
|
|
38
|
+
const runs = parseWithSchema(Type.Array(ghRunSummarySchema), items);
|
|
39
|
+
const text = renderRunList(runs);
|
|
40
|
+
const result = toToolResult(text, params);
|
|
41
|
+
result.details.pendant = subtitlePendant(params);
|
|
42
|
+
return withStructuredResult(result, { text, runs });
|
|
43
|
+
},
|
|
19
44
|
}),
|
|
20
|
-
|
|
21
|
-
const { repo, limit, status, workflow } = params;
|
|
22
|
-
const args = ["run", "list", ...repoArgs(repo)];
|
|
23
|
-
if (limit) {
|
|
24
|
-
args.push("--limit", String(limit));
|
|
25
|
-
}
|
|
26
|
-
if (status) {
|
|
27
|
-
args.push("--status", status);
|
|
28
|
-
}
|
|
29
|
-
if (workflow) {
|
|
30
|
-
args.push("--workflow", workflow);
|
|
31
|
-
}
|
|
32
|
-
const result = toToolResult(
|
|
33
|
-
await ghExec(args, { cwd: ctx.cwd, signal, input: params }),
|
|
34
|
-
params,
|
|
35
|
-
);
|
|
36
|
-
result.details.pendant = subtitlePendant(params);
|
|
37
|
-
return result;
|
|
38
|
-
},
|
|
39
|
-
});
|
|
45
|
+
);
|
|
40
46
|
}
|
|
@@ -14,7 +14,6 @@ import { createSeqState } from "../../lib/seq-state.js";
|
|
|
14
14
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
15
15
|
import {
|
|
16
16
|
type GhClient,
|
|
17
|
-
ghExec,
|
|
18
17
|
resolveRepo,
|
|
19
18
|
splitRepo,
|
|
20
19
|
structuredFailure,
|
|
@@ -55,10 +54,9 @@ export function jobLogPath(repo: string, runId: string, jobId: number): string {
|
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
async function getJobLog(
|
|
57
|
+
gh: GhClient,
|
|
58
58
|
job: RunJob,
|
|
59
59
|
signal: AbortSignal | undefined,
|
|
60
|
-
cwd: string | undefined,
|
|
61
|
-
input?: unknown,
|
|
62
60
|
): Promise<string> {
|
|
63
61
|
// The log download only accepts a job id, and the cache is keyed on the
|
|
64
62
|
// canonical repo/run from the job itself, not on the caller's `repo` string.
|
|
@@ -76,21 +74,10 @@ async function getJobLog(
|
|
|
76
74
|
// Not cached, fetch from GitHub
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// --allow-escape-sequences to output it anyway". The raw bytes are kept
|
|
84
|
-
// as-is (the file is the log exactly as GitHub delivers it); the tool never
|
|
85
|
-
// echoes them, and the TUI strips ANSI when rendering tool results.
|
|
86
|
-
const log = await ghExec(
|
|
87
|
-
["api", "--allow-escape-sequences", `/repos/${repo}/actions/jobs/${job.id}/logs`],
|
|
88
|
-
{
|
|
89
|
-
cwd,
|
|
90
|
-
signal,
|
|
91
|
-
input,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
77
|
+
// 日志原样落盘(GitHub 返回什么就是什么,含 ANSI 转义与 runner 时间戳);工具从不回显
|
|
78
|
+
// 它,模型自己读文件,TUI 渲染工具结果时会自己剥 ANSI。
|
|
79
|
+
const { owner, repo: name } = splitRepo(repo);
|
|
80
|
+
const log = await gh.reads.jobLogs(owner, name, job.id, signal);
|
|
94
81
|
|
|
95
82
|
// Write to cache
|
|
96
83
|
await mkdir(cacheDir, { recursive: true });
|
|
@@ -420,7 +407,7 @@ async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<CiLogs
|
|
|
420
407
|
details: {},
|
|
421
408
|
});
|
|
422
409
|
|
|
423
|
-
const rawLog = await getJobLog(target, signal
|
|
410
|
+
const rawLog = await getJobLog(gh, target, signal);
|
|
424
411
|
const index = jobLogIndex(target, rawLog);
|
|
425
412
|
|
|
426
413
|
return withStructuredResult(
|
|
@@ -3,14 +3,14 @@ import { Type } from "typebox";
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
4
|
import {
|
|
5
5
|
type GhClient,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
resolveRepo,
|
|
7
|
+
splitRepo,
|
|
8
8
|
subtitlePendant,
|
|
9
9
|
toStructuredJsonResult,
|
|
10
10
|
} from "../base.js";
|
|
11
11
|
import { issueCommentsSchema } from "../schemas.js";
|
|
12
12
|
|
|
13
|
-
export function addReadIssueCommentsTool(
|
|
13
|
+
export function addReadIssueCommentsTool(gh: GhClient, bus: ToolBus) {
|
|
14
14
|
bus.register(
|
|
15
15
|
defineStructuredTool({
|
|
16
16
|
name: "read-github-issue-comments",
|
|
@@ -24,11 +24,14 @@ export function addReadIssueCommentsTool(_gh: GhClient, bus: ToolBus) {
|
|
|
24
24
|
structuredSchema: issueCommentsSchema,
|
|
25
25
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
26
26
|
const { number, repo } = params;
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
28
|
+
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
29
|
+
const comments = await gh.reads.issueComments(owner, repoName, Number(number), signal);
|
|
30
|
+
const result = toStructuredJsonResult(
|
|
31
|
+
JSON.stringify({ comments }, null, 2),
|
|
32
|
+
params,
|
|
33
|
+
issueCommentsSchema,
|
|
30
34
|
);
|
|
31
|
-
const result = toStructuredJsonResult(out, params, issueCommentsSchema);
|
|
32
35
|
result.details.pendant = subtitlePendant(params, "number");
|
|
33
36
|
return result;
|
|
34
37
|
},
|
|
@@ -3,14 +3,14 @@ import { Type } from "typebox";
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
4
|
import {
|
|
5
5
|
type GhClient,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
resolveRepo,
|
|
7
|
+
splitRepo,
|
|
8
8
|
subtitlePendant,
|
|
9
9
|
toStructuredJsonResult,
|
|
10
10
|
} from "../base.js";
|
|
11
11
|
import { issueViewSchema } from "../schemas.js";
|
|
12
12
|
|
|
13
|
-
export function addReadIssueTool(
|
|
13
|
+
export function addReadIssueTool(gh: GhClient, bus: ToolBus) {
|
|
14
14
|
bus.register(
|
|
15
15
|
defineStructuredTool({
|
|
16
16
|
name: "read-github-issue",
|
|
@@ -24,18 +24,14 @@ export function addReadIssueTool(_gh: GhClient, bus: ToolBus) {
|
|
|
24
24
|
structuredSchema: issueViewSchema,
|
|
25
25
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
26
26
|
const { number, repo } = params;
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"title,state,body,author,createdAt,updatedAt,closedAt,url,labels,assignees,comments,milestone,number",
|
|
35
|
-
],
|
|
36
|
-
{ cwd: ctx.cwd, signal, input: params },
|
|
27
|
+
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
28
|
+
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
29
|
+
const data = await gh.reads.issue(owner, repoName, Number(number), signal);
|
|
30
|
+
const result = toStructuredJsonResult(
|
|
31
|
+
JSON.stringify(data, null, 2),
|
|
32
|
+
params,
|
|
33
|
+
issueViewSchema,
|
|
37
34
|
);
|
|
38
|
-
const result = toStructuredJsonResult(out, params, issueViewSchema);
|
|
39
35
|
result.details.pendant = subtitlePendant(params, "number");
|
|
40
36
|
return result;
|
|
41
37
|
},
|
|
@@ -2,17 +2,15 @@ import { Type } from "typebox";
|
|
|
2
2
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
4
|
import {
|
|
5
|
-
ghApiList,
|
|
6
5
|
type GhClient,
|
|
7
|
-
ghExec,
|
|
8
|
-
repoArgs,
|
|
9
6
|
resolveRepo,
|
|
7
|
+
splitRepo,
|
|
10
8
|
subtitlePendant,
|
|
11
9
|
toStructuredJsonResult,
|
|
12
10
|
} from "../base.js";
|
|
13
11
|
import { prCommentsSchema } from "../schemas.js";
|
|
14
12
|
|
|
15
|
-
export function addReadPrCommentsTool(
|
|
13
|
+
export function addReadPrCommentsTool(gh: GhClient, bus: ToolBus) {
|
|
16
14
|
bus.register(
|
|
17
15
|
defineStructuredTool({
|
|
18
16
|
name: "read-github-pr-comments",
|
|
@@ -33,42 +31,25 @@ export function addReadPrCommentsTool(_gh: GhClient, bus: ToolBus) {
|
|
|
33
31
|
structuredSchema: prCommentsSchema,
|
|
34
32
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
35
33
|
const { number, repo, reviews } = params;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
35
|
+
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
36
|
+
const pullNumber = Number(number);
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
signal,
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
input: params,
|
|
50
|
-
}),
|
|
51
|
-
]);
|
|
38
|
+
// reviews=true 给行内评审评论 + 评审摘要;缺省给 PR 上的普通(issue)评论
|
|
39
|
+
const payload = reviews
|
|
40
|
+
? {
|
|
41
|
+
reviews: await gh.reads.pullReviews(owner, repoName, pullNumber, signal),
|
|
42
|
+
comments: await gh.reads.pullReviewComments(owner, repoName, pullNumber, signal),
|
|
43
|
+
}
|
|
44
|
+
: {
|
|
45
|
+
comments: await gh.reads.issueComments(owner, repoName, pullNumber, signal),
|
|
46
|
+
};
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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);
|
|
48
|
+
const result = toStructuredJsonResult(
|
|
49
|
+
JSON.stringify(payload, null, 2),
|
|
50
|
+
params,
|
|
51
|
+
prCommentsSchema,
|
|
52
|
+
);
|
|
72
53
|
result.details.pendant = subtitlePendant(params, "number");
|
|
73
54
|
return result;
|
|
74
55
|
},
|