pi-onedev-toolkit 0.2.1 → 0.2.2

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.2.2 — 2026-09-04
6
+
7
+ ### Changed
8
+
9
+ - Compressed model-visible tool schemas and bundled workflow prompts without changing tool names, actions, validations, or approval gates; tests now enforce schema and skill byte budgets.
10
+ - Model-visible output now defaults to 16 KB for normal results and 32 KB for PR patches, build logs, and build-spec schemas. `max_bytes` can request up to 128 KB; internal watch polling remains independent.
11
+
3
12
  ## 0.2.1 — 2026-09-03
4
13
 
5
14
  ### Fixed
package/README.md CHANGED
@@ -37,6 +37,8 @@ When the current directory is a OneDev repository, the status line shows the act
37
37
 
38
38
  Lazy by design: domain tools activate only through `onedev_tools`, keeping the default tool list and system prompt light. Activation performs the first authenticated check and returns the server, project, and login context in the same response.
39
39
 
40
+ Model-visible issue, PR, and build output defaults to 16 KB. PR patches, build logs, and build-spec schemas default to 32 KB. Use `max_bytes` to raise or lower a call's budget up to 128 KB; truncation keeps the payload head/tail and is reported explicitly. Internal watch polling remains separately bounded and is never copied into the model context.
41
+
40
42
  Remote and local mutations use Pi's real interactive approval UI. An approval can apply once, for the session, or persist for the exact server/project/operation in `~/.pi/agent/onedev-toolkit.json`; remove that file to revoke all saved approvals. Non-interactive sessions reject unapproved mutations. `build run` in local mode uses an isolated temporary Git index so TOD cannot leave the user's index staged; `check_spec` is treated as a mutation because TOD may upgrade `.onedev-buildspec.yml`.
41
43
 
42
44
  Watches are session-scoped and one-shot. They use one timer, bounded polling, expiration and backoff, and wake the agent with metadata only. The agent retrieves logs, comments, or build details with the corresponding read tool. At most ten watches may be active; session changes and shutdown clear them.
@@ -60,6 +62,12 @@ Attachment download is intentionally not exposed: current TOD releases may send
60
62
  - `onedev-issue-to-pr` — gather issue context, branch, implement, present for review, then push and open the PR
61
63
  - `onedev-pr-review` — full-context PR review with line-anchored findings; submits only on explicit request
62
64
 
65
+ If you use only tools and slash commands, omit both workflow entries from every prompt while keeping the extension and tools:
66
+
67
+ ```json
68
+ { "source": "npm:pi-onedev-toolkit", "skills": [] }
69
+ ```
70
+
63
71
  ## Development
64
72
 
65
73
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-onedev-toolkit",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Lightweight OneDev integration for Pi via TOD: repo-aware issue, pull request, build, and watch tools",
5
5
  "type": "module",
6
6
  "author": {
@@ -53,11 +53,12 @@
53
53
  "typebox": "*"
54
54
  },
55
55
  "devDependencies": {
56
- "@earendil-works/pi-ai": "0.84.1",
57
- "@earendil-works/pi-coding-agent": "0.84.1",
56
+ "@earendil-works/pi-ai": "0.85.0",
57
+ "@earendil-works/pi-server": "0.85.0",
58
+ "@earendil-works/pi-coding-agent": "0.85.0",
58
59
  "@types/node": "^22.19.0",
59
60
  "@types/proper-lockfile": "4.1.4",
60
- "typebox": "1.3.12",
61
+ "typebox": "1.3.25",
61
62
  "typescript": "^7.0.2",
62
63
  "vitest": "^4.1.10"
63
64
  },
@@ -1,60 +1,26 @@
1
1
  ---
2
2
  name: onedev-issue-to-pr
3
- description: Implement a OneDev issue end to end - gather issue context, branch, implement, then (after user approval) push and open a linked pull request. Use when the user asks to work on, implement, or fix a OneDev issue.
3
+ description: "Implement a OneDev issue and open a linked PR only after user approval."
4
4
  ---
5
5
 
6
- # OneDev issue to pull request
6
+ # OneDev Issue to Pull Request
7
7
 
8
- ## Prerequisites
8
+ ## Rules
9
9
 
10
- - Call `onedev_tools` with domains `["issue", "pull"]` first; note your login name from the returned context. If activation reports a readiness problem, use `onedev_context` `setup` and stop until it is resolved.
11
- - The current repository belongs to the issue's project (plain refs like `123` resolve against it).
10
+ - Activate `issue` and `pull` together with `onedev_tools`; note the returned login. On readiness failure, use `onedev_context setup` and stop.
11
+ - Plain refs use the current repository's OneDev project. Never guess a missing ref or project.
12
+ - Preserve unrelated local changes. If checkout is dirty, report and stop; never stash automatically.
13
+ - Do not push, comment, or open a PR before the workflow checkpoint and explicit user approval. Toolkit mutations still require Pi UI approval.
12
14
 
13
15
  ## Workflow
14
16
 
15
- 1. **Resolve the issue reference.** Use the reference from the prompt, or derive it
16
- with `onedev_issue` action `current_reference`. If neither works, stop and report.
17
+ 1. Resolve the prompt ref or use `onedev_issue current_reference`; stop if neither works.
18
+ 2. Prepare the branch with `onedev_issue checkout` using `for_write: true`.
19
+ 3. Read `onedev_issue get` and `get_comments`; use author/login context and derive acceptance criteria from the full discussion.
20
+ 4. When failure evidence matters, activate `build`; inspect build metadata and start `get_log` with `max_bytes=32000`, raising it only if truncation hides required evidence. Follow same-commit dependency failures as the likely root cause; different-commit failures are inherited.
21
+ 5. Implement the smallest complete change and run the narrowest relevant test. Optionally run local OneDev CI with `onedev_build run mode=local`.
22
+ 6. **Stop for review.** Show the change, checks, and any issue comment you intend to post. Do not submit anything.
23
+ 7. After explicit approval, read `onedev_pull commit_message_requirement` and `title_description_requirement`; empty output means no extra requirement. Commit and push with local Git, then create the PR against the intended target branch. Link the issue in the description when the issue branch does not imply it. Post only the approved issue comment.
24
+ 8. For long CI, activate `watch` and start `onedev_watch watch_build`; re-read the build after the metadata-only notification.
17
25
 
18
- 2. **Prepare the checkout** (unless the user says otherwise):
19
- `onedev_issue` action `checkout` with `ref` and `for_write: true`.
20
- It creates the server-side issue branch and sets up tracking.
21
- If the working copy is dirty, report it and stop — do not stash on your own.
22
-
23
- 3. **Read the full issue context** — never work from the prompt alone:
24
- - `onedev_issue` `get` (title, description, fields, submitter)
25
- - `onedev_issue` `get_comments`
26
- - Match your login name against submitter/comment authors to understand your role.
27
- - If a failed build is part of the issue, gather evidence with `onedev_build`
28
- after activating the `build` domain (`get`, `get_log`, and a named
29
- `get_unit_test_report` when useful) before planning changes. Follow `Dependency build … failed`
30
- chains: same-commit dependency failures are the real target; different-commit
31
- ones mean the failure is inherited.
32
-
33
- 4. **Implement the work.** Code changes, or an explanatory issue comment if no code
34
- is needed. Run the smallest relevant test. Optionally validate CI locally with
35
- `onedev_build` `run` (`mode: "local"`) before committing.
36
-
37
- 5. **STOP — present for review.** Summarize the change and the comment you intend
38
- to post. Do not push or post anything yet. This is the mandatory checkpoint.
39
-
40
- 6. **After explicit user approval, submit:**
41
- - Compose the commit message against the server requirements:
42
- `onedev_pull` `commit_message_requirement` (and `title_description_requirement`
43
- before creating the PR). Empty output means no requirement.
44
- - `git add -A && git commit && git push` on the issue branch.
45
- - Create the PR: `onedev_pull` `create` with `title`, `description`, and
46
- `target_branch` (source branch defaults to the current branch). The issue link
47
- is inferred from the branch; state it in the description if not.
48
- - Post the drafted comment with `onedev_issue` `add_comment` if one was prepared.
49
-
50
- 7. **For long-running CI**, activate `watch` and use `onedev_watch` `watch_build`
51
- after obtaining a build reference. The notification is one-shot and metadata-only;
52
- inspect the build with `onedev_build` when it fires.
53
-
54
- ## Notes
55
-
56
- - Write comment text in Markdown.
57
- - Reference entities as `#123` (current project), `project#123`, or `PROJ-123`.
58
- - If blocked mid-workflow, report the command and error, and stop.
59
- - Toolkit mutations still open Pi's interactive approval UI. Do not try to bypass
60
- it with a confirmation parameter.
26
+ Use Markdown for comments and `#123`, `project#123`, or `PROJ-123` for entities. On failure, report the command/error and stop; never invent a confirmation parameter.
@@ -1,71 +1,28 @@
1
1
  ---
2
2
  name: onedev-pr-review
3
- description: Review a OneDev pull request - gather PR context, builds, and fixed issues, read the review-scoped patch, draft findings with line-anchored code comments, and submit the review or merge only on explicit request. Use when the user asks to review, address feedback on, or merge a OneDev pull request.
3
+ description: "Review a OneDev PR; publish findings or merge only when asked."
4
4
  ---
5
5
 
6
- # OneDev pull request review
6
+ # OneDev Pull Request Review
7
7
 
8
- ## Prerequisites
8
+ ## Rules
9
9
 
10
- - Call `onedev_tools` with domains `["pull", "issue", "build"]`; note your login name. If activation reports a readiness problem, use `onedev_context` `setup` and stop until it is resolved.
11
- - The current repository belongs to the PR's source or target project.
10
+ - Activate `pull`, `issue`, and `build` together with `onedev_tools`; note the returned login. On readiness failure, use `onedev_context setup` and stop.
11
+ - Resolve the exact PR in its source/target repository. Never review from a patch alone.
12
+ - Analysis is read-only. Draft first; publish findings, change review state, or merge only after explicit user request and Pi UI approval.
13
+ - Never alter another user's code comments.
12
14
 
13
- ## Gather context (never review from the patch alone)
15
+ ## Workflow
14
16
 
15
- 1. **Resolve the PR reference** from the prompt or `onedev_pull` `current_reference`.
17
+ 1. Resolve the prompt ref or use `onedev_pull current_reference`.
18
+ 2. Checkout for inspection; use `for_write: true` only when changing code. Dirty checkout means report and stop.
19
+ 3. Read PR metadata, comments, code comments, and builds. Query fixed issues with `fixed in pull request "<ref>"`, then read their bodies/comments as the primary specification. Match authors against the returned login.
20
+ 4. Investigate every failed build. Start `onedev_build get_log` with `max_bytes=32000`; raise it only if truncation hides required evidence. Follow same-commit dependency failures before blaming the current job.
21
+ 5. Read `onedev_pull get_patch` with `for_code_review: true` and `max_bytes=32000`; inspect checkout files and callers when context is missing.
22
+ 6. Keep only concrete correctness, regression, security, data-loss, error-handling, or coverage findings. Triage prior discussion: reply where needed, resolve only addressed comments you own, and leave unrelated comments alone.
23
+ 7. Draft line findings with file/right-side lines, outside-patch findings as one overall comment, and `approve` or `request_changes` only when acting as a pending reviewer.
24
+ 8. **Stop and present** findings, replies, outcome, and merge plan. Submit nothing.
25
+ 9. On explicit request, publish the approved draft. For merge, satisfy `commit_message_requirement`; squash messages should reflect PR and fixed issues. Ask before rewriting commits for rebase-based conflict resolution.
26
+ 10. When waiting, activate `watch` and use `watch_pull_attention`; re-read PR state after its metadata-only notification.
16
27
 
17
- 2. **Checkout** for inspection: `onedev_pull` `checkout` (`for_write: true` when you
18
- will change code). Dirty working copy → report and stop.
19
-
20
- 3. **Read everything:**
21
- - `onedev_pull` `get` — title, description, source/target, merge strategy, status,
22
- submitter, reviewers, review state.
23
- - `onedev_pull` `get_comments` and `get_code_comments` — note code-comment `id`s,
24
- file, line range, resolution state, replies.
25
- - Fixed issues: `onedev_issue` `list` with query `fixed in pull request "<ref>"`,
26
- then `get` / `get_comments` on each. Treat fixed issues as the primary spec
27
- for what the PR should accomplish.
28
- - `onedev_pull` `get_builds` — investigate every `FAILED` build with
29
- `onedev_build` `get` / `get_log` before reviewing code.
30
- - Match your login name against authors to understand your role
31
- (pending reviewer, prior commenter, outsider).
32
-
33
- ## Review
34
-
35
- 1. Read the review-scoped patch: `onedev_pull` `get_patch` with
36
- `for_code_review: true`. Read files in the checkout when the patch lacks context.
37
- Check correctness, edge cases, security, style, and test coverage.
38
-
39
- 2. **Triage prior discussion:** resolve addressed code comments, reply where new
40
- information is needed, leave unrelated unresolved ones alone. Never modify
41
- another user's code comments. Use `reply_code_comment`, `resolve_code_comment`,
42
- or `unresolve_code_comment` with the comment `id`.
43
-
44
- 3. **Draft findings:**
45
- - Line-anchored findings → `onedev_pull` `add_code_comment` (`file`, `from_line`,
46
- optional `to_line` — must be an added or context line visible in the patch).
47
- - Outside-patch findings → one overall PR comment with file paths and lines.
48
- - Review outcome if you are a pending reviewer: `approve` or `request_changes`
49
- with a `summary`.
50
-
51
- 4. **STOP — present the draft** (findings, replies, outcomes, merge plan).
52
- Submit nothing yet.
53
-
54
- 5. **On explicit request only:**
55
- - Post findings/comments/review outcome.
56
- - Merge: the toolkit asks for real interactive approval. For squash merges,
57
- compose the commit message from PR title/description + fixed issues,
58
- satisfying `commit_message_requirement` for the target project/branch.
59
- - Resolving merge conflicts: fetch the target branch, merge with the PR's
60
- strategy without committing, resolve, verify clean `git status`, commit.
61
- For rebase-based strategies, ask for direction before rewriting commits.
62
-
63
- 6. **When waiting on updates**, activate `watch` and use `onedev_watch`
64
- `watch_pull_attention`. It wakes once for a head/state/review/comment/build
65
- signal change and includes no comment bodies; re-read the PR after it fires.
66
-
67
- ## Notes
68
-
69
- - Comment text is Markdown; entity refs are `#123`, `project#123`, or `PROJ-123`.
70
- - Toolkit mutations still open Pi's interactive approval UI. Do not add or invent
71
- a confirmation parameter.
28
+ Use Markdown and `#123`, `project#123`, or `PROJ-123` refs. Never invent a confirmation parameter.
@@ -6,6 +6,9 @@ import { StringEnum } from "@earendil-works/pi-ai";
6
6
  import { Type, type Static } from "typebox";
7
7
  import {
8
8
  confirmMutation,
9
+ DEFAULT_LARGE_MODEL_OUTPUT_BYTES,
10
+ modelOutputBytes,
11
+ modelOutputLimit,
9
12
  pushRepeated,
10
13
  QUERY_DESCRIPTION,
11
14
  REF_DESCRIPTION,
@@ -59,6 +62,7 @@ export interface BuildParams {
59
62
  query?: string;
60
63
  count?: number;
61
64
  offset?: number;
65
+ max_bytes?: number;
62
66
  }
63
67
 
64
68
  export function buildBuildArgs(params: BuildParams): string[] {
@@ -154,6 +158,7 @@ async function runLocalBuildWithIsolatedIndex(
154
158
  deps: ToolDeps,
155
159
  args: string[],
156
160
  signal?: AbortSignal,
161
+ maxOutputBytes?: number,
157
162
  ): Promise<TodOutput> {
158
163
  if (process.platform === "win32") {
159
164
  throw new TodError(
@@ -183,7 +188,7 @@ async function runLocalBuildWithIsolatedIndex(
183
188
  cwd: context.cwd,
184
189
  signal,
185
190
  timeoutMs: BUILD_RUN_TIMEOUT_MS,
186
- maxOutputBytes: 24_000,
191
+ maxOutputBytes: modelOutputLimit(maxOutputBytes, 24_000),
187
192
  environment: { GIT_INDEX_FILE: isolatedIndex },
188
193
  });
189
194
  } catch (error) {
@@ -201,51 +206,33 @@ async function runLocalBuildWithIsolatedIndex(
201
206
 
202
207
  export function registerBuildTool(pi: ExtensionAPI, deps: ToolDeps): void {
203
208
  const parameters = Type.Object({
204
- action: StringEnum(BUILD_ACTIONS, {
205
- description:
206
- "list, get, get_log, get_code_problems, get_unit_test_report, get_changes_since_success, run, check_spec (may upgrade .onedev-buildspec.yml), get_spec_schema, query_description",
207
- }),
209
+ action: StringEnum(BUILD_ACTIONS),
208
210
  ref: Type.Optional(Type.String({ description: REF_DESCRIPTION })),
209
- report_name: Type.Optional(
210
- Type.String({ description: "Report name quoted in the build log" }),
211
- ),
211
+ report_name: Type.Optional(Type.String()),
212
212
  severity: Type.Optional(StringEnum(SEVERITY_LEVELS)),
213
- job: Type.Optional(
214
- Type.String({ description: "Job name defined in .onedev-buildspec.yml" }),
215
- ),
213
+ job: Type.Optional(Type.String()),
216
214
  mode: Type.Optional(
217
- StringEnum(RUN_MODES, {
218
- description:
219
- "run: local (uncommitted changes), branch, or tag; default local",
220
- }),
221
- ),
222
- branch: Type.Optional(
223
- Type.String({ description: "run with mode=branch: branch to run against" }),
224
- ),
225
- tag: Type.Optional(
226
- Type.String({ description: "run with mode=tag: tag to run against" }),
215
+ StringEnum(RUN_MODES, { description: "Default: local" }),
227
216
  ),
217
+ branch: Type.Optional(Type.String()),
218
+ tag: Type.Optional(Type.String()),
228
219
  params: Type.Optional(
229
- Type.Array(Type.String(), {
230
- description: 'Job parameters as "key=value", repeatable (-p)',
231
- }),
220
+ Type.Array(Type.String(), { description: '"key=value" parameters' }),
232
221
  ),
233
222
  project: Type.Optional(
234
- Type.String({
235
- description: "Project path for list; defaults to the current project",
236
- }),
223
+ Type.String({ description: "Default: current project" }),
237
224
  ),
238
225
  query: Type.Optional(Type.String({ description: QUERY_DESCRIPTION })),
239
226
  count: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
240
227
  offset: Type.Optional(Type.Integer({ minimum: 0 })),
228
+ max_bytes: modelOutputBytes(),
241
229
  }, { additionalProperties: false });
242
230
  type Params = Static<typeof parameters>;
243
231
 
244
232
  pi.registerTool({
245
233
  name: "onedev_build",
246
234
  label: "OneDev Builds",
247
- description:
248
- "Inspect OneDev CI/CD: query builds, read logs and code-problem reports, diff changes since the last success, validate the build spec, and run jobs (including against uncommitted local changes).",
235
+ description: "Read OneDev builds, logs, reports, and changes; validate or run jobs.",
249
236
  parameters,
250
237
  async execute(_toolCallId, params: Params, signal, _onUpdate, ctx) {
251
238
  const isRun = params.action === "run";
@@ -272,12 +259,24 @@ export function registerBuildTool(pi: ExtensionAPI, deps: ToolDeps): void {
272
259
  (params.mode === undefined && !params.branch && !params.tag));
273
260
  let output: TodOutput;
274
261
  if (isLocalRun) {
275
- output = await runLocalBuildWithIsolatedIndex(deps, args, signal);
262
+ output = await runLocalBuildWithIsolatedIndex(
263
+ deps,
264
+ args,
265
+ signal,
266
+ params.max_bytes,
267
+ );
276
268
  } else {
277
269
  output = await runInContext(deps, args, {
278
270
  signal,
279
271
  timeoutMs: isRun ? BUILD_RUN_TIMEOUT_MS : undefined,
280
- maxOutputBytes: isRun ? 24_000 : undefined,
272
+ maxOutputBytes:
273
+ params.max_bytes ??
274
+ (isRun
275
+ ? 24_000
276
+ : params.action === "get_log" ||
277
+ params.action === "get_spec_schema"
278
+ ? DEFAULT_LARGE_MODEL_OUTPUT_BYTES
279
+ : undefined),
281
280
  });
282
281
  }
283
282
  return toolResult(
@@ -1,4 +1,5 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
2
3
  import type {
3
4
  ContextRefreshOptions,
4
5
  OneDevSessionContext,
@@ -24,12 +25,32 @@ export interface ToolDeps {
24
25
  approvals: MutationApprovalManager;
25
26
  }
26
27
 
28
+ export const DEFAULT_MODEL_OUTPUT_BYTES = 16_000;
29
+ export const DEFAULT_LARGE_MODEL_OUTPUT_BYTES = 32_000;
30
+ export const MAX_MODEL_OUTPUT_BYTES = 128_000;
31
+
27
32
  export const REF_DESCRIPTION =
28
- "OneDev reference: plain number (123), #number, project#number (myproject#123), or issue key (PROJ-123). Plain numbers resolve against the current repository's project.";
33
+ "Ref: 123, #123, project#123, or PROJ-123; plain numbers use the current project.";
29
34
  export const FIELDS_DESCRIPTION =
30
- 'Issue field assignments as "key=value" strings, repeatable. Discover valid fields and values with the valid_fields action.';
35
+ '"key=value" assignments; use valid_fields to discover values.';
31
36
  export const QUERY_DESCRIPTION =
32
- "OneDev query, e.g. '\"State\" is \"Open\"' or 'assignee is me'. Syntax reference: query_description action.";
37
+ "OneDev query; use query_description for syntax.";
38
+
39
+ export function modelOutputBytes() {
40
+ return Type.Optional(
41
+ Type.Integer({ minimum: 1_000, maximum: MAX_MODEL_OUTPUT_BYTES }),
42
+ );
43
+ }
44
+
45
+ export function modelOutputLimit(
46
+ requested: number | undefined,
47
+ fallback = DEFAULT_MODEL_OUTPUT_BYTES,
48
+ ): number {
49
+ return Math.min(
50
+ MAX_MODEL_OUTPUT_BYTES,
51
+ Math.max(1_000, requested ?? fallback),
52
+ );
53
+ }
33
54
 
34
55
  export function toolResult(
35
56
  text: string,
@@ -65,7 +86,7 @@ export async function runInContext(
65
86
  cwd: context.cwd,
66
87
  signal: options.signal,
67
88
  timeoutMs: options.timeoutMs,
68
- maxOutputBytes: options.maxOutputBytes,
89
+ maxOutputBytes: modelOutputLimit(options.maxOutputBytes),
69
90
  });
70
91
  }
71
92
 
@@ -111,7 +132,7 @@ export function withFooter(
111
132
  ): string {
112
133
  const parts = [body || "(no output)"];
113
134
  if (truncated)
114
- parts.push("(output truncated; narrow the query or fetch a single entity)");
135
+ parts.push("(output truncated; narrow the query or raise max_bytes)");
115
136
  parts.push(contextFooter(context));
116
137
  return parts.filter(Boolean).join("\n\n");
117
138
  }
@@ -43,8 +43,7 @@ function registerContextTool(pi: ExtensionAPI, deps: ToolDeps): void {
43
43
  pi.registerTool({
44
44
  name: "onedev_context",
45
45
  label: "OneDev Context",
46
- description:
47
- "Inspect OneDev readiness without loading domain tools. Use health for an authenticated re-check and setup for actionable configuration guidance.",
46
+ description: "Inspect OneDev readiness, health, login, or setup.",
48
47
  parameters: Type.Object(
49
48
  {
50
49
  action: StringEnum(["current", "health", "login_name", "setup"]),
@@ -85,8 +84,7 @@ export function registerOnedevTools(
85
84
  pi.registerTool({
86
85
  name: "onedev_tools",
87
86
  label: "OneDev Tools",
88
- description:
89
- "Activate only the OneDev domains needed for the current task. Returns authenticated server, project, and login context with the activation result.",
87
+ description: "Activate needed OneDev domains and return authenticated context.",
90
88
  promptGuidelines: [
91
89
  "Use onedev_tools before an unavailable OneDev domain operation.",
92
90
  ],
@@ -97,7 +95,6 @@ export function registerOnedevTools(
97
95
  minItems: 1,
98
96
  maxItems: 4,
99
97
  uniqueItems: true,
100
- description: "Activate issue, pull, build, and/or watch in one call",
101
98
  }),
102
99
  },
103
100
  { additionalProperties: false },
@@ -4,6 +4,7 @@ import { Type, type Static } from "typebox";
4
4
  import {
5
5
  FIELDS_DESCRIPTION,
6
6
  confirmMutation,
7
+ modelOutputBytes,
7
8
  pushRepeated,
8
9
  QUERY_DESCRIPTION,
9
10
  REF_DESCRIPTION,
@@ -65,6 +66,7 @@ export interface IssueParams {
65
66
  query?: string;
66
67
  count?: number;
67
68
  offset?: number;
69
+ max_bytes?: number;
68
70
  }
69
71
 
70
72
  export function buildIssueArgs(params: IssueParams): string[] {
@@ -161,66 +163,51 @@ export function buildIssueArgs(params: IssueParams): string[] {
161
163
 
162
164
  export function registerIssueTool(pi: ExtensionAPI, deps: ToolDeps): void {
163
165
  const parameters = Type.Object({
164
- action: StringEnum(ISSUE_ACTIONS, {
165
- description:
166
- "list (query issues), get, get_comments, create, edit, change_state, add_comment, link, log_work, create_branch, checkout (branch locally), current_reference (issue for current branch), valid_fields, valid_links, query_description",
167
- }),
166
+ action: StringEnum(ISSUE_ACTIONS),
168
167
  ref: Type.Optional(Type.String({ description: REF_DESCRIPTION })),
169
168
  title: Type.Optional(Type.String()),
170
- description: Type.Optional(Type.String({ description: "Markdown body" })),
169
+ description: Type.Optional(Type.String()),
171
170
  state: Type.Optional(
172
- Type.String({
173
- description:
174
- "Target state, e.g. Open or Released; discover via valid_fields",
175
- }),
176
- ),
177
- comment: Type.Optional(
178
- Type.String({ description: "Markdown note attached to the action" }),
171
+ Type.String({ description: "Use valid_fields to discover states" }),
179
172
  ),
180
- content: Type.Optional(Type.String({ description: "Markdown comment text" })),
173
+ comment: Type.Optional(Type.String()),
174
+ content: Type.Optional(Type.String()),
181
175
  link_name: Type.Optional(
182
- Type.String({ description: "Issue link name; discover via valid_links" }),
183
- ),
184
- target_ref: Type.Optional(
185
- Type.String({ description: "Target issue reference for the link action" }),
186
- ),
187
- hours: Type.Optional(
188
- Type.Integer({ minimum: 1, description: "Hours to log for log_work" }),
176
+ Type.String({ description: "Use valid_links to discover names" }),
189
177
  ),
178
+ target_ref: Type.Optional(Type.String()),
179
+ hours: Type.Optional(Type.Integer({ minimum: 1 })),
190
180
  fields: Type.Optional(
191
181
  Type.Array(Type.String(), { description: FIELDS_DESCRIPTION }),
192
182
  ),
193
183
  iterations: Type.Optional(Type.Array(Type.String())),
194
184
  own_estimated_time: Type.Optional(
195
- Type.Number({ minimum: 0, description: "Estimated hours" }),
185
+ Type.Number({ minimum: 0, description: "Hours" }),
196
186
  ),
197
187
  confidential: Type.Optional(Type.Boolean()),
198
188
  for_write: Type.Optional(
199
- Type.Boolean({ description: "checkout: set up the branch for pushing" }),
189
+ Type.Boolean({ description: "Prepare checkout for push" }),
200
190
  ),
201
191
  project: Type.Optional(
202
- Type.String({
203
- description:
204
- "Project path for list/create; defaults to the current project",
205
- }),
192
+ Type.String({ description: "Default: current project" }),
206
193
  ),
207
194
  query: Type.Optional(Type.String({ description: QUERY_DESCRIPTION })),
208
195
  count: Type.Optional(
209
196
  Type.Integer({
210
197
  minimum: 1,
211
198
  maximum: 100,
212
- description: "Page size for list (default 25, max 100)",
199
+ description: "Default: 25",
213
200
  }),
214
201
  ),
215
202
  offset: Type.Optional(Type.Integer({ minimum: 0 })),
203
+ max_bytes: modelOutputBytes(),
216
204
  }, { additionalProperties: false });
217
205
  type Params = Static<typeof parameters>;
218
206
 
219
207
  pi.registerTool({
220
208
  name: "onedev_issue",
221
209
  label: "OneDev Issues",
222
- description:
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.",
210
+ description: "Read/manage OneDev issues, comments, fields, work, and branches.",
224
211
  parameters,
225
212
  async execute(_toolCallId, params: Params, signal, _onUpdate, ctx) {
226
213
  const args = buildIssueArgs(params);
@@ -236,7 +223,10 @@ export function registerIssueTool(pi: ExtensionAPI, deps: ToolDeps): void {
236
223
  signal,
237
224
  });
238
225
  }
239
- const output = await runInContext(deps, args, { signal });
226
+ const output = await runInContext(deps, args, {
227
+ signal,
228
+ maxOutputBytes: params.max_bytes,
229
+ });
240
230
  return toolResult(
241
231
  withFooter(output.text, deps.context(), output.truncated),
242
232
  {
package/src/tools/pull.ts CHANGED
@@ -3,6 +3,8 @@ import { StringEnum } from "@earendil-works/pi-ai";
3
3
  import { Type, type Static } from "typebox";
4
4
  import {
5
5
  confirmMutation,
6
+ DEFAULT_LARGE_MODEL_OUTPUT_BYTES,
7
+ modelOutputBytes,
6
8
  pushRepeated,
7
9
  QUERY_DESCRIPTION,
8
10
  REF_DESCRIPTION,
@@ -86,6 +88,7 @@ export interface PullParams {
86
88
  query?: string;
87
89
  count?: number;
88
90
  offset?: number;
91
+ max_bytes?: number;
89
92
  }
90
93
 
91
94
  export function buildPullArgs(params: PullParams): string[] {
@@ -232,92 +235,53 @@ export function buildPullArgs(params: PullParams): string[] {
232
235
 
233
236
  export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
234
237
  const parameters = Type.Object({
235
- action: StringEnum(PULL_ACTIONS, {
236
- description:
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",
238
- }),
238
+ action: StringEnum(PULL_ACTIONS),
239
239
  ref: Type.Optional(Type.String({ description: REF_DESCRIPTION })),
240
240
  title: Type.Optional(Type.String()),
241
- description: Type.Optional(Type.String({ description: "Markdown body" })),
242
- content: Type.Optional(Type.String({ description: "Markdown comment text" })),
243
- summary: Type.Optional(
244
- Type.String({
245
- description: "Markdown review summary for approve/request_changes",
246
- }),
247
- ),
248
- comment: Type.Optional(
249
- Type.String({ description: "Markdown note for discard" }),
250
- ),
251
- commit_message: Type.Optional(
252
- Type.String({
253
- description: "Commit message for merge (required by squash merges)",
254
- }),
255
- ),
256
- file: Type.Optional(
257
- Type.String({ description: "add_code_comment: file path in the PR patch" }),
258
- ),
241
+ description: Type.Optional(Type.String()),
242
+ content: Type.Optional(Type.String()),
243
+ summary: Type.Optional(Type.String()),
244
+ comment: Type.Optional(Type.String()),
245
+ commit_message: Type.Optional(Type.String()),
246
+ file: Type.Optional(Type.String()),
259
247
  from_line: Type.Optional(
260
- Type.Integer({
261
- minimum: 1,
262
- description: "add_code_comment: 1-based start line (right side of patch)",
263
- }),
264
- ),
265
- to_line: Type.Optional(
266
- Type.Integer({
267
- minimum: 1,
268
- description: "add_code_comment: 1-based end line; defaults to from_line",
269
- }),
270
- ),
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
- }),
248
+ Type.Integer({ minimum: 1, description: "Right-side line" }),
277
249
  ),
250
+ to_line: Type.Optional(Type.Integer({ minimum: 1 })),
251
+ comment_id: Type.Optional(Type.Integer({ minimum: 1 })),
278
252
  source_branch: Type.Optional(Type.String()),
279
253
  target_branch: Type.Optional(Type.String()),
280
254
  source_project: Type.Optional(Type.String()),
281
255
  target_project: Type.Optional(Type.String()),
282
- assignees: Type.Optional(
283
- Type.Array(Type.String(), { description: "User logins, repeatable" }),
284
- ),
285
- reviewers: Type.Optional(
286
- Type.Array(Type.String(), { description: "User logins, repeatable" }),
287
- ),
288
- remove_reviewers: Type.Optional(
289
- Type.Array(Type.String(), { description: "edit: user logins to remove" }),
290
- ),
256
+ assignees: Type.Optional(Type.Array(Type.String())),
257
+ reviewers: Type.Optional(Type.Array(Type.String())),
258
+ remove_reviewers: Type.Optional(Type.Array(Type.String())),
291
259
  labels: Type.Optional(
292
- Type.Array(Type.String(), {
293
- description: "Label names; discover via valid_labels",
294
- }),
260
+ Type.Array(Type.String(), { description: "Use valid_labels" }),
295
261
  ),
296
262
  merge_strategy: Type.Optional(Type.String()),
297
263
  auto_merge: Type.Optional(Type.Boolean()),
298
264
  auto_merge_commit_message: Type.Optional(Type.String()),
299
265
  for_code_review: Type.Optional(
300
- Type.Boolean({ description: "get_patch: review-scoped patch" }),
266
+ Type.Boolean({ description: "Review-scoped patch" }),
301
267
  ),
302
268
  for_write: Type.Optional(
303
- Type.Boolean({ description: "checkout: set up the branch for pushing" }),
269
+ Type.Boolean({ description: "Prepare checkout for push" }),
304
270
  ),
305
271
  project: Type.Optional(
306
- Type.String({
307
- description: "Project path for list; defaults to the current project",
308
- }),
272
+ Type.String({ description: "Default: current project" }),
309
273
  ),
310
274
  query: Type.Optional(Type.String({ description: QUERY_DESCRIPTION })),
311
275
  count: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
312
276
  offset: Type.Optional(Type.Integer({ minimum: 0 })),
277
+ max_bytes: modelOutputBytes(),
313
278
  }, { additionalProperties: false });
314
279
  type Params = Static<typeof parameters>;
315
280
 
316
281
  pi.registerTool({
317
282
  name: "onedev_pull",
318
283
  label: "OneDev Pull Requests",
319
- description:
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.",
284
+ description: "Read/manage OneDev PRs, patch, comments, reviews, and guarded merge.",
321
285
  parameters,
322
286
  async execute(_toolCallId, params: Params, signal, _onUpdate, ctx) {
323
287
  const args = buildPullArgs(params);
@@ -334,7 +298,14 @@ export function registerPullTool(pi: ExtensionAPI, deps: ToolDeps): void {
334
298
  signal,
335
299
  });
336
300
  }
337
- const output = await runInContext(deps, args, { signal });
301
+ const output = await runInContext(deps, args, {
302
+ signal,
303
+ maxOutputBytes:
304
+ params.max_bytes ??
305
+ (params.action === "get_patch"
306
+ ? DEFAULT_LARGE_MODEL_OUTPUT_BYTES
307
+ : undefined),
308
+ });
338
309
  return toolResult(
339
310
  withFooter(output.text, deps.context(), output.truncated),
340
311
  {
@@ -11,8 +11,7 @@ export function registerWatchTool(
11
11
  pi.registerTool({
12
12
  name: "onedev_watch",
13
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.",
14
+ description: "Start/list/stop one-shot build or PR-attention watches.",
16
15
  parameters: Type.Object(
17
16
  {
18
17
  action: StringEnum([
@@ -21,12 +20,8 @@ export function registerWatchTool(
21
20
  "list",
22
21
  "stop",
23
22
  ]),
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
- ),
23
+ ref: Type.Optional(Type.String()),
24
+ watch_id: Type.Optional(Type.String()),
30
25
  interval_seconds: Type.Optional(
31
26
  Type.Integer({ minimum: 15, maximum: 300, default: 30 }),
32
27
  ),