@trim21/personal-pi-extensions 0.1.690 → 0.1.691
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 +3 -3
- package/package.json +1 -1
- package/src/bwrap/dcg-scan.ts +2 -8
- package/src/bwrap/html.ts +13 -0
- package/src/bwrap/runtime.ts +1 -8
- package/src/claude-code/shell.ts +12 -42
- package/src/codemode/declarations.ts +3 -8
- package/src/gh/base.ts +91 -23
- package/src/gh/index.ts +8 -5
- package/src/gh/tools/download-release-assets.ts +6 -3
- package/src/gh/tools/get-workflow-jobs.ts +4 -7
- package/src/gh/tools/list-issues.ts +6 -39
- package/src/gh/tools/list-prs.ts +6 -39
- package/src/gh/tools/list-releases.ts +6 -9
- package/src/gh/tools/list-workflow-runs.ts +6 -9
- package/src/gh/tools/read-ci-logs.ts +8 -19
- package/src/gh/tools/read-issue-comments.ts +4 -12
- package/src/gh/tools/read-issue.ts +4 -12
- package/src/gh/tools/read-pr-comments.ts +6 -14
- package/src/gh/tools/read-pr-diff.ts +8 -8
- package/src/gh/tools/read-pr-status.ts +6 -4
- package/src/gh/tools/read-pr.ts +4 -12
- package/src/gh/tools/read-release.ts +5 -8
- package/src/gh/tools/read-repo.ts +5 -9
- package/src/gh/tools/wait-commit-checks.ts +2 -4
- package/src/gh/tools/wait-pr-checks.ts +2 -4
- package/src/gh/tools/watch-run.ts +7 -4
- package/src/lib/abort.ts +10 -0
- package/src/lib/bash-tool.ts +48 -0
- package/src/lib/github-reads.ts +3 -23
- package/src/lib/github.ts +81 -41
- package/src/lib/model-call.ts +3 -7
- package/src/lib/tools-config.ts +2 -4
- package/src/opencode/bash.ts +11 -42
- package/src/opencode/glob.ts +1 -6
- package/src/opencode/grep.ts +1 -6
- package/src/opencode/ripgrep.ts +5 -0
- package/src/web/fetch.ts +2 -6
- package/src/web/search.ts +2 -6
- package/src/orphan-worker-cleaner.ts +0 -38
|
@@ -4,10 +4,9 @@ import { parseWithSchema } from "../../lib/parse-with-schema.js";
|
|
|
4
4
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
5
5
|
import {
|
|
6
6
|
type GhClient,
|
|
7
|
-
|
|
8
|
-
splitRepo,
|
|
9
|
-
subtitlePendant,
|
|
7
|
+
resolveRepoTarget,
|
|
10
8
|
toToolResult,
|
|
9
|
+
withPendant,
|
|
11
10
|
withStructuredResult,
|
|
12
11
|
} from "../base.js";
|
|
13
12
|
import { renderReleaseList } from "../render.js";
|
|
@@ -27,10 +26,9 @@ export function addListReleasesTool(gh: GhClient, bus: ToolBus) {
|
|
|
27
26
|
}),
|
|
28
27
|
structuredSchema: ghReleaseListPayloadSchema,
|
|
29
28
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
30
|
-
const {
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const items = await gh.reads.listReleases(owner, repoName, limit, signal);
|
|
29
|
+
const { limit } = params;
|
|
30
|
+
const { owner, name } = await resolveRepoTarget(params.repo, signal, ctx.cwd, params);
|
|
31
|
+
const items = await gh.reads.listReleases(owner, name, limit, signal);
|
|
34
32
|
const releases = parseWithSchema(Type.Array(ghReleaseSummarySchema), items);
|
|
35
33
|
// REST 没有 isLatest:按列表顺序取第一个非 draft / prerelease 的条目
|
|
36
34
|
const latestIndex = releases.findIndex(
|
|
@@ -40,8 +38,7 @@ export function addListReleasesTool(gh: GhClient, bus: ToolBus) {
|
|
|
40
38
|
releases.map((release, index) => ({ ...release, latest: index === latestIndex })),
|
|
41
39
|
);
|
|
42
40
|
const result = toToolResult(text, params);
|
|
43
|
-
result
|
|
44
|
-
return withStructuredResult(result, { text, releases });
|
|
41
|
+
return withStructuredResult(withPendant(result, params), { text, releases });
|
|
45
42
|
},
|
|
46
43
|
}),
|
|
47
44
|
);
|
|
@@ -4,10 +4,9 @@ import { parseWithSchema } from "../../lib/parse-with-schema.js";
|
|
|
4
4
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
5
5
|
import {
|
|
6
6
|
type GhClient,
|
|
7
|
-
|
|
8
|
-
splitRepo,
|
|
9
|
-
subtitlePendant,
|
|
7
|
+
resolveRepoTarget,
|
|
10
8
|
toToolResult,
|
|
9
|
+
withPendant,
|
|
11
10
|
withStructuredResult,
|
|
12
11
|
} from "../base.js";
|
|
13
12
|
import { renderRunList } from "../render.js";
|
|
@@ -31,15 +30,13 @@ export function addListWorkflowRunsTool(gh: GhClient, bus: ToolBus) {
|
|
|
31
30
|
}),
|
|
32
31
|
structuredSchema: ghRunListPayloadSchema,
|
|
33
32
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
34
|
-
const {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const items = await gh.reads.listRuns(owner, repoName, { workflow, status, limit }, signal);
|
|
33
|
+
const { limit, status, workflow } = params;
|
|
34
|
+
const { owner, name } = await resolveRepoTarget(params.repo, signal, ctx.cwd, params);
|
|
35
|
+
const items = await gh.reads.listRuns(owner, name, { workflow, status, limit }, signal);
|
|
38
36
|
const runs = parseWithSchema(Type.Array(ghRunSummarySchema), items);
|
|
39
37
|
const text = renderRunList(runs);
|
|
40
38
|
const result = toToolResult(text, params);
|
|
41
|
-
result
|
|
42
|
-
return withStructuredResult(result, { text, runs });
|
|
39
|
+
return withStructuredResult(withPendant(result, params), { text, runs });
|
|
43
40
|
},
|
|
44
41
|
}),
|
|
45
42
|
);
|
|
@@ -9,12 +9,12 @@ import { dirname, join } from "node:path";
|
|
|
9
9
|
|
|
10
10
|
import { Type } from "typebox";
|
|
11
11
|
|
|
12
|
-
import type
|
|
12
|
+
import { isNotFound, type RunJob } from "../../lib/github.js";
|
|
13
13
|
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
|
-
|
|
17
|
+
resolveRepoTarget,
|
|
18
18
|
splitRepo,
|
|
19
19
|
structuredFailure,
|
|
20
20
|
type StructuredFailureResult,
|
|
@@ -299,19 +299,6 @@ export function stepLineSpans(log: string, apiSteps: readonly StepRef[]): Map<nu
|
|
|
299
299
|
return spans;
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
/** Text of `stepNumber` in its raw log, or null when the step never ran. */
|
|
303
|
-
export function extractStepFromLog(
|
|
304
|
-
log: string,
|
|
305
|
-
stepNumber: number,
|
|
306
|
-
apiSteps: readonly { number: number; name: string }[],
|
|
307
|
-
): string | null {
|
|
308
|
-
const span = stepLineSpans(log, apiSteps).get(stepNumber);
|
|
309
|
-
if (span === undefined) {
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
return log.split("\n").slice(span.start, span.end).join("\n").trimEnd();
|
|
313
|
-
}
|
|
314
|
-
|
|
315
302
|
/** One step of a job, indexed into the job's raw log file. */
|
|
316
303
|
export interface CiLogsStepIndex {
|
|
317
304
|
number: number;
|
|
@@ -374,8 +361,11 @@ async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<CiLogs
|
|
|
374
361
|
const jobId = toPositiveId(job_id, "job_id");
|
|
375
362
|
|
|
376
363
|
const pendant = subtitlePendant(params, "job_id");
|
|
377
|
-
const
|
|
378
|
-
|
|
364
|
+
const {
|
|
365
|
+
fullName: effectiveRepo,
|
|
366
|
+
owner,
|
|
367
|
+
name,
|
|
368
|
+
} = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
379
369
|
|
|
380
370
|
const failure = (text: string): StructuredFailureResult => ({
|
|
381
371
|
content: [{ type: "text", text }],
|
|
@@ -387,8 +377,7 @@ async function ciLogs(gh: GhClient, call: ToolCall<JobIdParams>): Promise<CiLogs
|
|
|
387
377
|
try {
|
|
388
378
|
target = await gh.checks.job(owner, name, jobId, signal);
|
|
389
379
|
} catch (error) {
|
|
390
|
-
|
|
391
|
-
if (status !== 404) {
|
|
380
|
+
if (!isNotFound(error)) {
|
|
392
381
|
throw error;
|
|
393
382
|
}
|
|
394
383
|
return failure(
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
|
-
import {
|
|
5
|
-
type GhClient,
|
|
6
|
-
resolveRepo,
|
|
7
|
-
splitRepo,
|
|
8
|
-
subtitlePendant,
|
|
9
|
-
toStructuredJsonResult,
|
|
10
|
-
} from "../base.js";
|
|
4
|
+
import { type GhClient, resolveRepoTarget, toStructuredJsonResult, withPendant } from "../base.js";
|
|
11
5
|
import { issueCommentsSchema } from "../schemas.js";
|
|
12
6
|
|
|
13
7
|
export function addReadIssueCommentsTool(gh: GhClient, bus: ToolBus) {
|
|
@@ -24,16 +18,14 @@ export function addReadIssueCommentsTool(gh: GhClient, bus: ToolBus) {
|
|
|
24
18
|
structuredSchema: issueCommentsSchema,
|
|
25
19
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
26
20
|
const { number, repo } = params;
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const comments = await gh.reads.issueComments(owner, repoName, Number(number), signal);
|
|
21
|
+
const { owner, name } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
22
|
+
const comments = await gh.reads.issueComments(owner, name, Number(number), signal);
|
|
30
23
|
const result = toStructuredJsonResult(
|
|
31
24
|
JSON.stringify({ comments }, null, 2),
|
|
32
25
|
params,
|
|
33
26
|
issueCommentsSchema,
|
|
34
27
|
);
|
|
35
|
-
result
|
|
36
|
-
return result;
|
|
28
|
+
return withPendant(result, params, "number");
|
|
37
29
|
},
|
|
38
30
|
}),
|
|
39
31
|
);
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
|
-
import {
|
|
5
|
-
type GhClient,
|
|
6
|
-
resolveRepo,
|
|
7
|
-
splitRepo,
|
|
8
|
-
subtitlePendant,
|
|
9
|
-
toStructuredJsonResult,
|
|
10
|
-
} from "../base.js";
|
|
4
|
+
import { type GhClient, resolveRepoTarget, toStructuredJsonResult, withPendant } from "../base.js";
|
|
11
5
|
import { issueViewSchema } from "../schemas.js";
|
|
12
6
|
|
|
13
7
|
export function addReadIssueTool(gh: GhClient, bus: ToolBus) {
|
|
@@ -24,16 +18,14 @@ export function addReadIssueTool(gh: GhClient, bus: ToolBus) {
|
|
|
24
18
|
structuredSchema: issueViewSchema,
|
|
25
19
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
26
20
|
const { number, repo } = params;
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const data = await gh.reads.issue(owner, repoName, Number(number), signal);
|
|
21
|
+
const { owner, name } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
22
|
+
const data = await gh.reads.issue(owner, name, Number(number), signal);
|
|
30
23
|
const result = toStructuredJsonResult(
|
|
31
24
|
JSON.stringify(data, null, 2),
|
|
32
25
|
params,
|
|
33
26
|
issueViewSchema,
|
|
34
27
|
);
|
|
35
|
-
result
|
|
36
|
-
return result;
|
|
28
|
+
return withPendant(result, params, "number");
|
|
37
29
|
},
|
|
38
30
|
}),
|
|
39
31
|
);
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
|
-
import {
|
|
5
|
-
type GhClient,
|
|
6
|
-
resolveRepo,
|
|
7
|
-
splitRepo,
|
|
8
|
-
subtitlePendant,
|
|
9
|
-
toStructuredJsonResult,
|
|
10
|
-
} from "../base.js";
|
|
4
|
+
import { type GhClient, resolveRepoTarget, toStructuredJsonResult, withPendant } from "../base.js";
|
|
11
5
|
import { prCommentsSchema } from "../schemas.js";
|
|
12
6
|
|
|
13
7
|
export function addReadPrCommentsTool(gh: GhClient, bus: ToolBus) {
|
|
@@ -31,18 +25,17 @@ export function addReadPrCommentsTool(gh: GhClient, bus: ToolBus) {
|
|
|
31
25
|
structuredSchema: prCommentsSchema,
|
|
32
26
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
33
27
|
const { number, repo, reviews } = params;
|
|
34
|
-
const
|
|
35
|
-
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
28
|
+
const { owner, name } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
36
29
|
const pullNumber = Number(number);
|
|
37
30
|
|
|
38
31
|
// reviews=true 给行内评审评论 + 评审摘要;缺省给 PR 上的普通(issue)评论
|
|
39
32
|
const payload = reviews
|
|
40
33
|
? {
|
|
41
|
-
reviews: await gh.reads.pullReviews(owner,
|
|
42
|
-
comments: await gh.reads.pullReviewComments(owner,
|
|
34
|
+
reviews: await gh.reads.pullReviews(owner, name, pullNumber, signal),
|
|
35
|
+
comments: await gh.reads.pullReviewComments(owner, name, pullNumber, signal),
|
|
43
36
|
}
|
|
44
37
|
: {
|
|
45
|
-
comments: await gh.reads.issueComments(owner,
|
|
38
|
+
comments: await gh.reads.issueComments(owner, name, pullNumber, signal),
|
|
46
39
|
};
|
|
47
40
|
|
|
48
41
|
const result = toStructuredJsonResult(
|
|
@@ -50,8 +43,7 @@ export function addReadPrCommentsTool(gh: GhClient, bus: ToolBus) {
|
|
|
50
43
|
params,
|
|
51
44
|
prCommentsSchema,
|
|
52
45
|
);
|
|
53
|
-
result
|
|
54
|
-
return result;
|
|
46
|
+
return withPendant(result, params, "number");
|
|
55
47
|
},
|
|
56
48
|
}),
|
|
57
49
|
);
|
|
@@ -3,10 +3,9 @@ import { Type } from "typebox";
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
4
|
import {
|
|
5
5
|
type GhClient,
|
|
6
|
-
|
|
7
|
-
splitRepo,
|
|
8
|
-
subtitlePendant,
|
|
6
|
+
resolveRepoTarget,
|
|
9
7
|
toToolResult,
|
|
8
|
+
withPendant,
|
|
10
9
|
withStructuredResult,
|
|
11
10
|
} from "../base.js";
|
|
12
11
|
import { parseDiffStats } from "../render.js";
|
|
@@ -26,13 +25,14 @@ export function addReadPrDiffTool(gh: GhClient, bus: ToolBus) {
|
|
|
26
25
|
structuredSchema: ghDiffPayloadSchema,
|
|
27
26
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
28
27
|
const { number, repo } = params;
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const diff = await gh.reads.pullDiff(owner, repoName, Number(number), signal);
|
|
28
|
+
const { owner, name } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
29
|
+
const diff = await gh.reads.pullDiff(owner, name, Number(number), signal);
|
|
32
30
|
const result = toToolResult(diff, params);
|
|
33
|
-
result.details.pendant = subtitlePendant(params, "number");
|
|
34
31
|
// 文本是原始 diff(没有 JSON 形式),载荷从同一份 diff 解析出变更统计
|
|
35
|
-
return withStructuredResult(result,
|
|
32
|
+
return withStructuredResult(withPendant(result, params, "number"), {
|
|
33
|
+
text: diff,
|
|
34
|
+
...parseDiffStats(diff),
|
|
35
|
+
});
|
|
36
36
|
},
|
|
37
37
|
}),
|
|
38
38
|
);
|
|
@@ -4,8 +4,7 @@ import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
|
4
4
|
import {
|
|
5
5
|
type GhClient,
|
|
6
6
|
mergeChecks,
|
|
7
|
-
|
|
8
|
-
splitRepo,
|
|
7
|
+
resolveRepoTarget,
|
|
9
8
|
type StructuredResultOf,
|
|
10
9
|
subtitlePendant,
|
|
11
10
|
type ToolCall,
|
|
@@ -32,8 +31,11 @@ export async function prStatus(
|
|
|
32
31
|
const { number, repo } = params;
|
|
33
32
|
const pullNumber = toPositiveId(number, "number");
|
|
34
33
|
const pendant = subtitlePendant(params, "number");
|
|
35
|
-
const
|
|
36
|
-
|
|
34
|
+
const {
|
|
35
|
+
fullName: effectiveRepo,
|
|
36
|
+
owner,
|
|
37
|
+
name,
|
|
38
|
+
} = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
37
39
|
|
|
38
40
|
const pollSignal = signal ?? new AbortController().signal;
|
|
39
41
|
const headSha = await gh.checks.pullHead(owner, name, pullNumber, pollSignal);
|
package/src/gh/tools/read-pr.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
|
-
import {
|
|
5
|
-
type GhClient,
|
|
6
|
-
resolveRepo,
|
|
7
|
-
splitRepo,
|
|
8
|
-
subtitlePendant,
|
|
9
|
-
toStructuredJsonResult,
|
|
10
|
-
} from "../base.js";
|
|
4
|
+
import { type GhClient, resolveRepoTarget, toStructuredJsonResult, withPendant } from "../base.js";
|
|
11
5
|
import { prViewSchema } from "../schemas.js";
|
|
12
6
|
|
|
13
7
|
export function addReadPrTool(gh: GhClient, bus: ToolBus) {
|
|
@@ -24,12 +18,10 @@ export function addReadPrTool(gh: GhClient, bus: ToolBus) {
|
|
|
24
18
|
structuredSchema: prViewSchema,
|
|
25
19
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
26
20
|
const { number, repo } = params;
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const data = await gh.reads.pull(owner, repoName, Number(number), signal);
|
|
21
|
+
const { owner, name } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
22
|
+
const data = await gh.reads.pull(owner, name, Number(number), signal);
|
|
30
23
|
const result = toStructuredJsonResult(JSON.stringify(data, null, 2), params, prViewSchema);
|
|
31
|
-
result
|
|
32
|
-
return result;
|
|
24
|
+
return withPendant(result, params, "number");
|
|
33
25
|
},
|
|
34
26
|
}),
|
|
35
27
|
);
|
|
@@ -4,10 +4,9 @@ import { parseWithSchema } from "../../lib/parse-with-schema.js";
|
|
|
4
4
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
5
5
|
import {
|
|
6
6
|
type GhClient,
|
|
7
|
-
|
|
8
|
-
splitRepo,
|
|
9
|
-
subtitlePendant,
|
|
7
|
+
resolveRepoTarget,
|
|
10
8
|
toToolResult,
|
|
9
|
+
withPendant,
|
|
11
10
|
withStructuredResult,
|
|
12
11
|
} from "../base.js";
|
|
13
12
|
import { renderReleaseView } from "../render.js";
|
|
@@ -28,14 +27,12 @@ export function addReadReleaseTool(gh: GhClient, bus: ToolBus) {
|
|
|
28
27
|
structuredSchema: ghReleaseViewPayloadSchema,
|
|
29
28
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
30
29
|
const { tag, repo } = params;
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const data = await gh.reads.release(owner, repoName, tag, signal);
|
|
30
|
+
const { owner, name } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
31
|
+
const data = await gh.reads.release(owner, name, tag, signal);
|
|
34
32
|
const view = parseWithSchema(ghReleaseViewSchema, data);
|
|
35
33
|
const text = renderReleaseView(view);
|
|
36
34
|
const result = toToolResult(text, params);
|
|
37
|
-
result
|
|
38
|
-
return withStructuredResult(result, { text, release: view });
|
|
35
|
+
return withStructuredResult(withPendant(result, params, "tag"), { text, release: view });
|
|
39
36
|
},
|
|
40
37
|
}),
|
|
41
38
|
);
|
|
@@ -4,10 +4,9 @@ import { parseWithSchema } from "../../lib/parse-with-schema.js";
|
|
|
4
4
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
5
5
|
import {
|
|
6
6
|
type GhClient,
|
|
7
|
-
|
|
8
|
-
splitRepo,
|
|
9
|
-
subtitlePendant,
|
|
7
|
+
resolveRepoTarget,
|
|
10
8
|
toToolResult,
|
|
9
|
+
withPendant,
|
|
11
10
|
withStructuredResult,
|
|
12
11
|
} from "../base.js";
|
|
13
12
|
import { renderRepoView } from "../render.js";
|
|
@@ -25,15 +24,12 @@ export function addReadRepoTool(gh: GhClient, bus: ToolBus) {
|
|
|
25
24
|
}),
|
|
26
25
|
structuredSchema: ghRepoPayloadSchema,
|
|
27
26
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
28
|
-
const {
|
|
29
|
-
const
|
|
30
|
-
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
31
|
-
const data = await gh.reads.repository(owner, repoName, signal);
|
|
27
|
+
const { owner, name } = await resolveRepoTarget(params.repo, signal, ctx.cwd, params);
|
|
28
|
+
const data = await gh.reads.repository(owner, name, signal);
|
|
32
29
|
const view = parseWithSchema(ghRepoViewSchema, data);
|
|
33
30
|
const text = renderRepoView(view);
|
|
34
31
|
const result = toToolResult(text, params);
|
|
35
|
-
result
|
|
36
|
-
return withStructuredResult(result, { text, repo: view });
|
|
32
|
+
return withStructuredResult(withPendant(result, params), { text, repo: view });
|
|
37
33
|
},
|
|
38
34
|
}),
|
|
39
35
|
);
|
|
@@ -3,8 +3,7 @@ import { Type } from "typebox";
|
|
|
3
3
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
4
4
|
import {
|
|
5
5
|
type GhClient,
|
|
6
|
-
|
|
7
|
-
splitRepo,
|
|
6
|
+
resolveRepoTarget,
|
|
8
7
|
type StructuredResultOf,
|
|
9
8
|
subtitlePendant,
|
|
10
9
|
type ToolCall,
|
|
@@ -33,8 +32,7 @@ async function waitCommitChecks(
|
|
|
33
32
|
details: {},
|
|
34
33
|
});
|
|
35
34
|
|
|
36
|
-
const
|
|
37
|
-
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
35
|
+
const { owner, name: repoName } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
38
36
|
|
|
39
37
|
const pollSignal = signal ?? new AbortController().signal;
|
|
40
38
|
const sha = await gh.checks.headSha(owner, repoName, String(commit), pollSignal);
|
|
@@ -4,8 +4,7 @@ import { Value } from "typebox/value";
|
|
|
4
4
|
import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
5
5
|
import {
|
|
6
6
|
type GhClient,
|
|
7
|
-
|
|
8
|
-
splitRepo,
|
|
7
|
+
resolveRepoTarget,
|
|
9
8
|
type StructuredResultOf,
|
|
10
9
|
subtitlePendant,
|
|
11
10
|
type ToolCall,
|
|
@@ -36,8 +35,7 @@ async function waitPrChecks(
|
|
|
36
35
|
details: {},
|
|
37
36
|
});
|
|
38
37
|
|
|
39
|
-
const
|
|
40
|
-
const { owner, repo: repoName } = splitRepo(effectiveRepo);
|
|
38
|
+
const { owner, name: repoName } = await resolveRepoTarget(repo, signal, ctx.cwd, params);
|
|
41
39
|
|
|
42
40
|
const pull = Value.Parse(
|
|
43
41
|
pullHeadSchema,
|
|
@@ -4,8 +4,7 @@ import { defineStructuredTool, type ToolBus } from "../../lib/tool-bus.js";
|
|
|
4
4
|
import {
|
|
5
5
|
type GhClient,
|
|
6
6
|
renderRunStatus,
|
|
7
|
-
|
|
8
|
-
splitRepo,
|
|
7
|
+
resolveRepoTarget,
|
|
9
8
|
subtitlePendant,
|
|
10
9
|
type ToolResult,
|
|
11
10
|
watchRun,
|
|
@@ -38,8 +37,12 @@ export function addWatchRunTool(gh: GhClient, bus: ToolBus) {
|
|
|
38
37
|
|
|
39
38
|
// 轮询循环要求非空 signal(与两个 wait 工具一致:没有就自建一个)
|
|
40
39
|
const pollSignal = signal ?? new AbortController().signal;
|
|
41
|
-
const
|
|
42
|
-
|
|
40
|
+
const { owner, name: repoName } = await resolveRepoTarget(
|
|
41
|
+
repo,
|
|
42
|
+
pollSignal,
|
|
43
|
+
ctx.cwd,
|
|
44
|
+
params,
|
|
45
|
+
);
|
|
43
46
|
const outcome = await watchRun({
|
|
44
47
|
owner,
|
|
45
48
|
repo: repoName,
|
package/src/lib/abort.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 把调用方的取消信号与一个本地超时合并成单个 AbortSignal。
|
|
3
|
+
*
|
|
4
|
+
* 调用方可能不传 signal(例如 agent 空闲时的 `ctx.signal`),那时仍然要有超时兜底,
|
|
5
|
+
* 所以本地 `AbortSignal.timeout` 始终生效,两者都触发时以先到者为准。
|
|
6
|
+
*/
|
|
7
|
+
export function withTimeoutSignal(signal: AbortSignal | undefined, ms: number): AbortSignal {
|
|
8
|
+
const timeout = AbortSignal.timeout(ms);
|
|
9
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
10
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-code 与 opencode 两套 Bash 工具共用的部分:超时上下限、结构化结果 schema、
|
|
3
|
+
* 以及截断后从落盘文件读回完整输出。
|
|
4
|
+
*
|
|
5
|
+
* 只放两边逐字相同的部分。两套工具的截断提示文案、失败判定与 details 是刻意的差异,
|
|
6
|
+
* 留在各自的工具里,不在这里统一。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
|
|
11
|
+
import type { TruncationResult } from "@earendil-works/pi-coding-agent";
|
|
12
|
+
import { Type } from "typebox";
|
|
13
|
+
|
|
14
|
+
export const BASH_DEFAULT_TIMEOUT_MS = 120_000;
|
|
15
|
+
export const BASH_MAX_TIMEOUT_MS = 7_200_000;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `Bash` 的结构化结果:只有退出码与完整输出——命令非零退出不是失败,所以载荷里没有成败
|
|
19
|
+
* 标志,脚本直接读 `exitCode` 分支;超时/中止时 `exitCode` 为 `null`(原因在模型侧文本里)。
|
|
20
|
+
*/
|
|
21
|
+
export const bashStructuredSchema = Type.Object({
|
|
22
|
+
exitCode: Type.Union([Type.Number(), Type.Null()], {
|
|
23
|
+
description: "Exit code of the command; null when it was killed (timeout or abort)",
|
|
24
|
+
}),
|
|
25
|
+
output: Type.String({
|
|
26
|
+
description:
|
|
27
|
+
"Complete output of the command (stdout and stderr merged). Never truncated, never mixed with tool-added notices.",
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 载荷里的输出:文本被截断时读回落盘的完整输出(读不到就退回那份截断文本),
|
|
33
|
+
* 因此脚本拿到的永远是命令真正输出的内容。
|
|
34
|
+
*/
|
|
35
|
+
export async function spilledOutput(
|
|
36
|
+
output: string,
|
|
37
|
+
truncation: TruncationResult,
|
|
38
|
+
spillPath: string | undefined,
|
|
39
|
+
): Promise<string> {
|
|
40
|
+
if (spillPath === undefined || !truncation.truncated) {
|
|
41
|
+
return output;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
return await readFile(spillPath, "utf8");
|
|
45
|
+
} catch {
|
|
46
|
+
return output;
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/lib/github-reads.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { pipeline } from "node:stream/promises";
|
|
|
15
15
|
|
|
16
16
|
import type { Octokit } from "octokit";
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { type GithubApi } from "./github.js";
|
|
19
19
|
|
|
20
20
|
/** `actions.listWorkflowRuns*` 的 `status` 取值(octokit 是字面量联合,工具参数是 string)。 */
|
|
21
21
|
type WorkflowRunParams = NonNullable<
|
|
@@ -146,23 +146,6 @@ function limitOf(limit: number | undefined, fallback: number): number {
|
|
|
146
146
|
return Math.min(Math.max(limit ?? fallback, 1), 100);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
/** 响应体不是 2xx 时抛错(状态、URL 与 GitHub 的 message 都在里面)。 */
|
|
150
|
-
async function ensureOk(response: Response): Promise<Response> {
|
|
151
|
-
if (response.ok) {
|
|
152
|
-
return response;
|
|
153
|
-
}
|
|
154
|
-
let detail: string | undefined;
|
|
155
|
-
try {
|
|
156
|
-
const body = await response.text();
|
|
157
|
-
detail = (JSON.parse(body) as { message?: string }).message ?? body.slice(0, 200);
|
|
158
|
-
} catch {
|
|
159
|
-
detail = undefined;
|
|
160
|
-
}
|
|
161
|
-
throw new Error(
|
|
162
|
-
`GitHub API error (HTTP ${response.status}) at ${response.url}${detail === undefined || detail === "" ? "" : `: ${detail}`}`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
149
|
function octokitRequest(signal: AbortSignal | undefined): { signal?: AbortSignal } {
|
|
167
150
|
return signal === undefined ? {} : { signal };
|
|
168
151
|
}
|
|
@@ -172,9 +155,7 @@ function asJson(value: unknown): unknown {
|
|
|
172
155
|
return value;
|
|
173
156
|
}
|
|
174
157
|
|
|
175
|
-
export function createGithubReads(
|
|
176
|
-
const api = createGithubApi(options);
|
|
177
|
-
|
|
158
|
+
export function createGithubReads(api: GithubApi): GithubReads {
|
|
178
159
|
return {
|
|
179
160
|
async issue(owner, repo, number, signal) {
|
|
180
161
|
const { data } = await api.call((octokit) =>
|
|
@@ -373,11 +354,11 @@ export function createGithubReads(options: GithubClientOptions = {}): GithubRead
|
|
|
373
354
|
},
|
|
374
355
|
|
|
375
356
|
async downloadAssetTo(owner, repo, assetId, destPath, signal) {
|
|
357
|
+
// rawFetch 非 2xx 直接抛 GithubApiError;这里只需流式落盘
|
|
376
358
|
const response = await api.rawFetch(
|
|
377
359
|
`https://api.github.com/repos/${owner}/${repo}/releases/assets/${assetId}`,
|
|
378
360
|
{ headers: { accept: "application/octet-stream" }, ...(signal && { signal }) },
|
|
379
361
|
);
|
|
380
|
-
await ensureOk(response);
|
|
381
362
|
await streamToFile(response, destPath);
|
|
382
363
|
},
|
|
383
364
|
|
|
@@ -388,7 +369,6 @@ export function createGithubReads(options: GithubClientOptions = {}): GithubRead
|
|
|
388
369
|
`https://api.github.com/repos/${owner}/${repo}/${kind}${suffix}`,
|
|
389
370
|
{ ...(signal && { signal }) },
|
|
390
371
|
);
|
|
391
|
-
await ensureOk(response);
|
|
392
372
|
await streamToFile(response, destPath);
|
|
393
373
|
},
|
|
394
374
|
};
|