@oh-my-pi/pi-coding-agent 13.0.2 → 13.1.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 CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [13.1.0] - 2026-02-23
6
+ ### Breaking Changes
7
+
8
+ - Renamed `file` parameter to `path` in replace, patch, and hashline edit operations
9
+
10
+ ### Added
11
+
12
+ - Added clarification in hashline edit documentation that the `end` tag must include closing braces/brackets when replacing blocks to prevent syntax errors
13
+
14
+ ### Changed
15
+
16
+ - Restructured task tool documentation for clarity, moving parameter definitions into a dedicated section and consolidating guidance on context, assignments, and parallelization
17
+ - Reformatted system prompt template to use markdown headings instead of XML tags for skills, preloaded skills, and rules sections
18
+ - Renamed `deviceScaleFactor` parameter to `device_scale_factor` in browser viewport configuration for consistency with snake_case naming convention
19
+ - Moved intent field documentation from per-tool JSON schema descriptions into a single system prompt block, reducing token overhead proportional to tool count
20
+
5
21
  ## [13.0.1] - 2026-02-22
6
22
  ### Changed
7
23
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "13.0.2",
4
+ "version": "13.1.0",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -41,12 +41,12 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@mozilla/readability": "^0.6",
44
- "@oh-my-pi/omp-stats": "13.0.2",
45
- "@oh-my-pi/pi-agent-core": "13.0.2",
46
- "@oh-my-pi/pi-ai": "13.0.2",
47
- "@oh-my-pi/pi-natives": "13.0.2",
48
- "@oh-my-pi/pi-tui": "13.0.2",
49
- "@oh-my-pi/pi-utils": "13.0.2",
44
+ "@oh-my-pi/omp-stats": "13.1.0",
45
+ "@oh-my-pi/pi-agent-core": "13.1.0",
46
+ "@oh-my-pi/pi-ai": "13.1.0",
47
+ "@oh-my-pi/pi-natives": "13.1.0",
48
+ "@oh-my-pi/pi-tui": "13.1.0",
49
+ "@oh-my-pi/pi-utils": "13.1.0",
50
50
  "@sinclair/typebox": "^0.34",
51
51
  "@xterm/headless": "^6.0",
52
52
  "ajv": "^8.18",
@@ -108,14 +108,14 @@ export { ApplyPatchError, EditMatchError, ParseError } from "./types";
108
108
  // ═══════════════════════════════════════════════════════════════════════════
109
109
 
110
110
  const replaceEditSchema = Type.Object({
111
- file: Type.String({ description: "File path (relative or absolute)" }),
111
+ path: Type.String({ description: "File path (relative or absolute)" }),
112
112
  old_text: Type.String({ description: "Text to find (fuzzy whitespace matching enabled)" }),
113
113
  new_text: Type.String({ description: "Replacement text" }),
114
114
  all: Type.Optional(Type.Boolean({ description: "Replace all occurrences (default: unique match required)" })),
115
115
  });
116
116
 
117
117
  const patchEditSchema = Type.Object({
118
- file: Type.String({ description: "File path" }),
118
+ path: Type.String({ description: "File path" }),
119
119
  op: Type.Optional(
120
120
  StringEnum(["create", "delete", "update"], {
121
121
  description: "Operation (default: update)",
@@ -192,10 +192,10 @@ const hashlineEditSchema = Type.Object(
192
192
 
193
193
  const hashlineEditParamsSchema = Type.Object(
194
194
  {
195
- file: Type.String({ description: "path" }),
196
- edits: Type.Array(hashlineEditSchema, { description: "edits over $file" }),
197
- delete: Type.Optional(Type.Boolean({ description: "If true, delete $file" })),
198
- move: Type.Optional(Type.String({ description: "If set, move $file to $move" })),
195
+ path: Type.String({ description: "path" }),
196
+ edits: Type.Array(hashlineEditSchema, { description: "edits over $path" }),
197
+ delete: Type.Optional(Type.Boolean({ description: "If true, delete $path" })),
198
+ move: Type.Optional(Type.String({ description: "If set, move $path to $move" })),
199
199
  },
200
200
  { additionalProperties: false },
201
201
  );
@@ -489,7 +489,7 @@ export class EditTool implements AgentTool<TInput> {
489
489
  // Hashline mode execution
490
490
  // ─────────────────────────────────────────────────────────────────
491
491
  if (this.mode === "hashline") {
492
- const { file: path, edits, delete: deleteFile, move } = params as HashlineParams;
492
+ const { path, edits, delete: deleteFile, move } = params as HashlineParams;
493
493
 
494
494
  enforcePlanModeWrite(this.session, path, { op: deleteFile ? "delete" : "update", move });
495
495
 
@@ -659,7 +659,7 @@ export class EditTool implements AgentTool<TInput> {
659
659
  // Patch mode execution
660
660
  // ─────────────────────────────────────────────────────────────────
661
661
  if (this.mode === "patch") {
662
- const { file: path, op: rawOp, rename, diff } = params as PatchParams;
662
+ const { path, op: rawOp, rename, diff } = params as PatchParams;
663
663
 
664
664
  // Normalize unrecognized operations to "update"
665
665
  const op: Operation = rawOp === "create" || rawOp === "delete" ? rawOp : "update";
@@ -741,7 +741,7 @@ export class EditTool implements AgentTool<TInput> {
741
741
  // ─────────────────────────────────────────────────────────────────
742
742
  // Replace mode execution
743
743
  // ─────────────────────────────────────────────────────────────────
744
- const { file: path, old_text, new_text, all } = params as ReplaceParams;
744
+ const { path, old_text, new_text, all } = params as ReplaceParams;
745
745
 
746
746
  enforcePlanModeWrite(this.session, path);
747
747
 
@@ -71,7 +71,6 @@ export interface EditToolDetails {
71
71
  interface EditRenderArgs {
72
72
  path?: string;
73
73
  file_path?: string;
74
- file?: string;
75
74
  oldText?: string;
76
75
  newText?: string;
77
76
  patch?: string;
@@ -220,7 +219,7 @@ export const editToolRenderer = {
220
219
  mergeCallAndResult: true,
221
220
 
222
221
  renderCall(args: EditRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
223
- const rawPath = args.file_path || args.path || args.file || "";
222
+ const rawPath = args.file_path || args.path || "";
224
223
  const filePath = shortenPath(rawPath);
225
224
  const editLanguage = getLanguageFromPath(rawPath) ?? "text";
226
225
  const editIcon = uiTheme.fg("muted", uiTheme.getLangIcon(editLanguage));
@@ -275,7 +274,7 @@ export const editToolRenderer = {
275
274
  uiTheme: Theme,
276
275
  args?: EditRenderArgs,
277
276
  ): Component {
278
- const rawPath = args?.file_path || args?.path || args?.file || "";
277
+ const rawPath = args?.file_path || args?.path || "";
279
278
  const filePath = shortenPath(rawPath);
280
279
  const editLanguage = getLanguageFromPath(rawPath) ?? "text";
281
280
  const editIcon = uiTheme.fg("muted", uiTheme.getLangIcon(editLanguage));
@@ -100,6 +100,10 @@ You MUST NOT open a file hoping. Hope is not a strategy.
100
100
  {{#has tools "grep"}}- Known territory → `grep` to locate target{{/has}}
101
101
  {{#has tools "read"}}- Known location → `read` with offset/limit, not whole file{{/has}}
102
102
  {{/ifAny}}
103
+
104
+ {{#if intentTracing}}
105
+ Every tool has a required `{{intentField}}` parameter. Describe intent as one sentence in present participle form (e.g., Inserting comment before the function) with no trailing period.
106
+ {{/if}}
103
107
  </tools>
104
108
 
105
109
  <procedure>
@@ -215,30 +219,26 @@ Match skill descriptions to the task domain. If a skill is relevant, you MUST re
215
219
  Relative paths in skill files resolve against the skill directory.
216
220
 
217
221
  {{#list skills join="\n"}}
218
- <skill name="{{name}}">
222
+ ### {{name}}
219
223
  {{description}}
220
- </skill>
221
224
  {{/list}}
222
225
  </skills>
223
226
  {{/if}}
224
227
  {{#if preloadedSkills.length}}
225
- <preloaded-skills>
228
+ <skills>
226
229
  {{#list preloadedSkills join="\n"}}
227
230
  <skill name="{{name}}">
228
231
  {{content}}
229
232
  </skill>
230
233
  {{/list}}
231
- </preloaded-skills>
234
+ </skills>
232
235
  {{/if}}
233
236
  {{#if rules.length}}
234
237
  <rules>
235
238
  Read `rule://<name>` when working in matching domain.
236
-
237
239
  {{#list rules join="\n"}}
238
- <rule name="{{name}}">
240
+ ### {{name}} (Glob: {{#list globs join=", "}}{{this}}{{/list}})
239
241
  {{description}}
240
- {{#list globs join="\n"}}<glob>{{this}}</glob>{{/list}}
241
- </rule>
242
242
  {{/list}}
243
243
  </rules>
244
244
  {{/if}}
@@ -12,33 +12,18 @@ Ask user when you need clarification or input during task execution.
12
12
  - Set `multi: true` on question to allow multiple selections
13
13
  </instruction>
14
14
 
15
- <output>
16
- Returns selected option(s) as text. For multi-part questions, returns map of question IDs to selected values.
17
- </output>
18
-
19
15
  <caution>
20
16
  - Provide 2-5 concise, distinct options
21
17
  </caution>
22
18
 
23
19
  <critical>
24
- **Default to action. You MUST NOT ask unless you are genuinely blocked and user preference is required to avoid a wrong outcome.**
25
- 1. You MUST **resolve ambiguity yourself** using repo conventions, existing patterns, and reasonable defaults.
26
- 2. You MUST **exhaust existing sources** (code, configs, docs, history) before asking anything.
27
- 3. **If multiple choices are acceptable**, you MUST pick the most conservative/standard option and proceed; state the choice.
28
- 4. You MUST **only ask when options have materially different tradeoffs and the user must decide.**
29
- **You MUST NOT include "Other" option in your options array.** UI automatically adds "Other (type your own)" to every question; adding your own creates duplicates.
20
+ - **Default to action.** Resolve ambiguity yourself using repo conventions, existing patterns, and reasonable defaults. Exhaust existing sources (code, configs, docs, history) before asking. Only ask when options have materially different tradeoffs the user must decide.
21
+ - **If multiple choices are acceptable**, pick the most conservative/standard option and proceed; state the choice.
22
+ - **Do NOT include "Other" option** — UI automatically adds "Other (type your own)" to every question.
30
23
  </critical>
31
24
 
32
25
  <example name="single">
33
26
  question: "Which authentication method should this API use?"
34
27
  options: [{"label": "JWT"}, {"label": "OAuth2"}, {"label": "Session cookies"}]
35
28
  recommended: 0
36
- </example>
37
-
38
- <example name="multi-part">
39
- questions: [
40
- {"id": "auth", "question": "Which auth method?", "options": [{"label": "JWT"}, {"label": "OAuth2"}], "recommended": 0},
41
- {"id": "cache", "question": "Enable caching?", "options": [{"label": "Yes"}, {"label": "No"}]},
42
- {"id": "features", "question": "Which features to include?", "options": [{"label": "Logging"}, {"label": "Metrics"}, {"label": "Tracing"}], "multi": true}
43
- ]
44
29
  </example>
@@ -1,4 +1,4 @@
1
- # Poll Jobs
1
+ # Await
2
2
 
3
3
  Block until one or more background jobs complete, fail, or are cancelled.
4
4
 
@@ -13,17 +13,18 @@ Executes bash command in shell session for terminal operations like git, bun, ca
13
13
  {{#if asyncEnabled}}
14
14
  - Use `async: true` for long-running commands when you don't need immediate output; the call returns a background job ID and the result is delivered automatically as a follow-up.
15
15
  - Use `read jobs://` to inspect all background jobs and `read jobs://<job-id>` for detailed status/output when needed.
16
- - When you need to wait for async results before continuing, you MUST call `poll_jobs` — it blocks until jobs complete. You MUST NOT poll `read jobs://` in a loop or yield and hope for delivery.
16
+ - When you need to wait for async results before continuing, call `await` — it blocks until jobs complete. Do NOT poll `read jobs://` in a loop or yield and hope for delivery.
17
17
  {{/if}}
18
18
  </instruction>
19
19
 
20
20
  <output>
21
21
  Returns the output, and an exit code from command execution.
22
+ - If output truncated, full output can be retrieved from `artifact://<id>`, linked in metadata
22
23
  - Exit codes shown on non-zero exit
23
24
  </output>
24
25
 
25
26
  <critical>
26
27
  - You MUST NOT use Bash for these operations like read, grep, find, edit, write, where specialized tools exist.
27
- - You MUST NOT use `2>&1` | `2>/dev/null` pattern, stdout and stderr are already merged.
28
+ - You MUST NOT use `2>&1` pattern, stdout and stderr are already merged.
28
29
  - You MUST NOT use `| head -n 50` or `| tail -n 100` pattern, use `head` and `tail` parameters instead.
29
30
  </critical>
@@ -1,24 +1,17 @@
1
1
  # Browser
2
2
 
3
- Use this tool to navigate, click, type, scroll, drag, query DOM content, and capture screenshots.
3
+ Navigate, click, type, scroll, drag, query DOM content, and capture screenshots.
4
4
 
5
5
  <instruction>
6
- - Use `action: "open"` to start a new headless browser session (or implicitly launch on first action)
7
- - Use `action: "goto"` with `url` to navigate
8
- - Use `action: "observe"` to capture a numbered accessibility snapshot with URL/title/viewport/scroll info
9
- - You SHOULD prefer `click_id`, `type_id`, or `fill_id` actions using the returned `element_id` values
10
- - Optional flags: `include_all` to include non-interactive nodes, `viewport_only` to limit to visible elements
11
- - Use `action: "click"`, `"type"`, `"fill"`, `"press"`, `"scroll"`, or `"drag"` for selector-based interactions
12
- - You SHOULD prefer ARIA or text selectors (e.g. `p-aria/[name="Sign in"]`, `p-text/Continue`) over brittle CSS
13
- - Use `action: "click_id"`, `"type_id"`, or `"fill_id"` to interact with observed elements without selectors
14
- - Use `action: "wait_for_selector"` before interacting when the page is dynamic
15
- - Use `action: "evaluate"` with `script` to run a JavaScript expression in the page context
16
- - Use `action: "get_text"`, `"get_html"`, or `"get_attribute"` for DOM queries
17
- - For batch queries, pass `args: [{ selector, attribute? }]` to get an array of results (attribute required for `get_attribute`)
18
- - Use `action: "extract_readable"` to return reader-mode content (title/byline/excerpt/text or markdown)
19
- - Set `format` to `"markdown"` (default) or `"text"`
20
- - Use `action: "screenshot"` to capture images (optionally with `selector` to capture a single element)
21
- - Use `action: "close"` to release the browser when done
6
+ - `"open"` starts a headless session (or implicitly on first action); `"goto"` navigates to `url`; `"close"` releases the browser
7
+ - `"observe"` captures a numbered accessibility snapshot — prefer `click_id`/`type_id`/`fill_id` using returned `element_id` values; flags: `include_all`, `viewport_only`
8
+ - `"click"`, `"type"`, `"fill"`, `"press"`, `"scroll"`, `"drag"` for selector-based interactions prefer ARIA/text selectors (`p-aria/[name="Sign in"]`, `p-text/Continue`) over brittle CSS
9
+ - `"click_id"`, `"type_id"`, `"fill_id"` to interact with observed elements without selectors
10
+ - `"wait_for_selector"` before interacting when the page is dynamic
11
+ - `"evaluate"` runs a JS expression in page context
12
+ - `"get_text"`, `"get_html"`, `"get_attribute"` for DOM queries batch via `args: [{ selector, attribute? }]`
13
+ - `"extract_readable"` returns reader-mode content; `format`: `"markdown"` (default) or `"text"`
14
+ - `"screenshot"` captures images (optionally with `selector`); can save to disk via `path`
22
15
  </instruction>
23
16
 
24
17
  <critical>
@@ -29,5 +22,5 @@ Use this tool to navigate, click, type, scroll, drag, query DOM content, and cap
29
22
  </critical>
30
23
 
31
24
  <output>
32
- Returns text output for navigation and DOM queries, and image output for screenshots. Screenshots can optionally be saved to disk via the `path` parameter.
25
+ Text for navigation/DOM queries, images for screenshots.
33
26
  </output>
@@ -3,14 +3,11 @@
3
3
  Retrieves content from a URL and returns it in a clean, readable format.
4
4
 
5
5
  <instruction>
6
- - Extract information from web pages (documentation, articles, API references)
7
- - Analyze GitHub issues, PRs, or repository content
8
- - Retrieve from Stack Overflow, Wikipedia, Reddit, NPM, arXiv, technical blogs
9
- - Access RSS/Atom feeds or JSON endpoints
6
+ - Extract information from web pages, GitHub issues/PRs, Stack Overflow, Wikipedia, Reddit, NPM, arXiv, technical blogs, RSS/Atom feeds, JSON endpoints
10
7
  - Read PDF or DOCX files hosted at a URL
11
8
  - Use `raw: true` for untouched HTML or debugging
12
9
  </instruction>
13
10
 
14
11
  <output>
15
- Returns processed, readable content extracted from the URL. HTML is transformed to remove navigation, ads, and boilerplate. PDF and DOCX files are converted to text. JSON endpoints return formatted JSON. With `raw: true`, returns untransformed HTML.
12
+ Returns processed, readable content. HTML transformed to remove boilerplate. PDF/DOCX converted to text. JSON returned formatted. With `raw: true`, returns untransformed HTML.
16
13
  </output>
@@ -10,7 +10,7 @@ Fast file pattern matching that works with any codebase size.
10
10
  </instruction>
11
11
 
12
12
  <output>
13
- Matching file paths sorted by modification time (most recent first). Results truncated at 1000 entries or 50KB (configurable via `limit`).
13
+ Matching file paths sorted by modification time (most recent first). Truncated at 1000 entries or 50KB (configurable via `limit`).
14
14
  </output>
15
15
 
16
16
  <example name="find files">
@@ -3,16 +3,13 @@
3
3
  Powerful search tool built on ripgrep.
4
4
 
5
5
  <instruction>
6
- - Supports full regex syntax (e.g., `log.*Error`, `function\\s+\\w+`)
6
+ - Supports full regex syntax (e.g., `log.*Error`, `function\\s+\\w+`); literal braces need escaping (`interface\\{\\}` for `interface{}` in Go)
7
7
  - Filter files with `glob` (e.g., `*.js`, `**/*.tsx`) or `type` (e.g., `js`, `py`, `rust`)
8
- - Pattern syntax uses ripgrep—literal braces need escaping (`interface\\{\\}` to find `interface{}` in Go)
9
8
  - For cross-line patterns like `struct \\{[\\s\\S]*?field`, set `multiline: true` if needed
10
9
  - If the pattern contains a literal `\n`, multiline defaults to true
11
10
  </instruction>
12
11
 
13
12
  <output>
14
- - Results are always content mode.
15
- - Results grouped by directory (`# dir`) and file (`## └─ file`) headings
16
13
  {{#if IS_HASHLINE_MODE}}
17
14
  - Text output is CID prefixed: `LINE#ID:content`
18
15
  {{else}}
@@ -22,20 +22,20 @@ Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `p
22
22
  - `null` or `[]` — **delete** the line(s) entirely
23
23
 
24
24
  ### Line or range replace/delete
25
- - `{ file: "…", edits: [{ op: "replace", pos: "N#ID", lines: null }] }` — delete one line
26
- - `{ file: "…", edits: [{ op: "replace", pos: "N#ID", end: "M#ID", lines: null }] }` — delete a range
27
- - `{ file: "…", edits: [{ op: "replace", pos: "N#ID", lines: […] }] }` — replace one line
28
- - `{ file: "…", edits: [{ op: "replace", pos: "N#ID", end: "M#ID", lines: […] }] }` — replace a range
25
+ - `{ path: "…", edits: [{ op: "replace", pos: "N#ID", lines: null }] }` — delete one line
26
+ - `{ path: "…", edits: [{ op: "replace", pos: "N#ID", end: "M#ID", lines: null }] }` — delete a range
27
+ - `{ path: "…", edits: [{ op: "replace", pos: "N#ID", lines: […] }] }` — replace one line
28
+ - `{ path: "…", edits: [{ op: "replace", pos: "N#ID", end: "M#ID", lines: […] }] }` — replace a range
29
29
 
30
30
  ### Insert new lines
31
- - `{ file: "…", edits: [{ op: "prepend", pos: "N#ID", lines: […] }] }` — insert before tagged line
32
- - `{ file: "…", edits: [{ op: "prepend", lines: […] }] }` — insert at beginning of file (no tag)
33
- - `{ file: "…", edits: [{ op: "append", pos: "N#ID", lines: […] }] }` — insert after tagged line
34
- - `{ file: "…", edits: [{ op: "append", lines: […] }] }` — insert at end of file (no tag)
31
+ - `{ path: "…", edits: [{ op: "prepend", pos: "N#ID", lines: […] }] }` — insert before tagged line
32
+ - `{ path: "…", edits: [{ op: "prepend", lines: […] }] }` — insert at beginning of file (no tag)
33
+ - `{ path: "…", edits: [{ op: "append", pos: "N#ID", lines: […] }] }` — insert after tagged line
34
+ - `{ path: "…", edits: [{ op: "append", lines: […] }] }` — insert at end of file (no tag)
35
35
 
36
36
  ### File-level controls
37
- - `{ file: "…", delete: true, edits: [] }` — delete the file
38
- - `{ file: "…", move: "new/path.ts", edits: […] }` — move file to new path (edits applied first)
37
+ - `{ path: "…", delete: true, edits: [] }` — delete the file
38
+ - `{ path: "…", move: "new/path.ts", edits: […] }` — move file to new path (edits applied first)
39
39
  **Atomicity:** all ops in one call validate against the same pre-edit snapshot; tags reference the last `read`. Edits are applied bottom-up, so earlier tags stay valid even when later ops add or remove lines.
40
40
  </operations>
41
41
 
@@ -44,6 +44,7 @@ Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `p
44
44
  2. **No no-ops:** replacement MUST differ from current.
45
45
  3. **Prefer insertion over neighbor rewrites:** You SHOULD anchor on structural boundaries (`}`, `]`, `},`), not interior lines.
46
46
  4. **For swaps/moves:** You SHOULD prefer one range op over multiple single-line ops.
47
+ 5. **Range end tag:** When replacing a block (e.g., an `if` body), the `end` tag MUST include the block's closing brace/bracket — not just the last interior line. Verify the `end` tag covers all lines being logically removed, including trailing `}`, `]`, or `)`. An off-by-one on `end` orphans a brace and breaks syntax.
47
48
  </rules>
48
49
 
49
50
  <recovery>
@@ -57,7 +58,7 @@ Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `p
57
58
  ```
58
59
  ```
59
60
  {
60
- file: "…",
61
+ path: "…",
61
62
  edits: [{
62
63
  op: "replace",
63
64
  pos: "{{hlineref 23 " const timeout: number = 5000;"}}",
@@ -71,7 +72,7 @@ Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `p
71
72
  Single line — `lines: null` deletes entirely:
72
73
  ```
73
74
  {
74
- file: "…",
75
+ path: "…",
75
76
  edits: [{
76
77
  op: "replace",
77
78
  pos: "{{hlineref 7 "// @ts-ignore"}}",
@@ -82,7 +83,7 @@ Single line — `lines: null` deletes entirely:
82
83
  Range — add `end`:
83
84
  ```
84
85
  {
85
- file: "…",
86
+ path: "…",
86
87
  edits: [{
87
88
  op: "replace",
88
89
  pos: "{{hlineref 80 " // TODO: remove after migration"}}",
@@ -99,7 +100,7 @@ Range — add `end`:
99
100
  ```
100
101
  ```
101
102
  {
102
- file: "…",
103
+ path: "…",
103
104
  edits: [{
104
105
  op: "replace",
105
106
  pos: "{{hlineref 14 " placeholder: \"DO NOT SHIP\","}}",
@@ -118,7 +119,7 @@ Range — add `end`:
118
119
  ```
119
120
  ```
120
121
  {
121
- file: "…",
122
+ path: "…",
122
123
  edits: [{
123
124
  op: "replace",
124
125
  pos: "{{hlineref 60 " } catch (err) {"}}",
@@ -141,7 +142,7 @@ Range — add `end`:
141
142
  ```
142
143
  ```
143
144
  {
144
- file: "…",
145
+ path: "…",
145
146
  edits: [{
146
147
  op: "prepend",
147
148
  pos: "{{hlineref 45 " \"test\": \"bun test\""}}",
@@ -168,7 +169,7 @@ Bad — append after "}"
168
169
  Good — anchors to structural line:
169
170
  ```
170
171
  {
171
- file: "…",
172
+ path: "…",
172
173
  edits: [{
173
174
  op: "prepend",
174
175
  pos: "{{hlineref 103 "export function serialize(data: unknown): string {"}}",
@@ -184,7 +185,7 @@ Good — anchors to structural line:
184
185
  </example>
185
186
 
186
187
  <critical>
187
- - Edit payload: `{ file, edits[] }`. Each entry: `op`, `lines`, optional `pos`/`end`. No extra keys.
188
+ - Edit payload: `{ path, edits[] }`. Each entry: `op`, `lines`, optional `pos`/`end`. No extra keys.
188
189
  - Every tag MUST be copied exactly from fresh tool result as `N#ID`.
189
190
  - You MUST re-read after each edit call before issuing another on same file.
190
191
  </critical>
@@ -3,25 +3,15 @@
3
3
  Interact with Language Server Protocol servers for code intelligence.
4
4
 
5
5
  <operations>
6
- - `definition`: Go to symbol definition
7
- - `references`: Find all references to symbol
8
- - `hover`: Get type info and documentation
9
- - `symbols`: List symbols in file, or search workspace (with query, no file)
10
- - `rename`: Rename symbol across codebase
11
- - `diagnostics`: Get errors/warnings for file, or check entire project (no file)
6
+ - `definition`: Go to symbol definition → file path + position
7
+ - `references`: Find all references list of locations (file + position)
8
+ - `hover`: Get type info and documentation → type signature + docs
9
+ - `symbols`: List symbols in file, or search workspace (with query, no file) → names, kinds, locations
10
+ - `rename`: Rename symbol across codebase → confirmation of changes
11
+ - `diagnostics`: Get errors/warnings for file, or check entire project (no file) → list with severity + message
12
12
  - `reload`: Restart the language server
13
13
  </operations>
14
14
 
15
- <output>
16
- - `definition`: File path and position of definition
17
- - `references`: List of locations (file + position) where symbol used
18
- - `hover`: Type signature and documentation text
19
- - `symbols`: List of symbol names, kinds, locations
20
- - `rename`: Confirmation of changes made across files
21
- - `diagnostics`: List of errors/warnings with file, line, severity, message
22
- - `reload`: Confirmation of server restart
23
- </output>
24
-
25
15
  <caution>
26
16
  - Requires running LSP server for target language
27
17
  - Some operations require file to be saved to disk
@@ -1,6 +1,6 @@
1
1
  # Read
2
2
 
3
- Reads files from local filesystem or harness URLs.
3
+ Reads files from local filesystem or internal URLs.
4
4
 
5
5
  <instruction>
6
6
  - Reads up to {{DEFAULT_MAX_LINES}} lines default
@@ -14,13 +14,10 @@ Reads files from local filesystem or harness URLs.
14
14
  {{/if}}
15
15
  - Supports images (PNG, JPG) and PDFs
16
16
  - For directories, returns formatted listing with modification times
17
- - You SHOULD parallelize reads when exploring related files
17
+ - Parallelize reads when exploring related files
18
18
  </instruction>
19
19
 
20
20
  <output>
21
- - Returns file content as text
22
- - Images: returns visual content for analysis
23
- - PDFs: returns extracted text
21
+ - Returns file content as text; images return visual content; PDFs return extracted text
24
22
  - Missing files: returns closest filename matches for correction
25
- - Internal URLs: returns resolved content with pagination support
26
23
  </output>
@@ -1,317 +1,135 @@
1
1
  # Task
2
2
 
3
- Launch subagents to execute parallel, well-scoped tasks.
3
+ Launches subagents to parallelize workflows.
4
+
4
5
  {{#if asyncEnabled}}
5
- Use `read jobs://` to inspect background task state and `read jobs://<job-id>` for detailed status/output when needed.
6
- When you need to wait for async results before continuing, call `poll_jobs` it blocks until jobs complete. You MUST NOT poll `read jobs://` in a loop or yield and hope for delivery.
6
+ - Use `read jobs://` to inspect state; `read jobs://<job_id>` for detail.
7
+ - Use the `await` tool to wait until completion. You MUST NOT poll `read jobs://` in a loop.
7
8
  {{/if}}
8
9
 
9
- ## What subagents inherit automatically
10
- Subagents receive the **full system prompt**, including AGENTS.md, context files, and skills. You MUST NOT repeat project rules, coding conventions, or style guidelines in `context` — they already have them.
11
-
12
- ## What subagents do NOT have
13
- Subagents have no access to your conversation history. They don't know:
14
- - Decisions you made but didn't write down
15
- - Which approach you chose among alternatives
16
- - What you learned from reading files during this session
17
- - Requirements the user stated only in conversation
18
-
19
- Subagents CAN grep the parent conversation file for supplementary details.
20
-
21
- For large intermediate outputs (long traces, JSON payloads, temporary analysis snapshots), you SHOULD write them to `local://<path>` and pass the path in task context instead of inlining bulky text.
22
-
23
- ## Parameters
24
-
25
- ### `agent` (required)
10
+ Subagents lack your conversation history. Every decision, file content, and user requirement they need MUST be explicit in `context` or `assignment`.
26
11
 
27
- Agent type for all tasks in this batch.
28
-
29
- ### `context` (optional strongly recommended)
30
-
31
- Shared background prepended verbatim to every task `assignment`. Use only for session-specific information subagents lack.
12
+ <parameters>
13
+ - `agent`: Agent type for all tasks.
14
+ - `.id`: CamelCase, max 32 chars
15
+ - `.description`: UI display only — subagent never sees it
16
+ - `.assignment`: Complete self-contained instructions. One-liners PROHIBITED; missing acceptance criteria = too vague.
17
+ - `.skills`: Skill names to preload
18
+ - `context`: Shared background prepended to every assignment. Session-specific info only.
19
+ - `schema`: JTD schema for expected output. Format lives here — MUST NOT be duplicated in assignments.
20
+ - `tasks`: Tasks to execute in parallel.
21
+ - `isolated`: Run in isolated git worktree; returns patches. Use when tasks edit overlapping files.
22
+ </parameters>
32
23
 
33
24
  <critical>
34
- You MUST NOT include project rules, coding conventions, or style guidelines — subagents already have AGENTS.md and context files in their system prompt. Repeating them wastes tokens and inflates context. Restating any rule from AGENTS.md in `context` is a bug — treat it like a lint error.
25
+ - MUST NOT include AGENTS.md rules, coding conventions, or style guidelines — subagents already have them.
26
+ - MUST NOT duplicate shared constraints across assignments — put them in `context` once.
27
+ - MUST NOT tell tasks to run project-wide build/test/lint. Parallel agents share the working tree; each task edits, stops. Caller verifies after all complete.
28
+ - For large payloads (traces, JSON blobs), write to `local://<path>` and pass the path in context.
29
+ - If scope is unclear, run a **Discovery task** first to enumerate files and callsites, then fan out.
35
30
  </critical>
36
- **Before writing each line of context, ask:** "Would this sentence be true for ANY task in this repo, or only for THIS specific batch?" If it applies to any task → it's a project rule → the subagent already has it → you MUST delete the line.
37
31
 
38
- WRONG — restating project rules the subagent already has:
39
- ```
40
- ## Constraints
41
- - Use X import style, not Y (per AGENTS.md)
42
- - Use Z for private fields per AGENTS.md
43
- - Run the formatter after changes
44
- - Follow the logging convention
45
- ```
46
- Every line above restates a project convention. The subagent reads AGENTS.md. You MUST delete them all.
32
+ <scope>
33
+ Each task: **at most 3–5 files**. Globs in file paths, "update all", or package-wide scope = too broad. Enumerate files explicitly and fan out to a cluster of agents.
34
+ </scope>
47
35
 
48
- RIGHT — only session-specific decisions the subagent cannot infer from project files:
49
- ```
50
- ## Constraints
51
- - We decided to use approach A over B (session decision)
52
- - The migration target type is `Foo` from `bar` package (looked up this session)
53
- ```
54
-
55
- Use template; omit non-applicable sections:
56
-
57
- ````
58
- ## Goal
59
- One sentence: batch accomplishes together.
60
-
61
- ## Non-goals
62
- Explicitly exclude tempting scope — what tasks must not touch/attempt.
63
-
64
- ## Constraints
65
- - Task-specific MUST / MUST NOT rules not already in AGENTS.md
66
- - Decisions made during this session that affect implementation
67
-
68
- ## Reference Files
69
- - `path/to/file.ext` — pattern demo
70
- - `path/to/other.ext` — reuse or avoid
71
-
72
- ## API Contract (if tasks produce/consume shared interface)
73
- ```language
74
- // Exact type definitions, function signatures, interface shapes
75
- ```
36
+ <parallelization>
37
+ **Test:** Can task B produce correct output without seeing A's result? Yes → parallel. No → sequential.
76
38
 
77
- ## Acceptance (global)
78
- - Definition of "done" for batch
79
- - Note: build/test/lint verification happens AFTER all tasks complete — not inside tasks (see below)
80
- ````
81
- **Belongs in `context`**: task-specific goal, non-goals, session decisions, reference paths, shared type definitions, API contracts, global acceptance commands — anything 2+ tasks need that isn't already in AGENTS.md.
82
- **Rule of thumb:** if repeat in 2+ tasks, belongs in `context`.
83
- **Does NOT belong in `context`**: project rules already in AGENTS.md/context files, per-task file lists, one-off requirements (go in `assignment`), structured output format (goes in `schema`).
84
-
85
- ### `tasks` (required)
86
-
87
- Array tasks execute in parallel.
88
-
89
- |Field|Required|Purpose|
39
+ |Sequential first|Then|Reason|
90
40
  |---|---|---|
91
- |`id`|✓|CamelCase identifier, max 32 chars|
92
- |`description`|✓|Short one-liner for UI display only — not seen by subagent|
93
- |`assignment`|✓|Complete per-task instructions. See [Writing an assignment](#writing-an-assignment).|
94
- |`skills`||Skill names preload. Use only when changes correctness — don’t spam every task.|
95
-
96
- {{#if isolationEnabled}}
97
- ### `isolated` (optional)
98
-
99
- Run in isolated git worktree; returns patches. Use when tasks edit overlapping files or when you want clean per-task diffs.
100
- {{/if}}
101
- ### `schema` (optional — recommended for structured output)
102
-
103
- JTD schema defining expected response structure. Use typed properties. If you care about parsing result, define here — you MUST NOT describe output format in `context` or `assignment`.
104
-
105
- <caution>
106
- **Schema vs agent mismatch causes null output.** Agents with `output="structured"` (e.g., `explore`) have a built-in schema. If you also pass `schema`, yours takes precedence — but if you describe output format in `context`/`assignment` instead, the agent's built-in schema wins. The agent gets confused trying to fit your requested format into its schema shape and submits `null`. Either: (1) use `schema` to override the built-in one, (2) use `task` agent which has no built-in schema, or (3) match your instructions to the agent's expected output shape.
107
- </caution>
108
- ---
109
-
110
- ## Writing an assignment
111
-
112
- <critical>## Task scope
113
-
114
- `assignment` MUST contain enough info for agent to act **without asking a clarifying question**.
115
- **Minimum bar:** assignment under ~8 lines or missing acceptance criteria = too vague. One-liners guaranteed failure.
116
-
117
- Use structure every assignment:
118
-
41
+ |Types/interfaces|Consumers|Need contract|
42
+ |API exports|Callers|Need signatures|
43
+ |Core module|Dependents|Import dependency|
44
+ |Schema/migration|App logic|Schema dependency|
45
+ **Safe to parallelize:** independent modules, isolated file-scoped refactors, tests for existing code.
46
+ </parallelization>
47
+
48
+ <templates>
49
+ **context:**
119
50
  ```
120
- ## Target
121
- - Files: exact path(s)
122
- - Symbols/entrypoints: specific functions, types, exports
123
- - Non-goals: what task must NOT touch (prevents scope creep)
124
-
125
- ## Change
126
- - Step-by-step: add/remove/rename/restructure
127
- - Patterns/APIs to use; reference files if applicable
128
-
129
- ## Edge Cases / Don't Break
130
- - Tricky case 1: …
131
- - Tricky case 2: …
132
- - Existing behavior must survive: …
133
-
134
- ## Acceptance (task-local)
135
- - Expected behavior or observable result
136
- - DO NOT include project-wide build/test/lint commands (see below)
51
+ ## Goal ← one sentence: what the batch accomplishes
52
+ ## Non-goals ← what tasks must not touch
53
+ ## Constraints ← MUST/MUST NOT rules beyond AGENTS.md; session decisions
54
+ ## API Contract exact types/signatures if tasks share an interface (omit if N/A)
55
+ ## Acceptance ← definition of done; build/lint runs AFTER all tasks complete
137
56
  ```
138
-
139
- `context` carries shared background. `assignment` carries only delta: file-specific instructions, local edge cases, per-task acceptance checks. You MUST NOT duplicate shared constraints across assignments.
140
-
141
- ### Anti-patterns (ban these)
142
- **Vague assignments** — agent guesses wrong or stalls:
143
- - "Refactor this to be cleaner."
144
- - "Migrate to N-API."
145
- - "Fix the bug in streaming."
146
- - "Update all constructors in `src/**/*.ts`."
147
- **Vague context** — forces agent invent conventions:
148
- - "Use existing patterns."
149
- - "Follow conventions."
150
- - "No WASM."
151
- **Redundant context** — wastes tokens repeating what subagents already have:
152
- - Restating AGENTS.md rules (coding style, import conventions, formatting commands, logger usage, etc.)
153
- - Repeating project constraints from context files
154
- - Listing tool/framework preferences already documented in the repo
155
-
156
- If a constraint appears in AGENTS.md, it MUST NOT appear in `context`. The subagent has the full system prompt.
157
-
158
- If tempted to write above, expand using templates.
159
- **Output format in prose instead of `schema`** — agent returns null:
160
- Structured agents (`explore`, `reviewer`) have built-in output schemas. Describing a different output format in `context`/`assignment` without overriding via `schema` creates a mismatch — the agent can't reconcile your prose instructions with its schema and submits null data. You MUST use `schema` for output structure, or pick an agent whose built-in schema matches your needs.
161
- **Test/lint commands in parallel tasks** — edit wars:
162
- Parallel agents share working tree. If two agents run `bun check` or `bun test` concurrently, they see each other's half-finished edits, "fix" phantom errors, loop. You MUST NOT tell parallel tasks to run project-wide build/test/lint commands. Each task edits, stops. Caller verifies after all tasks complete.
163
- **If you can't specify scope yet**, create **Discovery task** first: enumerate files, find callsites, list candidates. Then fan out with explicit paths.
164
-
165
- ### Delegate intent, not keystrokes
166
-
167
- Your role as tech lead: set direction, define boundaries, call out pitfalls — then get out of way. Don’t read every file, decide every edit, dictate line-by-line. That makes you bottleneck; agent typist.
168
- **Be specific about:** constraints, naming conventions, API contracts, "don’t break" items, acceptance criteria.
169
- **Delegate:** code reading, approach selection, exact edit locations, implementation details. Agent has tools, can reason about code.
170
-
171
- Micromanaging (you think, agent types):
172
- ```
173
- assignment: "In src/api/handler.ts, line 47, change `throw err` to `throw new ApiError(err.message, 500)`.
174
- On line 63, wrap fetch call try/catch return 502 on failure.
175
- On line 89, add null check before accessing resp.body…"
57
+ **assignment:**
176
58
  ```
177
-
178
- Delegating (agent thinks within constraints):
59
+ ## Target ← exact file paths; named symbols; explicit non-goals
60
+ ## Change ← step-by-step what to add/remove/rename; patterns/APIs to use
61
+ ## Edge Cases ← tricky inputs; existing behavior that must survive
62
+ ## Acceptance ← observable result proving the task is done; no project-wide commands
179
63
  ```
180
- assignment: "## Target\n- Files: src/api/handler.ts\n\n## Change\nImprove error handling: replace raw throws
181
- with typed ApiError instances, add try/catch around external calls, guard against null responses.\n\n
182
- ## Edge Cases / Don't Break\n- Existing error codes in tests must still match\n
183
- - Don't change public function signatures"
184
- ```
185
-
186
- First style wastes your time, brittle if code shifts. Second gives agent room to do work.
187
- </critical>
64
+ </templates>
188
65
 
189
- ## Example
66
+ <checklist>
67
+ Before invoking:
68
+ - `context` contains only session-specific info not in AGENTS.md
69
+ - Every `assignment` follows the template; no one-liners; edge cases covered
70
+ - Tasks are truly parallel — you can articulate why none depends on another's output
71
+ - File paths are explicit; no globs
72
+ - `schema` is set if you expect structured output
73
+ </checklist>
190
74
 
191
- <example type="bad" label="Duplicated context inflates tokens">
192
- <tasks>
193
- <task name="Grep">
194
- <description>Port grep module from WASM to N-API…</description>
195
- <assignment>Port grep module from WASM to N-API… (same blob repeated)</assignment>
196
- </task>
197
- </tasks>
198
- </example>
75
+ <example label="Rename exported symbol + update all call sites">
76
+ Two tasks with non-overlapping file sets. Neither depends on the other's edits.
199
77
 
200
- <example type="good" label="Shared rules in context, only deltas in assignment">
201
78
  <context>
202
79
  ## Goal
203
- Port WASM modules to N-API, matching existing pi-natives conventions.
204
-
80
+ Rename `parseConfig` `loadConfig` in `src/config/parser.ts` and all callers.
205
81
  ## Non-goals
206
- Do not touch TS bindings or downstream consumers separate phase.
207
-
208
- ## Constraints
209
- - MUST use `#[napi]` attribute macro on all exports
210
- - MUST return `napi::Result<T>` for fallible ops; never panic
211
- - MUST use `spawn_blocking` for filesystem I/O or >1ms work
212
-
213
-
82
+ Do not change function behavior, signature, or testsrename only.
214
83
  ## Acceptance (global)
215
- - Caller verifies after all tasks: `cargo test -p pi-natives` and `cargo build -p pi-natives` with no warnings
216
- - Individual tasks must NOT run these commands themselves
84
+ Caller runs `bun check:ts` after both tasks complete. Tasks must NOT run it.
217
85
  </context>
218
-
219
86
  <tasks>
220
- <task name="PortGrep">
221
- <description>Port grep module to N-API</description>
87
+ <task name="RenameExport">
88
+ <description>Rename the export in parser.ts</description>
222
89
  <assignment>
223
90
  ## Target
224
- - Files: `src/grep.rs`, `src/lib.rs` (registration only)
225
- - Symbols: search, search_multi, compile_pattern
91
+ - File: `src/config/parser.ts`
92
+ - Symbol: exported function `parseConfig`
93
+ - Non-goals: do not touch callers or tests
226
94
 
227
95
  ## Change
228
- - Implement three N-API exports in grep.rs:
229
- - `search(pattern: JsString, path: JsString, env: Env) → napi::Result<Vec<Match>>`
230
-
96
+ - Rename `parseConfig` `loadConfig` (declaration + any JSDoc referencing it)
97
+ - If `src/config/index.ts` re-exports `parseConfig`, update that re-export too
98
+
99
+ ## Edge Cases
100
+ - If the function is overloaded, rename all overload signatures
101
+ - Internal helpers named `_parseConfigValue` or similar: leave untouched — different symbols
102
+ - Do not add a backwards-compat alias
231
103
 
232
- ## Acceptance (task-local)
233
- - Three functions exported with correct signatures (caller verifies build after all tasks)
104
+ ## Acceptance
105
+ - `src/config/parser.ts` exports `loadConfig`; `parseConfig` no longer appears as a top-level export in that file
234
106
  </assignment>
235
107
  </task>
236
-
237
- <task name="PortHighlight">
238
- <description>Port highlight module to N-API</description>
108
+ <task name="UpdateCallers">
109
+ <description>Update import and call sites in consuming modules</description>
239
110
  <assignment>
240
111
  ## Target
241
- - Files: `src/highlight.rs`, `src/lib.rs` (registration only)
242
-
112
+ - Files: `src/cli/init.ts`, `src/server/bootstrap.ts`, `src/worker/index.ts`
113
+ - Non-goals: do not touch `src/config/parser.ts` or `src/config/index.ts` — handled by sibling task
114
+
115
+ ## Change
116
+ - In each file: replace `import { parseConfig }` → `import { loadConfig }` from its config path
117
+ - Replace every call site `parseConfig(` → `loadConfig(`
118
+
119
+ ## Edge Cases
120
+ - If a file spreads the import (`import * as cfg from "…"`) and calls `cfg.parseConfig(…)`, update the property access too
121
+ - String literals containing "parseConfig" (log messages, comments) are documentation — leave them
122
+ - If any file re-exports `parseConfig` to an external package boundary, keep the old name via `export { loadConfig as parseConfig }` and add a `// TODO: remove after next major` comment
123
+
124
+ ## Acceptance
125
+ - No bare reference to `parseConfig` (as identifier, not string) remains in the three target files
243
126
  </assignment>
244
127
  </task>
245
128
  </tasks>
246
129
  </example>
247
- ---
248
-
249
- ## Task scope
250
-
251
- Each task MUST have small, well-defined scope — **at most 3–5 files**.
252
- **Signs task too broad:**
253
- - File paths use globs (`src/**/*.ts`) instead of explicit names
254
- - Assignment says "update all" / "migrate everything" / "refactor across"
255
- - Scope covers entire package or directory tree
256
- **Fix:** You MUST enumerate files first (grep/glob discovery), then fan out one task per file or small cluster.
257
- ---
258
-
259
- ## Parallelization
260
- **Test:** Can task B produce correct output without seeing task A's result?
261
- - **Yes** → parallelize
262
- - **No** → run sequentially (A completes, then launch B with A output in context)
263
-
264
- ### Must be sequential
265
-
266
- |First|Then|Reason|
267
- |---|---|---|
268
- |Define types/interfaces|Implement consumers|Consumers need contract|
269
- |Create API exports|Write bindings/callers|Callers need export names/signatures|
270
- |Scaffold structure|Implement bodies|Bodies need shape|
271
- |Core module|Dependent modules|Dependents import from core|
272
- |Schema/DB migration|Application logic|Logic depends on new schema shape|
273
-
274
- ### Safe to parallelize
275
- - Independent modules, no cross-imports
276
- - Tests for already-implemented code
277
- - Isolated file-scoped refactors
278
- - Documentation for stable APIs
279
-
280
- ### Phased execution
281
-
282
- <caution>
283
- **Parallel agents share the working tree.** They see each other's half-finished edits in real time. This is why:
284
- - Parallel tasks MUST NOT run project-wide build/test/lint — they will collide on phantom errors
285
- - Tasks editing overlapping files MUST use `isolated: true` (worktree isolation) or be made sequential
286
- - The caller MUST run verification after all tasks complete, not inside any individual task
287
- </caution>
288
-
289
- Layered work with dependencies:
290
- **Phase 1 — Foundation** (caller MUST do this, MUST NOT delegate): define interfaces, create scaffolds, establish API shape. You MUST NOT fan out until contract is known.
291
- **Phase 2 — Parallel implementation**: fan out tasks consuming same known interface. Include Phase 1 API contract in `context`.
292
- **Phase 3 — Integration** (caller MUST do this, MUST NOT delegate): wire modules, fix mismatches, verify builds.
293
- **Phase 4 — Dependent layer**: fan out tasks consuming Phase 2 outputs.
294
- ---
295
-
296
- ## Pre-flight checklist
297
-
298
- <critical>
299
- Before calling tool, verify each item:
300
- - [ ] `context` MUST include only session-specific info not already in AGENTS.md/context files
301
- - [ ] Each `assignment` MUST follow the assignment template — one-liners are PROHIBITED
302
- - [ ] Each `assignment` MUST include edge cases / "don't break" items
303
- - [ ] Tasks MUST be truly parallel — you MUST be able to articulate why no task depends on another's output
304
- - [ ] Scope MUST be small; file paths MUST be explicit (no globs)
305
- - [ ] Tasks MUST NOT run project-wide build/test/lint — caller MUST verify after all tasks complete
306
- - [ ] `schema` MUST be used if you expect structured output
307
- </critical>
308
- ---
309
-
310
- ## Agents
311
130
 
312
131
  {{#list agents join="\n"}}
313
- <agent name="{{name}}"{{#if output}} output="structured"{{/if}}>
314
- <description>{{description}}</description>
315
- <tools>{{default (join tools ", ") "All tools"}}</tools>
316
- </agent>
132
+ ### Agent: {{name}}
133
+ **Tools:** {{default (join tools ", ") "All"}}
134
+ {{description}}
317
135
  {{/list}}
@@ -7,13 +7,6 @@ Search the web for up-to-date information beyond Claude's knowledge cutoff.
7
7
  - You MUST include links for cited sources in the final response
8
8
  </instruction>
9
9
 
10
- <output>
11
- Returns search results formatted as blocks with:
12
- - Result summaries and relevant excerpts
13
- - Links as markdown hyperlinks for citation
14
- - Provider-dependent structure based on selected backend
15
- </output>
16
-
17
10
  <caution>
18
11
  Searches are performed automatically within a single API call—no pagination or follow-up requests needed.
19
12
  </caution>
@@ -5,13 +5,8 @@ Creates or overwrites file at specified path.
5
5
  <conditions>
6
6
  - Creating new files explicitly required by task
7
7
  - Replacing entire file contents when editing would be more complex
8
- - Prefer `local://<path>` for large temporary artifacts, subagent handoff payloads, and reusable planning artifacts that should survive within the session
9
8
  </conditions>
10
9
 
11
- <output>
12
- Confirmation of file creation/write with path. When LSP available, content may be auto-formatted before writing and diagnostics returned. Returns error if write fails (permissions, invalid path, disk full).
13
- </output>
14
-
15
10
  <critical>
16
11
  - You SHOULD use Edit tool for modifying existing files (more precise, preserves formatting)
17
12
  - You MUST NOT create documentation files (*.md, README) unless explicitly requested
package/src/sdk.ts CHANGED
@@ -1,4 +1,11 @@
1
- import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
1
+ import {
2
+ Agent,
3
+ type AgentEvent,
4
+ type AgentMessage,
5
+ type AgentTool,
6
+ INTENT_FIELD,
7
+ type ThinkingLevel,
8
+ } from "@oh-my-pi/pi-agent-core";
2
9
  import { type Message, type Model, supportsXhigh } from "@oh-my-pi/pi-ai";
3
10
  import { prewarmOpenAICodexResponses } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
4
11
  import type { Component } from "@oh-my-pi/pi-tui";
@@ -1114,6 +1121,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1114
1121
  });
1115
1122
 
1116
1123
  const repeatToolDescriptions = settings.get("repeatToolDescriptions");
1124
+ const intentField = settings.get("tools.intentTracing") || $env.PI_INTENT_TRACING === "1" ? INTENT_FIELD : undefined;
1117
1125
  const rebuildSystemPrompt = async (toolNames: string[], tools: Map<string, AgentTool>): Promise<string> => {
1118
1126
  toolContextStore.setToolNames(toolNames);
1119
1127
  const memoryInstructions = await buildMemoryToolDeveloperInstructions(agentDir, settings);
@@ -1128,6 +1136,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1128
1136
  skillsSettings: settings.getGroup("skills") as SkillsSettings,
1129
1137
  appendSystemPrompt: memoryInstructions,
1130
1138
  repeatToolDescriptions,
1139
+ intentField,
1131
1140
  });
1132
1141
 
1133
1142
  if (options.systemPrompt === undefined) {
@@ -1146,6 +1155,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1146
1155
  customPrompt: options.systemPrompt,
1147
1156
  appendSystemPrompt: memoryInstructions,
1148
1157
  repeatToolDescriptions,
1158
+ intentField,
1149
1159
  });
1150
1160
  }
1151
1161
  return options.systemPrompt(defaultPrompt);
@@ -1283,7 +1293,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1283
1293
  },
1284
1294
  cursorExecHandlers,
1285
1295
  transformToolCallArguments: obfuscator?.hasSecrets() ? args => obfuscator!.deobfuscateObject(args) : undefined,
1286
- intentTracing: settings.get("tools.intentTracing") || $env.PI_INTENT_TRACING === "1",
1296
+ intentTracing: !!intentField,
1287
1297
  });
1288
1298
  cursorEventEmitter = event => agent.emitExternalEvent(event);
1289
1299
 
@@ -419,6 +419,8 @@ export interface BuildSystemPromptOptions {
419
419
  preloadedSkills?: Skill[];
420
420
  /** Pre-loaded rulebook rules (rules with descriptions, excluding TTSR and always-apply). */
421
421
  rules?: Array<{ name: string; description?: string; path: string; globs?: string[] }>;
422
+ /** Intent field name injected into every tool schema. If set, explains the field in the prompt. */
423
+ intentField?: string;
422
424
  }
423
425
 
424
426
  /** Build the system prompt with tools, guidelines, and context */
@@ -439,6 +441,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
439
441
  skills: providedSkills,
440
442
  preloadedSkills: providedPreloadedSkills,
441
443
  rules,
444
+ intentField,
442
445
  } = options;
443
446
  const resolvedCwd = cwd ?? getProjectDir();
444
447
  const preloadedSkills = providedPreloadedSkills;
@@ -609,5 +612,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
609
612
  dateTime,
610
613
  cwd: resolvedCwd,
611
614
  appendSystemPrompt: resolvedAppendPrompt ?? "",
615
+ intentTracing: !!intentField,
616
+ intentField: intentField ?? "",
612
617
  });
613
618
  }
@@ -1,10 +1,10 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import { type Static, Type } from "@sinclair/typebox";
3
3
  import { renderPromptTemplate } from "../config/prompt-templates";
4
- import pollJobsDescription from "../prompts/tools/poll-jobs.md" with { type: "text" };
4
+ import awaitDescription from "../prompts/tools/await.md" with { type: "text" };
5
5
  import type { ToolSession } from "./index";
6
6
 
7
- const pollJobsSchema = Type.Object({
7
+ const awaitSchema = Type.Object({
8
8
  job_ids: Type.Optional(
9
9
  Type.Array(Type.String(), {
10
10
  description: "Specific job IDs to wait for. If omitted, waits for any running job.",
@@ -17,9 +17,9 @@ const pollJobsSchema = Type.Object({
17
17
  ),
18
18
  });
19
19
 
20
- type PollJobsParams = Static<typeof pollJobsSchema>;
20
+ type AwaitParams = Static<typeof awaitSchema>;
21
21
 
22
- interface PollJobResult {
22
+ interface AwaitResult {
23
23
  id: string;
24
24
  type: "bash" | "task";
25
25
  status: "running" | "completed" | "failed" | "cancelled";
@@ -29,34 +29,34 @@ interface PollJobResult {
29
29
  errorText?: string;
30
30
  }
31
31
 
32
- export interface PollJobsToolDetails {
33
- jobs: PollJobResult[];
32
+ export interface AwaitToolDetails {
33
+ jobs: AwaitResult[];
34
34
  timedOut: boolean;
35
35
  }
36
36
 
37
- export class PollJobsTool implements AgentTool<typeof pollJobsSchema, PollJobsToolDetails> {
38
- readonly name = "poll_jobs";
39
- readonly label = "PollJobs";
37
+ export class AwaitTool implements AgentTool<typeof awaitSchema, AwaitToolDetails> {
38
+ readonly name = "await";
39
+ readonly label = "Await";
40
40
  readonly description: string;
41
- readonly parameters = pollJobsSchema;
41
+ readonly parameters = awaitSchema;
42
42
  readonly strict = true;
43
43
 
44
44
  constructor(private readonly session: ToolSession) {
45
- this.description = renderPromptTemplate(pollJobsDescription);
45
+ this.description = renderPromptTemplate(awaitDescription);
46
46
  }
47
47
 
48
- static createIf(session: ToolSession): PollJobsTool | null {
48
+ static createIf(session: ToolSession): AwaitTool | null {
49
49
  if (!session.settings.get("async.enabled")) return null;
50
- return new PollJobsTool(session);
50
+ return new AwaitTool(session);
51
51
  }
52
52
 
53
53
  async execute(
54
54
  _toolCallId: string,
55
- params: PollJobsParams,
55
+ params: AwaitParams,
56
56
  signal?: AbortSignal,
57
- _onUpdate?: AgentToolUpdateCallback<PollJobsToolDetails>,
57
+ _onUpdate?: AgentToolUpdateCallback<AwaitToolDetails>,
58
58
  _context?: AgentToolContext,
59
- ): Promise<AgentToolResult<PollJobsToolDetails>> {
59
+ ): Promise<AgentToolResult<AwaitToolDetails>> {
60
60
  const manager = this.session.asyncJobManager;
61
61
  if (!manager) {
62
62
  return {
@@ -129,12 +129,12 @@ export class PollJobsTool implements AgentTool<typeof pollJobsSchema, PollJobsTo
129
129
  errorText?: string;
130
130
  }[],
131
131
  timedOut: boolean,
132
- ): AgentToolResult<PollJobsToolDetails> {
132
+ ): AgentToolResult<AwaitToolDetails> {
133
133
  const now = Date.now();
134
- const jobResults: PollJobResult[] = jobs.map(j => ({
134
+ const jobResults: AwaitResult[] = jobs.map(j => ({
135
135
  id: j.id,
136
136
  type: j.type,
137
- status: j.status as PollJobResult["status"],
137
+ status: j.status as AwaitResult["status"],
138
138
  label: j.label,
139
139
  durationMs: Math.max(0, now - j.startTime),
140
140
  ...(j.resultText ? { resultText: j.resultText } : {}),
@@ -380,7 +380,7 @@ const browserSchema = Type.Object({
380
380
  Type.Object({
381
381
  width: Type.Number({ description: "Viewport width in pixels" }),
382
382
  height: Type.Number({ description: "Viewport height in pixels" }),
383
- deviceScaleFactor: Type.Optional(Type.Number({ description: "Device scale factor" })),
383
+ device_scale_factor: Type.Optional(Type.Number({ description: "Device scale factor" })),
384
384
  }),
385
385
  ),
386
386
  delta_x: Type.Optional(Type.Number({ description: "Scroll delta X (scroll)" })),
@@ -515,7 +515,14 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
515
515
  async #resetBrowser(params?: BrowserParams): Promise<Page> {
516
516
  await this.#closeBrowser();
517
517
  this.#currentHeadless = this.session.settings.get("browser.headless");
518
- const initialViewport = params?.viewport ?? DEFAULT_VIEWPORT;
518
+ const vp = params?.viewport;
519
+ const initialViewport = vp
520
+ ? {
521
+ width: vp.width,
522
+ height: vp.height,
523
+ deviceScaleFactor: vp.device_scale_factor ?? DEFAULT_VIEWPORT.deviceScaleFactor,
524
+ }
525
+ : DEFAULT_VIEWPORT;
519
526
  const puppeteer = await loadPuppeteer();
520
527
  this.#browser = await puppeteer.launch({
521
528
  headless: this.#currentHeadless,
@@ -556,8 +563,15 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
556
563
  }
557
564
 
558
565
  async #applyViewport(page: Page, viewport?: BrowserParams["viewport"]): Promise<void> {
559
- const target = viewport ?? DEFAULT_VIEWPORT;
560
- await page.setViewport(target);
566
+ if (!viewport) {
567
+ await page.setViewport(DEFAULT_VIEWPORT);
568
+ return;
569
+ }
570
+ await page.setViewport({
571
+ width: viewport.width,
572
+ height: viewport.height,
573
+ deviceScaleFactor: viewport.device_scale_factor ?? DEFAULT_VIEWPORT.deviceScaleFactor,
574
+ });
561
575
  }
562
576
 
563
577
  async #clearElementCache(): Promise<void> {
@@ -15,6 +15,7 @@ import type { AgentOutputManager } from "../task/output-manager";
15
15
  import type { EventBus } from "../utils/event-bus";
16
16
  import { SearchTool } from "../web/search";
17
17
  import { AskTool } from "./ask";
18
+ import { AwaitTool } from "./await-tool";
18
19
  import { BashTool } from "./bash";
19
20
  import { BrowserTool } from "./browser";
20
21
  import { CalculatorTool } from "./calculator";
@@ -25,7 +26,6 @@ import { FindTool } from "./find";
25
26
  import { GrepTool } from "./grep";
26
27
  import { NotebookTool } from "./notebook";
27
28
  import { wrapToolWithMetaNotice } from "./output-meta";
28
- import { PollJobsTool } from "./poll-jobs";
29
29
  import { PythonTool } from "./python";
30
30
  import { ReadTool } from "./read";
31
31
  import { reportFindingTool } from "./review";
@@ -54,6 +54,7 @@ export * from "../session/streaming-output";
54
54
  export { BUNDLED_AGENTS, TaskTool } from "../task";
55
55
  export * from "../web/search";
56
56
  export { AskTool, type AskToolDetails } from "./ask";
57
+ export { AwaitTool, type AwaitToolDetails } from "./await-tool";
57
58
  export { BashTool, type BashToolDetails, type BashToolInput, type BashToolOptions } from "./bash";
58
59
  export { BrowserTool, type BrowserToolDetails } from "./browser";
59
60
  export { CalculatorTool, type CalculatorToolDetails } from "./calculator";
@@ -64,7 +65,6 @@ export { type FindOperations, FindTool, type FindToolDetails, type FindToolInput
64
65
  export { setPreferredImageProvider } from "./gemini-image";
65
66
  export { GrepTool, type GrepToolDetails, type GrepToolInput } from "./grep";
66
67
  export { NotebookTool, type NotebookToolDetails } from "./notebook";
67
- export { PollJobsTool, type PollJobsToolDetails } from "./poll-jobs";
68
68
  export { PythonTool, type PythonToolDetails, type PythonToolOptions } from "./python";
69
69
  export { ReadTool, type ReadToolDetails, type ReadToolInput } from "./read";
70
70
  export { reportFindingTool, type SubmitReviewDetails } from "./review";
@@ -169,7 +169,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
169
169
  browser: s => new BrowserTool(s),
170
170
  task: TaskTool.create,
171
171
  cancel_job: CancelJobTool.createIf,
172
- poll_jobs: PollJobsTool.createIf,
172
+ await: AwaitTool.createIf,
173
173
  todo_write: s => new TodoWriteTool(s),
174
174
  fetch: s => new FetchTool(s),
175
175
  web_search: s => new SearchTool(s),
@@ -63,7 +63,7 @@ export class NotebookTool implements AgentTool<typeof notebookSchema, NotebookTo
63
63
  readonly name = "notebook";
64
64
  readonly label = "Notebook";
65
65
  readonly description =
66
- "Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file) with new source. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path. The cell_number is 0-indexed. Use edit_mode=insert to add a new cell at the index specified by cell_number. Use edit_mode=delete to delete the cell at the index specified by cell_number.";
66
+ "Edit, insert, or delete cells in Jupyter notebooks (.ipynb). cell_index is 0-based. Paths must be absolute.";
67
67
  readonly parameters = notebookSchema;
68
68
  readonly strict = true;
69
69
  readonly concurrency = "exclusive";