pi-onedev-toolkit 0.1.1 → 0.2.0
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/CHANGELOG.md +24 -1
- package/README.md +22 -7
- package/package.json +9 -5
- package/skills/onedev-issue-to-pr/SKILL.md +9 -4
- package/skills/onedev-pr-review/SKILL.md +12 -6
- package/src/config-storage.ts +105 -0
- package/src/context.ts +204 -38
- package/src/extension.ts +43 -7
- package/src/mutation-approvals.ts +130 -0
- package/src/source-watch.ts +521 -0
- package/src/tod.ts +33 -3
- package/src/tools/build.ts +143 -21
- package/src/tools/common.ts +27 -12
- package/src/tools/index.ts +91 -63
- package/src/tools/issue.ts +50 -26
- package/src/tools/pull.ts +116 -67
- package/src/tools/watch.ts +70 -0
package/src/tools/issue.ts
CHANGED
|
@@ -2,8 +2,8 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
3
3
|
import { Type, type Static } from "typebox";
|
|
4
4
|
import {
|
|
5
|
-
confirmGate,
|
|
6
5
|
FIELDS_DESCRIPTION,
|
|
6
|
+
confirmMutation,
|
|
7
7
|
pushRepeated,
|
|
8
8
|
QUERY_DESCRIPTION,
|
|
9
9
|
REF_DESCRIPTION,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
withFooter,
|
|
14
14
|
type ToolDeps,
|
|
15
15
|
} from "./common.js";
|
|
16
|
+
import type { MutationOperation } from "../mutation-approvals.js";
|
|
16
17
|
|
|
17
18
|
const ISSUE_ACTIONS = [
|
|
18
19
|
"list",
|
|
@@ -33,6 +34,17 @@ const ISSUE_ACTIONS = [
|
|
|
33
34
|
] as const;
|
|
34
35
|
export type IssueAction = (typeof ISSUE_ACTIONS)[number];
|
|
35
36
|
|
|
37
|
+
const ISSUE_MUTATIONS: Partial<Record<IssueAction, MutationOperation>> = {
|
|
38
|
+
create: "issue.create",
|
|
39
|
+
edit: "issue.edit",
|
|
40
|
+
change_state: "issue.change_state",
|
|
41
|
+
add_comment: "issue.add_comment",
|
|
42
|
+
link: "issue.link",
|
|
43
|
+
log_work: "issue.log_work",
|
|
44
|
+
create_branch: "issue.create_branch",
|
|
45
|
+
checkout: "issue.checkout",
|
|
46
|
+
};
|
|
47
|
+
|
|
36
48
|
export interface IssueParams {
|
|
37
49
|
action?: IssueAction;
|
|
38
50
|
ref?: string;
|
|
@@ -41,14 +53,14 @@ export interface IssueParams {
|
|
|
41
53
|
state?: string;
|
|
42
54
|
comment?: string;
|
|
43
55
|
content?: string;
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
link_name?: string;
|
|
57
|
+
target_ref?: string;
|
|
46
58
|
hours?: number;
|
|
47
59
|
fields?: string[];
|
|
48
60
|
iterations?: string[];
|
|
49
|
-
|
|
61
|
+
own_estimated_time?: number;
|
|
50
62
|
confidential?: boolean;
|
|
51
|
-
|
|
63
|
+
for_write?: boolean;
|
|
52
64
|
project?: string;
|
|
53
65
|
query?: string;
|
|
54
66
|
count?: number;
|
|
@@ -79,9 +91,10 @@ export function buildIssueArgs(params: IssueParams): string[] {
|
|
|
79
91
|
if (params.project !== undefined) args.push("--project", params.project);
|
|
80
92
|
pushRepeated(args, "--field", params.fields);
|
|
81
93
|
pushRepeated(args, "--iteration", params.iterations);
|
|
82
|
-
if (params.
|
|
83
|
-
args.push("--own-estimated-time", String(params.
|
|
84
|
-
if (params.confidential
|
|
94
|
+
if (params.own_estimated_time !== undefined)
|
|
95
|
+
args.push("--own-estimated-time", String(params.own_estimated_time));
|
|
96
|
+
if (params.confidential !== undefined)
|
|
97
|
+
args.push(`--confidential=${params.confidential}`);
|
|
85
98
|
return args;
|
|
86
99
|
case "edit":
|
|
87
100
|
args.push("edit", ref());
|
|
@@ -90,10 +103,10 @@ export function buildIssueArgs(params: IssueParams): string[] {
|
|
|
90
103
|
args.push("--description", params.description);
|
|
91
104
|
pushRepeated(args, "--field", params.fields);
|
|
92
105
|
pushRepeated(args, "--iteration", params.iterations);
|
|
93
|
-
if (params.
|
|
94
|
-
args.push("--own-estimated-time", String(params.
|
|
106
|
+
if (params.own_estimated_time !== undefined)
|
|
107
|
+
args.push("--own-estimated-time", String(params.own_estimated_time));
|
|
95
108
|
if (params.confidential !== undefined)
|
|
96
|
-
args.push(
|
|
109
|
+
args.push(`--confidential=${params.confidential}`);
|
|
97
110
|
return args;
|
|
98
111
|
case "change_state":
|
|
99
112
|
args.push(
|
|
@@ -116,8 +129,8 @@ export function buildIssueArgs(params: IssueParams): string[] {
|
|
|
116
129
|
...args,
|
|
117
130
|
"link",
|
|
118
131
|
ref(),
|
|
119
|
-
requireParam(params.
|
|
120
|
-
requireParam(params.
|
|
132
|
+
requireParam(params.link_name, action, "link_name"),
|
|
133
|
+
requireParam(params.target_ref, action, "target_ref"),
|
|
121
134
|
];
|
|
122
135
|
case "log_work":
|
|
123
136
|
args.push(
|
|
@@ -131,7 +144,7 @@ export function buildIssueArgs(params: IssueParams): string[] {
|
|
|
131
144
|
return [...args, "create-branch", ref()];
|
|
132
145
|
case "checkout":
|
|
133
146
|
args.push("checkout", ref());
|
|
134
|
-
if (params.
|
|
147
|
+
if (params.for_write === true) args.push("--for-write");
|
|
135
148
|
return args;
|
|
136
149
|
case "current_reference":
|
|
137
150
|
return [...args, "current-reference"];
|
|
@@ -141,6 +154,8 @@ export function buildIssueArgs(params: IssueParams): string[] {
|
|
|
141
154
|
return [...args, "get-valid-links"];
|
|
142
155
|
case "query_description":
|
|
143
156
|
return [...args, "get-query-description"];
|
|
157
|
+
default:
|
|
158
|
+
throw new Error(`unsupported issue action: ${String(action)}`);
|
|
144
159
|
}
|
|
145
160
|
}
|
|
146
161
|
|
|
@@ -163,10 +178,10 @@ export function registerIssueTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
163
178
|
Type.String({ description: "Markdown note attached to the action" }),
|
|
164
179
|
),
|
|
165
180
|
content: Type.Optional(Type.String({ description: "Markdown comment text" })),
|
|
166
|
-
|
|
181
|
+
link_name: Type.Optional(
|
|
167
182
|
Type.String({ description: "Issue link name; discover via valid_links" }),
|
|
168
183
|
),
|
|
169
|
-
|
|
184
|
+
target_ref: Type.Optional(
|
|
170
185
|
Type.String({ description: "Target issue reference for the link action" }),
|
|
171
186
|
),
|
|
172
187
|
hours: Type.Optional(
|
|
@@ -176,11 +191,11 @@ export function registerIssueTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
176
191
|
Type.Array(Type.String(), { description: FIELDS_DESCRIPTION }),
|
|
177
192
|
),
|
|
178
193
|
iterations: Type.Optional(Type.Array(Type.String())),
|
|
179
|
-
|
|
180
|
-
Type.
|
|
194
|
+
own_estimated_time: Type.Optional(
|
|
195
|
+
Type.Number({ minimum: 0, description: "Estimated hours" }),
|
|
181
196
|
),
|
|
182
197
|
confidential: Type.Optional(Type.Boolean()),
|
|
183
|
-
|
|
198
|
+
for_write: Type.Optional(
|
|
184
199
|
Type.Boolean({ description: "checkout: set up the branch for pushing" }),
|
|
185
200
|
),
|
|
186
201
|
project: Type.Optional(
|
|
@@ -198,7 +213,7 @@ export function registerIssueTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
198
213
|
}),
|
|
199
214
|
),
|
|
200
215
|
offset: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
201
|
-
});
|
|
216
|
+
}, { additionalProperties: false });
|
|
202
217
|
type Params = Static<typeof parameters>;
|
|
203
218
|
|
|
204
219
|
pi.registerTool({
|
|
@@ -206,13 +221,22 @@ export function registerIssueTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
206
221
|
label: "OneDev Issues",
|
|
207
222
|
description:
|
|
208
223
|
"Read and edit OneDev issues: query, inspect, comment, change fields/labels/state, log work, manage issue branches. Runs in the current repository's project; plain numbers resolve against it.",
|
|
209
|
-
promptGuidelines: [
|
|
210
|
-
"Use onedev_issue valid_fields before setting fields or state you have not seen this session.",
|
|
211
|
-
"Post comments and field changes only after presenting the intent to the user.",
|
|
212
|
-
],
|
|
213
224
|
parameters,
|
|
214
|
-
async execute(_toolCallId, params: Params, signal) {
|
|
215
|
-
const
|
|
225
|
+
async execute(_toolCallId, params: Params, signal, _onUpdate, ctx) {
|
|
226
|
+
const args = buildIssueArgs(params);
|
|
227
|
+
const operation = ISSUE_MUTATIONS[params.action];
|
|
228
|
+
if (operation) {
|
|
229
|
+
await confirmMutation(deps, ctx, {
|
|
230
|
+
operation,
|
|
231
|
+
title: `Approve OneDev issue action: ${params.action}`,
|
|
232
|
+
details: [
|
|
233
|
+
params.ref ? `Issue: ${params.ref}` : undefined,
|
|
234
|
+
params.title ? `Title: ${params.title}` : undefined,
|
|
235
|
+
].filter(Boolean).join("\n") || "This action changes issue or local branch state.",
|
|
236
|
+
signal,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const output = await runInContext(deps, args, { signal });
|
|
216
240
|
return toolResult(
|
|
217
241
|
withFooter(output.text, deps.context(), output.truncated),
|
|
218
242
|
{
|
package/src/tools/pull.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
3
3
|
import { Type, type Static } from "typebox";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
confirmMutation,
|
|
6
6
|
pushRepeated,
|
|
7
7
|
QUERY_DESCRIPTION,
|
|
8
8
|
REF_DESCRIPTION,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
withFooter,
|
|
13
13
|
type ToolDeps,
|
|
14
14
|
} from "./common.js";
|
|
15
|
+
import type { MutationOperation } from "../mutation-approvals.js";
|
|
15
16
|
|
|
16
17
|
const PULL_ACTIONS = [
|
|
17
18
|
"list",
|
|
@@ -24,6 +25,9 @@ const PULL_ACTIONS = [
|
|
|
24
25
|
"edit",
|
|
25
26
|
"add_comment",
|
|
26
27
|
"add_code_comment",
|
|
28
|
+
"reply_code_comment",
|
|
29
|
+
"resolve_code_comment",
|
|
30
|
+
"unresolve_code_comment",
|
|
27
31
|
"approve",
|
|
28
32
|
"request_changes",
|
|
29
33
|
"merge",
|
|
@@ -37,6 +41,21 @@ const PULL_ACTIONS = [
|
|
|
37
41
|
] as const;
|
|
38
42
|
export type PullAction = (typeof PULL_ACTIONS)[number];
|
|
39
43
|
|
|
44
|
+
const PULL_MUTATIONS: Partial<Record<PullAction, MutationOperation>> = {
|
|
45
|
+
create: "pull.create",
|
|
46
|
+
edit: "pull.edit",
|
|
47
|
+
add_comment: "pull.add_comment",
|
|
48
|
+
add_code_comment: "pull.add_code_comment",
|
|
49
|
+
reply_code_comment: "pull.reply_code_comment",
|
|
50
|
+
resolve_code_comment: "pull.resolve_code_comment",
|
|
51
|
+
unresolve_code_comment: "pull.unresolve_code_comment",
|
|
52
|
+
approve: "pull.approve",
|
|
53
|
+
request_changes: "pull.request_changes",
|
|
54
|
+
merge: "pull.merge",
|
|
55
|
+
discard: "pull.discard",
|
|
56
|
+
checkout: "pull.checkout",
|
|
57
|
+
};
|
|
58
|
+
|
|
40
59
|
export interface PullParams {
|
|
41
60
|
action?: PullAction;
|
|
42
61
|
ref?: string;
|
|
@@ -44,29 +63,29 @@ export interface PullParams {
|
|
|
44
63
|
description?: string;
|
|
45
64
|
content?: string;
|
|
46
65
|
summary?: string;
|
|
47
|
-
|
|
66
|
+
commit_message?: string;
|
|
48
67
|
comment?: string;
|
|
49
68
|
file?: string;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
from_line?: number;
|
|
70
|
+
to_line?: number;
|
|
71
|
+
comment_id?: number;
|
|
72
|
+
source_branch?: string;
|
|
73
|
+
target_branch?: string;
|
|
74
|
+
source_project?: string;
|
|
75
|
+
target_project?: string;
|
|
56
76
|
assignees?: string[];
|
|
57
77
|
reviewers?: string[];
|
|
58
|
-
|
|
78
|
+
remove_reviewers?: string[];
|
|
59
79
|
labels?: string[];
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
merge_strategy?: string;
|
|
81
|
+
auto_merge?: boolean;
|
|
82
|
+
auto_merge_commit_message?: string;
|
|
83
|
+
for_code_review?: boolean;
|
|
84
|
+
for_write?: boolean;
|
|
65
85
|
project?: string;
|
|
66
86
|
query?: string;
|
|
67
87
|
count?: number;
|
|
68
88
|
offset?: number;
|
|
69
|
-
confirm?: boolean;
|
|
70
89
|
}
|
|
71
90
|
|
|
72
91
|
export function buildPullArgs(params: PullParams): string[] {
|
|
@@ -74,14 +93,14 @@ export function buildPullArgs(params: PullParams): string[] {
|
|
|
74
93
|
const action = requireParam(params.action, "pr", "action");
|
|
75
94
|
const ref = (): string => requireParam(params.ref, action, "ref");
|
|
76
95
|
const branchScope = (): void => {
|
|
77
|
-
if (params.
|
|
78
|
-
args.push("--source-branch", params.
|
|
79
|
-
if (params.
|
|
80
|
-
args.push("--target-branch", params.
|
|
81
|
-
if (params.
|
|
82
|
-
args.push("--source-project", params.
|
|
83
|
-
if (params.
|
|
84
|
-
args.push("--target-project", params.
|
|
96
|
+
if (params.source_branch !== undefined)
|
|
97
|
+
args.push("--source-branch", params.source_branch);
|
|
98
|
+
if (params.target_branch !== undefined)
|
|
99
|
+
args.push("--target-branch", params.target_branch);
|
|
100
|
+
if (params.source_project !== undefined)
|
|
101
|
+
args.push("--source-project", params.source_project);
|
|
102
|
+
if (params.target_project !== undefined)
|
|
103
|
+
args.push("--target-project", params.target_project);
|
|
85
104
|
};
|
|
86
105
|
switch (action) {
|
|
87
106
|
case "list":
|
|
@@ -102,7 +121,7 @@ export function buildPullArgs(params: PullParams): string[] {
|
|
|
102
121
|
return [...args, "get-builds", ref()];
|
|
103
122
|
case "get_patch":
|
|
104
123
|
args.push("get-patch", ref());
|
|
105
|
-
if (params.
|
|
124
|
+
if (params.for_code_review === true) args.push("--for-code-review");
|
|
106
125
|
return args;
|
|
107
126
|
case "create":
|
|
108
127
|
args.push("create", requireParam(params.title, action, "title"));
|
|
@@ -112,8 +131,8 @@ export function buildPullArgs(params: PullParams): string[] {
|
|
|
112
131
|
pushRepeated(args, "--assignee", params.assignees);
|
|
113
132
|
pushRepeated(args, "--reviewer", params.reviewers);
|
|
114
133
|
pushRepeated(args, "--label", params.labels);
|
|
115
|
-
if (params.
|
|
116
|
-
args.push("--merge-strategy", params.
|
|
134
|
+
if (params.merge_strategy !== undefined)
|
|
135
|
+
args.push("--merge-strategy", params.merge_strategy);
|
|
117
136
|
return args;
|
|
118
137
|
case "edit":
|
|
119
138
|
args.push("edit", ref());
|
|
@@ -122,15 +141,15 @@ export function buildPullArgs(params: PullParams): string[] {
|
|
|
122
141
|
args.push("--description", params.description);
|
|
123
142
|
pushRepeated(args, "--assignee", params.assignees);
|
|
124
143
|
pushRepeated(args, "--add-reviewer", params.reviewers);
|
|
125
|
-
pushRepeated(args, "--remove-reviewer", params.
|
|
144
|
+
pushRepeated(args, "--remove-reviewer", params.remove_reviewers);
|
|
126
145
|
pushRepeated(args, "--label", params.labels);
|
|
127
|
-
if (params.
|
|
128
|
-
args.push("--merge-strategy", params.
|
|
129
|
-
if (params.
|
|
130
|
-
args.push(
|
|
131
|
-
if (params.autoMergeCommitMessage !== undefined)
|
|
132
|
-
args.push("--auto-merge-commit-message", params.autoMergeCommitMessage);
|
|
146
|
+
if (params.merge_strategy !== undefined)
|
|
147
|
+
args.push("--merge-strategy", params.merge_strategy);
|
|
148
|
+
if (params.auto_merge !== undefined) {
|
|
149
|
+
args.push(`--auto-merge=${params.auto_merge}`);
|
|
133
150
|
}
|
|
151
|
+
if (params.auto_merge_commit_message !== undefined)
|
|
152
|
+
args.push("--auto-merge-commit-message", params.auto_merge_commit_message);
|
|
134
153
|
return args;
|
|
135
154
|
case "add_comment":
|
|
136
155
|
return [
|
|
@@ -148,10 +167,29 @@ export function buildPullArgs(params: PullParams): string[] {
|
|
|
148
167
|
args.push("--file", requireParam(params.file, action, "file"));
|
|
149
168
|
args.push(
|
|
150
169
|
"--from-line",
|
|
151
|
-
String(requireParam(params.
|
|
170
|
+
String(requireParam(params.from_line, action, "from_line")),
|
|
152
171
|
);
|
|
153
|
-
args.push("--to-line", String(params.
|
|
172
|
+
args.push("--to-line", String(params.to_line ?? params.from_line));
|
|
154
173
|
return args;
|
|
174
|
+
case "reply_code_comment":
|
|
175
|
+
return [
|
|
176
|
+
"code-comment",
|
|
177
|
+
"add-reply",
|
|
178
|
+
String(requireParam(params.comment_id, action, "comment_id")),
|
|
179
|
+
requireParam(params.content, action, "content"),
|
|
180
|
+
];
|
|
181
|
+
case "resolve_code_comment":
|
|
182
|
+
return [
|
|
183
|
+
"code-comment",
|
|
184
|
+
"resolve",
|
|
185
|
+
String(requireParam(params.comment_id, action, "comment_id")),
|
|
186
|
+
];
|
|
187
|
+
case "unresolve_code_comment":
|
|
188
|
+
return [
|
|
189
|
+
"code-comment",
|
|
190
|
+
"unresolve",
|
|
191
|
+
String(requireParam(params.comment_id, action, "comment_id")),
|
|
192
|
+
];
|
|
155
193
|
case "approve":
|
|
156
194
|
args.push("approve", ref());
|
|
157
195
|
if (params.summary !== undefined) args.push("--summary", params.summary);
|
|
@@ -161,19 +199,17 @@ export function buildPullArgs(params: PullParams): string[] {
|
|
|
161
199
|
if (params.summary !== undefined) args.push("--summary", params.summary);
|
|
162
200
|
return args;
|
|
163
201
|
case "merge":
|
|
164
|
-
confirmGate(action, params.confirm);
|
|
165
202
|
args.push("merge", ref());
|
|
166
|
-
if (params.
|
|
167
|
-
args.push("--commit-message", params.
|
|
203
|
+
if (params.commit_message !== undefined)
|
|
204
|
+
args.push("--commit-message", params.commit_message);
|
|
168
205
|
return args;
|
|
169
206
|
case "discard":
|
|
170
|
-
confirmGate(action, params.confirm);
|
|
171
207
|
args.push("discard", ref());
|
|
172
208
|
if (params.comment !== undefined) args.push("--comment", params.comment);
|
|
173
209
|
return args;
|
|
174
210
|
case "checkout":
|
|
175
211
|
args.push("checkout", ref());
|
|
176
|
-
if (params.
|
|
212
|
+
if (params.for_write === true) args.push("--for-write");
|
|
177
213
|
return args;
|
|
178
214
|
case "current_reference":
|
|
179
215
|
return [...args, "current-reference"];
|
|
@@ -189,6 +225,8 @@ export function buildPullArgs(params: PullParams): string[] {
|
|
|
189
225
|
args.push("get-commit-message-requirement");
|
|
190
226
|
branchScope();
|
|
191
227
|
return args;
|
|
228
|
+
default:
|
|
229
|
+
throw new Error(`unsupported pull action: ${String(action)}`);
|
|
192
230
|
}
|
|
193
231
|
}
|
|
194
232
|
|
|
@@ -196,7 +234,7 @@ export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
196
234
|
const parameters = Type.Object({
|
|
197
235
|
action: StringEnum(PULL_ACTIONS, {
|
|
198
236
|
description:
|
|
199
|
-
"list, get, get_comments, get_code_comments, get_builds, get_patch
|
|
237
|
+
"list, get, get_comments, get_code_comments, get_builds, get_patch, create, edit, add_comment, add_code_comment, reply_code_comment, resolve_code_comment, unresolve_code_comment, approve, request_changes, merge, discard, checkout, current_reference, valid_labels, query_description, title_description_requirement, commit_message_requirement",
|
|
200
238
|
}),
|
|
201
239
|
ref: Type.Optional(Type.String({ description: REF_DESCRIPTION })),
|
|
202
240
|
title: Type.Optional(Type.String()),
|
|
@@ -210,7 +248,7 @@ export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
210
248
|
comment: Type.Optional(
|
|
211
249
|
Type.String({ description: "Markdown note for discard" }),
|
|
212
250
|
),
|
|
213
|
-
|
|
251
|
+
commit_message: Type.Optional(
|
|
214
252
|
Type.String({
|
|
215
253
|
description: "Commit message for merge (required by squash merges)",
|
|
216
254
|
}),
|
|
@@ -218,29 +256,36 @@ export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
218
256
|
file: Type.Optional(
|
|
219
257
|
Type.String({ description: "add_code_comment: file path in the PR patch" }),
|
|
220
258
|
),
|
|
221
|
-
|
|
259
|
+
from_line: Type.Optional(
|
|
222
260
|
Type.Integer({
|
|
223
261
|
minimum: 1,
|
|
224
262
|
description: "add_code_comment: 1-based start line (right side of patch)",
|
|
225
263
|
}),
|
|
226
264
|
),
|
|
227
|
-
|
|
265
|
+
to_line: Type.Optional(
|
|
228
266
|
Type.Integer({
|
|
229
267
|
minimum: 1,
|
|
230
268
|
description: "add_code_comment: 1-based end line; defaults to from_line",
|
|
231
269
|
}),
|
|
232
270
|
),
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
271
|
+
comment_id: Type.Optional(
|
|
272
|
+
Type.Integer({
|
|
273
|
+
minimum: 1,
|
|
274
|
+
description:
|
|
275
|
+
"Code comment id for reply_code_comment, resolve_code_comment, or unresolve_code_comment",
|
|
276
|
+
}),
|
|
277
|
+
),
|
|
278
|
+
source_branch: Type.Optional(Type.String()),
|
|
279
|
+
target_branch: Type.Optional(Type.String()),
|
|
280
|
+
source_project: Type.Optional(Type.String()),
|
|
281
|
+
target_project: Type.Optional(Type.String()),
|
|
237
282
|
assignees: Type.Optional(
|
|
238
283
|
Type.Array(Type.String(), { description: "User logins, repeatable" }),
|
|
239
284
|
),
|
|
240
285
|
reviewers: Type.Optional(
|
|
241
286
|
Type.Array(Type.String(), { description: "User logins, repeatable" }),
|
|
242
287
|
),
|
|
243
|
-
|
|
288
|
+
remove_reviewers: Type.Optional(
|
|
244
289
|
Type.Array(Type.String(), { description: "edit: user logins to remove" }),
|
|
245
290
|
),
|
|
246
291
|
labels: Type.Optional(
|
|
@@ -248,13 +293,13 @@ export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
248
293
|
description: "Label names; discover via valid_labels",
|
|
249
294
|
}),
|
|
250
295
|
),
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
296
|
+
merge_strategy: Type.Optional(Type.String()),
|
|
297
|
+
auto_merge: Type.Optional(Type.Boolean()),
|
|
298
|
+
auto_merge_commit_message: Type.Optional(Type.String()),
|
|
299
|
+
for_code_review: Type.Optional(
|
|
255
300
|
Type.Boolean({ description: "get_patch: review-scoped patch" }),
|
|
256
301
|
),
|
|
257
|
-
|
|
302
|
+
for_write: Type.Optional(
|
|
258
303
|
Type.Boolean({ description: "checkout: set up the branch for pushing" }),
|
|
259
304
|
),
|
|
260
305
|
project: Type.Optional(
|
|
@@ -265,13 +310,7 @@ export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
265
310
|
query: Type.Optional(Type.String({ description: QUERY_DESCRIPTION })),
|
|
266
311
|
count: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
|
|
267
312
|
offset: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
268
|
-
|
|
269
|
-
Type.Boolean({
|
|
270
|
-
description:
|
|
271
|
-
"Required true for merge/discard: re-run with confirm after the user approves the destructive action",
|
|
272
|
-
}),
|
|
273
|
-
),
|
|
274
|
-
});
|
|
313
|
+
}, { additionalProperties: false });
|
|
275
314
|
type Params = Static<typeof parameters>;
|
|
276
315
|
|
|
277
316
|
pi.registerTool({
|
|
@@ -279,13 +318,23 @@ export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
|
279
318
|
label: "OneDev Pull Requests",
|
|
280
319
|
description:
|
|
281
320
|
"Read and edit OneDev pull requests: metadata, labels, comments, line-anchored code comments, builds, patch, review outcomes, and guarded merge/discard. Runs in the current repository's project.",
|
|
282
|
-
promptGuidelines: [
|
|
283
|
-
"Use onedev_pull get_patch with for_code_review for review-scoped diffs.",
|
|
284
|
-
"merge and discard require confirm: true after explicit user approval.",
|
|
285
|
-
],
|
|
286
321
|
parameters,
|
|
287
|
-
async execute(_toolCallId, params: Params, signal) {
|
|
288
|
-
const
|
|
322
|
+
async execute(_toolCallId, params: Params, signal, _onUpdate, ctx) {
|
|
323
|
+
const args = buildPullArgs(params);
|
|
324
|
+
const operation = PULL_MUTATIONS[params.action];
|
|
325
|
+
if (operation) {
|
|
326
|
+
await confirmMutation(deps, ctx, {
|
|
327
|
+
operation,
|
|
328
|
+
title: `Approve OneDev pull request action: ${params.action}`,
|
|
329
|
+
details: [
|
|
330
|
+
params.ref ? `Pull request: ${params.ref}` : undefined,
|
|
331
|
+
params.comment_id ? `Code comment: ${params.comment_id}` : undefined,
|
|
332
|
+
params.title ? `Title: ${params.title}` : undefined,
|
|
333
|
+
].filter(Boolean).join("\n") || "This action changes pull request or local branch state.",
|
|
334
|
+
signal,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
const output = await runInContext(deps, args, { signal });
|
|
289
338
|
return toolResult(
|
|
290
339
|
withFooter(output.text, deps.context(), output.truncated),
|
|
291
340
|
{
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
import type { OneDevSourceWatchManager } from "../source-watch.js";
|
|
5
|
+
import { toolResult } from "./common.js";
|
|
6
|
+
|
|
7
|
+
export function registerWatchTool(
|
|
8
|
+
pi: ExtensionAPI,
|
|
9
|
+
watches: OneDevSourceWatchManager,
|
|
10
|
+
): void {
|
|
11
|
+
pi.registerTool({
|
|
12
|
+
name: "onedev_watch",
|
|
13
|
+
label: "OneDev Watch",
|
|
14
|
+
description:
|
|
15
|
+
"Create session-scoped one-shot watches for a build result or pull-request attention, or list/stop active watches. Notifications contain bounded metadata; use read tools for details.",
|
|
16
|
+
parameters: Type.Object(
|
|
17
|
+
{
|
|
18
|
+
action: StringEnum([
|
|
19
|
+
"watch_build",
|
|
20
|
+
"watch_pull_attention",
|
|
21
|
+
"list",
|
|
22
|
+
"stop",
|
|
23
|
+
]),
|
|
24
|
+
ref: Type.Optional(
|
|
25
|
+
Type.String({ description: "Build or pull request reference to watch" }),
|
|
26
|
+
),
|
|
27
|
+
watch_id: Type.Optional(
|
|
28
|
+
Type.String({ description: "Watch id required by stop" }),
|
|
29
|
+
),
|
|
30
|
+
interval_seconds: Type.Optional(
|
|
31
|
+
Type.Integer({ minimum: 15, maximum: 300, default: 30 }),
|
|
32
|
+
),
|
|
33
|
+
ttl_minutes: Type.Optional(
|
|
34
|
+
Type.Integer({ minimum: 1, maximum: 1440, default: 60 }),
|
|
35
|
+
),
|
|
36
|
+
},
|
|
37
|
+
{ additionalProperties: false },
|
|
38
|
+
),
|
|
39
|
+
async execute(_toolCallId, params, signal) {
|
|
40
|
+
if (params.action === "list") {
|
|
41
|
+
const active = watches.list();
|
|
42
|
+
return toolResult(
|
|
43
|
+
active.length > 0
|
|
44
|
+
? JSON.stringify(active, null, 2)
|
|
45
|
+
: "No active OneDev watches.",
|
|
46
|
+
{ watches: active },
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
if (params.action === "stop") {
|
|
50
|
+
if (!params.watch_id) throw new Error("stop requires watch_id");
|
|
51
|
+
const stopped = watches.stop(params.watch_id);
|
|
52
|
+
return toolResult(
|
|
53
|
+
stopped
|
|
54
|
+
? `Stopped OneDev watch ${params.watch_id}.`
|
|
55
|
+
: `OneDev watch ${params.watch_id} was not active.`,
|
|
56
|
+
{ watch_id: params.watch_id, stopped },
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (!params.ref) throw new Error(`${params.action} requires ref`);
|
|
60
|
+
const result = await watches.add({
|
|
61
|
+
kind: params.action === "watch_build" ? "build" : "pull_attention",
|
|
62
|
+
ref: params.ref,
|
|
63
|
+
intervalSeconds: params.interval_seconds,
|
|
64
|
+
ttlMinutes: params.ttl_minutes,
|
|
65
|
+
signal,
|
|
66
|
+
});
|
|
67
|
+
return toolResult(result.message, result.watch ?? { active: false });
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|