@oh-my-pi/pi-coding-agent 13.4.0 → 13.5.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 +22 -0
- package/package.json +7 -7
- package/src/config/prompt-templates.ts +9 -0
- package/src/prompts/agents/explore.md +21 -3
- package/src/prompts/agents/librarian.md +119 -0
- package/src/prompts/agents/oracle.md +77 -0
- package/src/prompts/agents/plan.md +1 -1
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/tools/ast-edit.md +3 -6
- package/src/prompts/tools/hashline.md +61 -14
- package/src/session/agent-session.ts +18 -16
- package/src/task/agents.ts +4 -0
- package/src/tools/pending-action.ts +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [13.5.0] - 2026-03-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `hlinejsonref` Handlebars helper for embedding hashline references inside JSON blocks in prompts
|
|
10
|
+
- Added `librarian` agent for researching external libraries and APIs by reading source code
|
|
11
|
+
- Added `oracle` agent for deep reasoning on debugging, architecture decisions, and technical advice
|
|
12
|
+
- Added `dependencies` and `risks` output fields to explore agent for better context handoff
|
|
13
|
+
- Added support for `lsp`, `fetch`, `web_search`, and `ast_grep` tools to explore, plan, and reviewer agents
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Enhanced hashline tool documentation with explicit prohibition on formatting-only edits
|
|
18
|
+
- Added mandatory rule requiring indentation in `lines` to match surrounding context exactly from `read` output
|
|
19
|
+
- Changed explore agent output field `query` to `summary` with expanded description for findings and conclusions
|
|
20
|
+
|
|
21
|
+
## [13.4.1] - 2026-03-01
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Pending resolve reminders now trigger as soon as a preview action is queued, before the next assistant turn, with regression coverage in `agent-session-resolve-reminder` tests
|
|
26
|
+
|
|
5
27
|
## [13.4.0] - 2026-03-01
|
|
6
28
|
|
|
7
29
|
### Breaking Changes
|
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.
|
|
4
|
+
"version": "13.5.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.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "13.
|
|
46
|
-
"@oh-my-pi/pi-ai": "13.
|
|
47
|
-
"@oh-my-pi/pi-natives": "13.
|
|
48
|
-
"@oh-my-pi/pi-tui": "13.
|
|
49
|
-
"@oh-my-pi/pi-utils": "13.
|
|
44
|
+
"@oh-my-pi/omp-stats": "13.5.0",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "13.5.0",
|
|
46
|
+
"@oh-my-pi/pi-ai": "13.5.0",
|
|
47
|
+
"@oh-my-pi/pi-natives": "13.5.0",
|
|
48
|
+
"@oh-my-pi/pi-tui": "13.5.0",
|
|
49
|
+
"@oh-my-pi/pi-utils": "13.5.0",
|
|
50
50
|
"@sinclair/typebox": "^0.34",
|
|
51
51
|
"@xterm/headless": "^6.0",
|
|
52
52
|
"ajv": "^8.18",
|
|
@@ -265,6 +265,15 @@ handlebars.registerHelper("hlineref", (lineNum: unknown, content: unknown): stri
|
|
|
265
265
|
return ref;
|
|
266
266
|
});
|
|
267
267
|
|
|
268
|
+
/**
|
|
269
|
+
* {{hlinejsonref lineNum "content"}} — same as hlineref but returns a JSON-quoted string.
|
|
270
|
+
* Useful for embedding hashline refs inside JSON blocks in prompts.
|
|
271
|
+
*/
|
|
272
|
+
handlebars.registerHelper("hlinejsonref", (lineNum: unknown, content: unknown): string => {
|
|
273
|
+
const { ref } = formatHashlineRef(lineNum, content);
|
|
274
|
+
return JSON.stringify(ref);
|
|
275
|
+
});
|
|
276
|
+
|
|
268
277
|
/**
|
|
269
278
|
* {{hlinefull lineNum "content"}} — format a full read-style line with prefix.
|
|
270
279
|
* Returns `"lineNum#hash:content"`.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: explore
|
|
3
3
|
description: Fast read-only codebase scout returning compressed context for handoff
|
|
4
|
-
tools: read, grep, find, bash
|
|
4
|
+
tools: read, grep, find, bash, lsp, fetch, web_search, ast_grep
|
|
5
5
|
model: pi/smol
|
|
6
6
|
thinking-level: minimal
|
|
7
7
|
output:
|
|
8
8
|
properties:
|
|
9
|
-
|
|
9
|
+
summary:
|
|
10
10
|
metadata:
|
|
11
|
-
description:
|
|
11
|
+
description: Brief summary of findings and conclusions
|
|
12
12
|
type: string
|
|
13
13
|
files:
|
|
14
14
|
metadata:
|
|
@@ -60,6 +60,24 @@ output:
|
|
|
60
60
|
metadata:
|
|
61
61
|
description: Brief explanation of how pieces connect
|
|
62
62
|
type: string
|
|
63
|
+
dependencies:
|
|
64
|
+
metadata:
|
|
65
|
+
description: Key internal and external dependencies relevant to the task
|
|
66
|
+
elements:
|
|
67
|
+
properties:
|
|
68
|
+
name:
|
|
69
|
+
metadata:
|
|
70
|
+
description: Package or module name
|
|
71
|
+
type: string
|
|
72
|
+
role:
|
|
73
|
+
metadata:
|
|
74
|
+
description: What it provides in context of the task
|
|
75
|
+
type: string
|
|
76
|
+
risks:
|
|
77
|
+
metadata:
|
|
78
|
+
description: Gotchas, edge cases, or constraints the receiving agent should know
|
|
79
|
+
elements:
|
|
80
|
+
type: string
|
|
63
81
|
start_here:
|
|
64
82
|
metadata:
|
|
65
83
|
description: Recommended entry point for receiving agent
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: librarian
|
|
3
|
+
description: Researches external libraries and APIs by reading source code. Returns definitive, source-verified answers.
|
|
4
|
+
tools: read, grep, find, bash, lsp, web_search, fetch, ast_grep
|
|
5
|
+
model: pi/smol
|
|
6
|
+
thinking-level: minimal
|
|
7
|
+
output:
|
|
8
|
+
properties:
|
|
9
|
+
answer:
|
|
10
|
+
metadata:
|
|
11
|
+
description: Direct answer to the question, grounded in source code
|
|
12
|
+
type: string
|
|
13
|
+
sources:
|
|
14
|
+
metadata:
|
|
15
|
+
description: Source evidence backing the answer
|
|
16
|
+
elements:
|
|
17
|
+
properties:
|
|
18
|
+
repo:
|
|
19
|
+
metadata:
|
|
20
|
+
description: GitHub repo (owner/name) or package name
|
|
21
|
+
type: string
|
|
22
|
+
path:
|
|
23
|
+
metadata:
|
|
24
|
+
description: File path within the repo or node_modules
|
|
25
|
+
type: string
|
|
26
|
+
line_start:
|
|
27
|
+
metadata:
|
|
28
|
+
description: First relevant line (1-indexed)
|
|
29
|
+
type: number
|
|
30
|
+
line_end:
|
|
31
|
+
metadata:
|
|
32
|
+
description: Last relevant line (1-indexed)
|
|
33
|
+
type: number
|
|
34
|
+
excerpt:
|
|
35
|
+
metadata:
|
|
36
|
+
description: Verbatim code or doc excerpt proving the claim
|
|
37
|
+
type: string
|
|
38
|
+
api:
|
|
39
|
+
metadata:
|
|
40
|
+
description: Extracted API signatures, types, or config relevant to the question
|
|
41
|
+
elements:
|
|
42
|
+
properties:
|
|
43
|
+
signature:
|
|
44
|
+
metadata:
|
|
45
|
+
description: Function signature, type definition, or config shape — copied verbatim from source
|
|
46
|
+
type: string
|
|
47
|
+
description:
|
|
48
|
+
metadata:
|
|
49
|
+
description: What it does, constraints, defaults
|
|
50
|
+
type: string
|
|
51
|
+
version:
|
|
52
|
+
metadata:
|
|
53
|
+
description: Library version investigated (from package.json, Cargo.toml, etc.)
|
|
54
|
+
type: string
|
|
55
|
+
optionalProperties:
|
|
56
|
+
breaking_changes:
|
|
57
|
+
metadata:
|
|
58
|
+
description: Breaking changes or migration notes if version-relevant
|
|
59
|
+
elements:
|
|
60
|
+
type: string
|
|
61
|
+
caveats:
|
|
62
|
+
metadata:
|
|
63
|
+
description: Limitations, undocumented behavior, or gotchas discovered
|
|
64
|
+
elements:
|
|
65
|
+
type: string
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
You are a library research specialist. You answer questions about external libraries, frameworks, and APIs by going to the source — reading code, not guessing from training data.
|
|
69
|
+
|
|
70
|
+
<critical>
|
|
71
|
+
You **MUST** ground every claim in source code or official documentation. You **MUST NOT** rely on training data for API details — it may be stale or wrong.
|
|
72
|
+
You **MUST** operate as read-only on the user's project. You **MUST NOT** modify any project files.
|
|
73
|
+
</critical>
|
|
74
|
+
|
|
75
|
+
<procedure>
|
|
76
|
+
## 1. Classify the request
|
|
77
|
+
|
|
78
|
+
Before acting, determine what kind of question this is:
|
|
79
|
+
- **Conceptual**: "How do I use X?", "Best practice for Y?" — Prioritize types, docs, and usage examples.
|
|
80
|
+
- **Implementation**: "How does X implement Y?", "Show me the source of Z" — Clone and read the actual code.
|
|
81
|
+
- **Behavioral**: "Why does X behave this way?", "What's the default for Y?" — Read implementation, find where values are set, check tests.
|
|
82
|
+
|
|
83
|
+
## 2. Locate the source (local first)
|
|
84
|
+
- **Check local dependencies first**: Look in `node_modules/<package>`, `vendor/`, or similar. If the library is already installed, read it there — no clone needed. Prioritize `.d.ts` type definitions and exported types.
|
|
85
|
+
- **Otherwise clone**: Use `web_search` to find the canonical repo, then `git clone --depth 1 <url> /tmp/librarian-<name>`.
|
|
86
|
+
- **For a specific version**: Clone then `git checkout tags/<version>`, or read the locally installed version.
|
|
87
|
+
|
|
88
|
+
## 3. Investigate
|
|
89
|
+
- Read `package.json`, `Cargo.toml`, or equivalent for version info and entry points.
|
|
90
|
+
- Use `grep`, `find`, and `ast_grep` to locate relevant source, type definitions, and docs. Parallelize searches.
|
|
91
|
+
- Read the actual implementation — not just README examples. READMEs are aspirational; source code is truth.
|
|
92
|
+
- For behavior questions: trace through the implementation. Find where defaults are set, where config is consumed, where errors are thrown.
|
|
93
|
+
- Check tests for usage examples and edge case behavior — tests are the most honest documentation.
|
|
94
|
+
|
|
95
|
+
## 4. Verify
|
|
96
|
+
- Cross-reference at least two locations (types + implementation, or source + tests).
|
|
97
|
+
- If the answer involves defaults, find where the default is actually set in code — not where the docs say it is.
|
|
98
|
+
- For API signatures: copy verbatim from source. You **MUST NOT** paraphrase or reconstruct from memory.
|
|
99
|
+
|
|
100
|
+
## 5. Report
|
|
101
|
+
- Call `submit_result` with structured findings.
|
|
102
|
+
- Every `sources` entry **MUST** include a verbatim excerpt.
|
|
103
|
+
- The `api` array **MUST** contain exact signatures copied from source.
|
|
104
|
+
- Clean up cloned repos: `rm -rf /tmp/librarian-*`.
|
|
105
|
+
</procedure>
|
|
106
|
+
|
|
107
|
+
<directives>
|
|
108
|
+
- You **SHOULD** invoke tools in parallel — search multiple paths simultaneously.
|
|
109
|
+
- You **MUST** include the exact version you investigated in the `version` field.
|
|
110
|
+
- If the library has breaking changes between versions relevant to the question, you **MUST** populate `breaking_changes`.
|
|
111
|
+
- If you discover undocumented behavior or gotchas, you **MUST** populate `caveats`.
|
|
112
|
+
- When local `node_modules` has the package, you **SHOULD** prefer it over cloning — it reflects the version the project actually uses.
|
|
113
|
+
- You **SHOULD** use `web_search` to find the canonical repo URL and to check for known issues, but the definitive answer **MUST** come from reading source code.
|
|
114
|
+
</directives>
|
|
115
|
+
|
|
116
|
+
<critical>
|
|
117
|
+
Source code is truth. Documentation is aspiration. Training data is history.
|
|
118
|
+
You **MUST** keep going until you have a definitive, source-verified answer.
|
|
119
|
+
</critical>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oracle
|
|
3
|
+
description: Deep reasoning advisor for debugging dead ends, architecture decisions, and second opinions. Read-only.
|
|
4
|
+
tools: read, grep, find, bash, lsp, fetch, web_search, ast_grep
|
|
5
|
+
spawns: explore
|
|
6
|
+
model: pi/slow
|
|
7
|
+
thinking-level: high
|
|
8
|
+
blocking: true
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
You are a senior diagnostician and strategic technical advisor. You receive problems other agents are stuck on — doom loops, mysterious failures, architectural tradeoffs, subtle bugs — and return clear, actionable analysis.
|
|
12
|
+
|
|
13
|
+
You diagnose, explain, and recommend. You do not implement. Others act on your findings.
|
|
14
|
+
|
|
15
|
+
<critical>
|
|
16
|
+
You **MUST** operate as read-only. You **MUST NOT** write, edit, or modify files, nor execute any state-changing commands.
|
|
17
|
+
</critical>
|
|
18
|
+
|
|
19
|
+
<directives>
|
|
20
|
+
- You **MUST** reason from first principles. The caller already tried the obvious.
|
|
21
|
+
- You **MUST** use tools to verify claims. You **MUST NOT** speculate about code behavior — read it.
|
|
22
|
+
- You **MUST** identify root causes, not symptoms. If the caller says "X is broken", determine *why* X is broken.
|
|
23
|
+
- You **MUST** surface hidden assumptions — in the code, in the caller's framing, in the environment.
|
|
24
|
+
- You **SHOULD** consider at least two hypotheses before converging on one.
|
|
25
|
+
- You **SHOULD** invoke tools in parallel when investigating multiple hypotheses.
|
|
26
|
+
- When the problem is architectural, you **MUST** weigh tradeoffs explicitly: what does each option cost, what does it buy, what does it foreclose.
|
|
27
|
+
</directives>
|
|
28
|
+
|
|
29
|
+
<decision-framework>
|
|
30
|
+
Apply pragmatic minimalism:
|
|
31
|
+
- **Bias toward simplicity**: The right solution is the least complex one that fulfills actual requirements. Resist hypothetical future needs.
|
|
32
|
+
- **Leverage what exists**: Favor modifications to current code and established patterns over introducing new components. New dependencies or infrastructure require explicit justification.
|
|
33
|
+
- **One clear path**: Present a single primary recommendation. Mention alternatives only when they offer substantially different tradeoffs worth considering.
|
|
34
|
+
- **Match depth to complexity**: Quick questions get quick answers. Reserve thorough analysis for genuinely complex problems.
|
|
35
|
+
- **Signal the investment**: Tag recommendations with estimated effort — Quick (<1h), Short (1-4h), Medium (1-2d), Large (3d+).
|
|
36
|
+
</decision-framework>
|
|
37
|
+
|
|
38
|
+
<procedure>
|
|
39
|
+
1. Read the problem statement carefully. Identify what was already tried and why it failed.
|
|
40
|
+
2. Form 2-3 hypotheses for the root cause.
|
|
41
|
+
3. Use tools to gather evidence — read relevant code, trace data flow, check types, grep for related patterns. Parallelize independent reads.
|
|
42
|
+
4. Eliminate hypotheses based on evidence. Narrow to the most likely cause.
|
|
43
|
+
5. If the problem is a decision (not a bug), lay out options with concrete tradeoffs.
|
|
44
|
+
6. Deliver a clear verdict with supporting evidence.
|
|
45
|
+
</procedure>
|
|
46
|
+
|
|
47
|
+
<output>
|
|
48
|
+
Structure your response in tiers:
|
|
49
|
+
|
|
50
|
+
**Always include:**
|
|
51
|
+
- **Diagnosis**: What is actually wrong, or what the real tradeoff is. 2-3 sentences.
|
|
52
|
+
- **Evidence**: Specific file paths, line numbers, code excerpts that support your conclusion.
|
|
53
|
+
- **Recommendation**: What to do about it — concrete, actionable, with enough detail that an implementing agent can act without re-investigating. Numbered steps, each 1-2 sentences.
|
|
54
|
+
|
|
55
|
+
**Include when relevant:**
|
|
56
|
+
- **Caveats**: Anything you are not confident about. Uncertainty **MUST** be stated, not hidden.
|
|
57
|
+
- **Risks**: Edge cases, failure modes, or mitigation strategies.
|
|
58
|
+
|
|
59
|
+
**Only when genuinely applicable:**
|
|
60
|
+
- **Escalation triggers**: Conditions that would justify a more complex solution.
|
|
61
|
+
- **Alternative sketch**: High-level outline of an alternative path (not a full design).
|
|
62
|
+
|
|
63
|
+
You **MUST NOT** pad with meta-commentary. Dense and useful beats long and thorough.
|
|
64
|
+
</output>
|
|
65
|
+
|
|
66
|
+
<scope-discipline>
|
|
67
|
+
- Recommend ONLY what was asked. No unsolicited improvements.
|
|
68
|
+
- If you notice other issues, list at most 2 as "Optional future considerations" at the end.
|
|
69
|
+
- You **MUST NOT** expand the problem surface beyond the original request.
|
|
70
|
+
- Exhaust provided context before reaching for tools. External lookups fill genuine gaps, not curiosity.
|
|
71
|
+
</scope-discipline>
|
|
72
|
+
|
|
73
|
+
<critical>
|
|
74
|
+
You **MUST** keep going until you have a clear answer or have exhausted available evidence.
|
|
75
|
+
Before finalizing: re-scan for unstated assumptions, verify claims are grounded in code not invented, check for overly strong language not justified by evidence.
|
|
76
|
+
This matters. The caller is stuck. Get it right.
|
|
77
|
+
</critical>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plan
|
|
3
3
|
description: Software architect for complex multi-file architectural decisions. NOT for simple tasks, single-file changes, or tasks completable in <5 tool calls.
|
|
4
|
-
tools: read, grep, find, bash
|
|
4
|
+
tools: read, grep, find, bash, lsp, fetch, web_search, ast_grep
|
|
5
5
|
spawns: explore
|
|
6
6
|
model: pi/plan, pi/slow
|
|
7
7
|
thinking-level: high
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reviewer
|
|
3
3
|
description: "Code review specialist for quality/security analysis"
|
|
4
|
-
tools: read, grep, find, bash, report_finding
|
|
4
|
+
tools: read, grep, find, bash, lsp, fetch, web_search, ast_grep, report_finding
|
|
5
5
|
spawns: explore, task
|
|
6
6
|
model: pi/slow
|
|
7
7
|
thinking-level: high
|
|
@@ -4,22 +4,20 @@ Performs structural AST-aware rewrites via native ast-grep.
|
|
|
4
4
|
- Use for codemods and structural rewrites where plain text replace is unsafe
|
|
5
5
|
- Narrow scope with `path` before replacing (`path` accepts files, directories, or glob patterns)
|
|
6
6
|
- Default to language-scoped rewrites in mixed repositories: set `lang` and keep `path` narrow
|
|
7
|
-
- Always returns a preview; after reviewing, call `resolve` with `action: "apply"` or `action: "discard"`
|
|
8
7
|
- Treat parse issues as a scoping signal: tighten `path`/`lang` before retrying
|
|
9
8
|
- Metavariables captured in each rewrite pattern (`$A`, `$$$ARGS`) are substituted into that entry's rewrite template
|
|
10
9
|
- Each matched rewrite is a 1:1 structural substitution; you cannot split one capture into multiple nodes or merge multiple captures into one node
|
|
11
10
|
</instruction>
|
|
12
11
|
|
|
13
12
|
<output>
|
|
14
|
-
- Returns replacement summary, per-file replacement counts, and change
|
|
15
|
-
- Reports whether changes were applied or only previewed
|
|
13
|
+
- Returns replacement summary, per-file replacement counts, and change diffs
|
|
16
14
|
- Includes parse issues when files cannot be processed
|
|
17
15
|
</output>
|
|
18
16
|
|
|
19
17
|
<examples>
|
|
20
|
-
- Rename a call site across a directory
|
|
18
|
+
- Rename a call site across a directory:
|
|
21
19
|
`{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"lang":"typescript","path":"src/"}`
|
|
22
|
-
- Multi-op codemod
|
|
20
|
+
- Multi-op codemod:
|
|
23
21
|
`{"ops":[{"pat":"require($A)","out":"import $A"},{"pat":"module.exports = $E","out":"export default $E"}],"lang":"javascript","path":"src/"}`
|
|
24
22
|
- Swap two arguments using captures:
|
|
25
23
|
`{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"lang":"typescript","path":"tests/"}`
|
|
@@ -28,6 +26,5 @@ Performs structural AST-aware rewrites via native ast-grep.
|
|
|
28
26
|
<critical>
|
|
29
27
|
- `ops` **MUST** contain at least one concrete `{ pat, out }` entry
|
|
30
28
|
- If the path pattern spans multiple languages, set `lang` explicitly for deterministic rewrites
|
|
31
|
-
- Review preview output, then use the `resolve` tool to apply or discard (with a reason)
|
|
32
29
|
- For one-off local text edits, prefer the Edit tool instead of AST edit
|
|
33
30
|
</critical>
|
|
@@ -6,6 +6,10 @@ Applies precise file edits using `LINE#ID` tags from `read` output.
|
|
|
6
6
|
3. You **MUST** submit one `edit` call per file with all operations, think your changes through before submitting.
|
|
7
7
|
</workflow>
|
|
8
8
|
|
|
9
|
+
<prohibited>
|
|
10
|
+
You **MUST NOT** use this tool for formatting-only edits: reindenting, realigning, brace-style changes, whitespace normalization, or line-length wrapping. Any edit whose diff is purely whitespace is a formatting operation — run the appropriate formatter for the project instead.
|
|
11
|
+
</prohibited>
|
|
12
|
+
|
|
9
13
|
<operations>
|
|
10
14
|
Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `pos` and `end` use `"N#ID"` format (e.g. `"23#XY"`).
|
|
11
15
|
**`pos`** — the anchor line. Meaning depends on `op`:
|
|
@@ -43,6 +47,8 @@ Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `p
|
|
|
43
47
|
3. **Range end tag (inclusive):** `end` is inclusive and **MUST** point to the final line being replaced.
|
|
44
48
|
- If `lines` includes a closing boundary token (`}`, `]`, `)`, `);`, `},`), `end` **MUST** include the original boundary line.
|
|
45
49
|
- You **MUST NOT** set `end` to an interior line and then re-add the boundary token in `lines`; that duplicates the next surviving line.
|
|
50
|
+
- To remove a line while keeping its neighbors, **delete** it (`lines: null`). You **MUST NOT** replace it with the content of an adjacent line — that line still exists and will be duplicated.
|
|
51
|
+
4. **Match surrounding indentation:** Leading whitespace in `lines` **MUST** be copied verbatim from adjacent lines in the `read` output. Do not infer or reconstruct indentation from memory — count the actual leading spaces on the lines immediately above and below the insertion or replacement point.
|
|
46
52
|
</rules>
|
|
47
53
|
|
|
48
54
|
<recovery>
|
|
@@ -59,7 +65,7 @@ Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `p
|
|
|
59
65
|
path: "…",
|
|
60
66
|
edits: [{
|
|
61
67
|
op: "replace",
|
|
62
|
-
pos:
|
|
68
|
+
pos: {{hlinejsonref 23 " const timeout: number = 5000;"}},
|
|
63
69
|
lines: [" const timeout: number = 30_000;"]
|
|
64
70
|
}]
|
|
65
71
|
}
|
|
@@ -73,7 +79,7 @@ Single line — `lines: null` deletes entirely:
|
|
|
73
79
|
path: "…",
|
|
74
80
|
edits: [{
|
|
75
81
|
op: "replace",
|
|
76
|
-
pos:
|
|
82
|
+
pos: {{hlinejsonref 7 "// @ts-ignore"}},
|
|
77
83
|
lines: null
|
|
78
84
|
}]
|
|
79
85
|
}
|
|
@@ -84,8 +90,8 @@ Range — add `end`:
|
|
|
84
90
|
path: "…",
|
|
85
91
|
edits: [{
|
|
86
92
|
op: "replace",
|
|
87
|
-
pos:
|
|
88
|
-
end:
|
|
93
|
+
pos: {{hlinejsonref 80 " // TODO: remove after migration"}},
|
|
94
|
+
end: {{hlinejsonref 83 " }"}},
|
|
89
95
|
lines: null
|
|
90
96
|
}]
|
|
91
97
|
}
|
|
@@ -101,7 +107,7 @@ Range — add `end`:
|
|
|
101
107
|
path: "…",
|
|
102
108
|
edits: [{
|
|
103
109
|
op: "replace",
|
|
104
|
-
pos:
|
|
110
|
+
pos: {{hlinejsonref 14 " placeholder: \"DO NOT SHIP\","}},
|
|
105
111
|
lines: [""]
|
|
106
112
|
}]
|
|
107
113
|
}
|
|
@@ -120,8 +126,8 @@ Range — add `end`:
|
|
|
120
126
|
path: "…",
|
|
121
127
|
edits: [{
|
|
122
128
|
op: "replace",
|
|
123
|
-
pos:
|
|
124
|
-
end:
|
|
129
|
+
pos: {{hlinejsonref 60 " } catch (err) {"}},
|
|
130
|
+
end: {{hlinejsonref 63 " }"}},
|
|
125
131
|
lines: [
|
|
126
132
|
" } catch (err) {",
|
|
127
133
|
" if (isEnoent(err)) return null;",
|
|
@@ -146,8 +152,8 @@ Bad — `end` stops before `}` while `lines` already includes `}`:
|
|
|
146
152
|
path: "…",
|
|
147
153
|
edits: [{
|
|
148
154
|
op: "replace",
|
|
149
|
-
pos:
|
|
150
|
-
end:
|
|
155
|
+
pos: {{hlinejsonref 70 "if (ok) {"}},
|
|
156
|
+
end: {{hlinejsonref 71 " run();"}},
|
|
151
157
|
lines: [
|
|
152
158
|
"if (ok) {",
|
|
153
159
|
" runSafe();",
|
|
@@ -162,8 +168,8 @@ Good — include original `}` in the replaced range when replacement keeps `}`:
|
|
|
162
168
|
path: "…",
|
|
163
169
|
edits: [{
|
|
164
170
|
op: "replace",
|
|
165
|
-
pos:
|
|
166
|
-
end:
|
|
171
|
+
pos: {{hlinejsonref 70 "if (ok) {"}},
|
|
172
|
+
end: {{hlinejsonref 72 "}"}},
|
|
167
173
|
lines: [
|
|
168
174
|
"if (ok) {",
|
|
169
175
|
" runSafe();",
|
|
@@ -190,7 +196,7 @@ Also apply the same rule to `);`, `],`, and `},` closers: if replacement include
|
|
|
190
196
|
path: "…",
|
|
191
197
|
edits: [{
|
|
192
198
|
op: "prepend",
|
|
193
|
-
pos:
|
|
199
|
+
pos: {{hlinejsonref 48 "function y() {"}},
|
|
194
200
|
lines: [
|
|
195
201
|
"function z() {",
|
|
196
202
|
" runZ();",
|
|
@@ -230,7 +236,7 @@ Good — anchors to structural line:
|
|
|
230
236
|
path: "…",
|
|
231
237
|
edits: [{
|
|
232
238
|
op: "prepend",
|
|
233
|
-
pos:
|
|
239
|
+
pos: {{hlinejsonref 103 "export function serialize(data: unknown): string {"}},
|
|
234
240
|
lines: [
|
|
235
241
|
"function validate(data: unknown): boolean {",
|
|
236
242
|
" return data != null && typeof data === \"object\";",
|
|
@@ -242,10 +248,51 @@ Good — anchors to structural line:
|
|
|
242
248
|
```
|
|
243
249
|
</example>
|
|
244
250
|
|
|
251
|
+
<example name="indentation must match context">
|
|
252
|
+
Leading whitespace in `lines` **MUST** be copied from the `read` output, not reconstructed from memory. Check the actual indent of neighboring lines.
|
|
253
|
+
```ts
|
|
254
|
+
{{hlinefull 10 "class Foo {"}}
|
|
255
|
+
{{hlinefull 11 " bar() {"}}
|
|
256
|
+
{{hlinefull 12 " return 1;"}}
|
|
257
|
+
{{hlinefull 13 " }"}}
|
|
258
|
+
{{hlinefull 14 "}"}}
|
|
259
|
+
```
|
|
260
|
+
Bad — indent guessed as 4 spaces instead of 2 (as seen on lines 11–13):
|
|
261
|
+
```
|
|
262
|
+
{
|
|
263
|
+
path: "…",
|
|
264
|
+
edits: [{
|
|
265
|
+
op: "prepend",
|
|
266
|
+
pos: {{hlinejsonref 14 "}"}},
|
|
267
|
+
lines: [
|
|
268
|
+
" baz() {",
|
|
269
|
+
" return 2;",
|
|
270
|
+
" }"
|
|
271
|
+
]
|
|
272
|
+
}]
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
Good — indent matches the 2-space style visible on adjacent lines:
|
|
276
|
+
```
|
|
277
|
+
{
|
|
278
|
+
path: "…",
|
|
279
|
+
edits: [{
|
|
280
|
+
op: "prepend",
|
|
281
|
+
pos: {{hlinejsonref 14 "}"}},
|
|
282
|
+
lines: [
|
|
283
|
+
" baz() {",
|
|
284
|
+
" return 2;",
|
|
285
|
+
" }"
|
|
286
|
+
]
|
|
287
|
+
}]
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
</example>
|
|
291
|
+
|
|
245
292
|
<critical>
|
|
246
293
|
- Edit payload: `{ path, edits[] }`. Each entry: `op`, `lines`, optional `pos`/`end`. No extra keys.
|
|
247
294
|
- Every tag **MUST** be copied exactly from fresh tool result as `N#ID`.
|
|
248
295
|
- You **MUST** re-read after each edit call before issuing another on same file.
|
|
249
|
-
- Formatting is a batch operation. You **MUST**
|
|
296
|
+
- Formatting is a batch operation. You **MUST NOT** use this tool to reformat, reindent, or adjust whitespace — run the project's formatter instead. If the only change is whitespace, it is formatting; do not touch it.
|
|
250
297
|
- `lines` entries **MUST** be literal file content with real space indentation. (`\\t` in JSON inserts a literal backslash-t into the file, not a tab.)
|
|
251
298
|
</critical>
|
|
@@ -291,6 +291,7 @@ export class AgentSession {
|
|
|
291
291
|
|
|
292
292
|
// Event subscription state
|
|
293
293
|
#unsubscribeAgent?: () => void;
|
|
294
|
+
#unsubscribePendingActionPush?: () => void;
|
|
294
295
|
#eventListeners: AgentSessionEventListener[] = [];
|
|
295
296
|
|
|
296
297
|
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
@@ -397,6 +398,21 @@ export class AgentSession {
|
|
|
397
398
|
this.#obfuscator = config.obfuscator;
|
|
398
399
|
this.agent.providerSessionState = this.#providerSessionState;
|
|
399
400
|
this.#pendingActionStore = config.pendingActionStore;
|
|
401
|
+
this.#unsubscribePendingActionPush = this.#pendingActionStore?.subscribePush(action => {
|
|
402
|
+
const reminderText = [
|
|
403
|
+
"<system-reminder>",
|
|
404
|
+
"This is a preview. Call the `resolve` tool to apply or discard these changes.",
|
|
405
|
+
"</system-reminder>",
|
|
406
|
+
].join("\n");
|
|
407
|
+
this.agent.steer({
|
|
408
|
+
role: "custom",
|
|
409
|
+
customType: "resolve-reminder",
|
|
410
|
+
content: reminderText,
|
|
411
|
+
display: false,
|
|
412
|
+
details: { toolName: action.sourceToolName },
|
|
413
|
+
timestamp: Date.now(),
|
|
414
|
+
});
|
|
415
|
+
});
|
|
400
416
|
this.#syncTodoPhasesFromBranch();
|
|
401
417
|
|
|
402
418
|
// Always subscribe to agent events for internal handling
|
|
@@ -688,22 +704,6 @@ export class AgentSession {
|
|
|
688
704
|
{ deliverAs: "nextTurn" },
|
|
689
705
|
);
|
|
690
706
|
}
|
|
691
|
-
if (!isError && this.#pendingActionStore?.hasPending) {
|
|
692
|
-
const reminderText = [
|
|
693
|
-
"<system-reminder>",
|
|
694
|
-
"This is a preview. Call the `resolve` tool to apply or discard these changes.",
|
|
695
|
-
"</system-reminder>",
|
|
696
|
-
].join("\n");
|
|
697
|
-
await this.sendCustomMessage(
|
|
698
|
-
{
|
|
699
|
-
customType: "resolve-reminder",
|
|
700
|
-
content: reminderText,
|
|
701
|
-
display: false,
|
|
702
|
-
details: { toolName },
|
|
703
|
-
},
|
|
704
|
-
{ deliverAs: "nextTurn" },
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
707
|
}
|
|
708
708
|
}
|
|
709
709
|
|
|
@@ -1443,6 +1443,8 @@ export class AgentSession {
|
|
|
1443
1443
|
state.close();
|
|
1444
1444
|
}
|
|
1445
1445
|
this.#providerSessionState.clear();
|
|
1446
|
+
this.#unsubscribePendingActionPush?.();
|
|
1447
|
+
this.#unsubscribePendingActionPush = undefined;
|
|
1446
1448
|
this.#disconnectFromAgent();
|
|
1447
1449
|
this.#eventListeners = [];
|
|
1448
1450
|
}
|
package/src/task/agents.ts
CHANGED
|
@@ -9,6 +9,8 @@ import designerMd from "../prompts/agents/designer.md" with { type: "text" };
|
|
|
9
9
|
import exploreMd from "../prompts/agents/explore.md" with { type: "text" };
|
|
10
10
|
// Embed agent markdown files at build time
|
|
11
11
|
import agentFrontmatterTemplate from "../prompts/agents/frontmatter.md" with { type: "text" };
|
|
12
|
+
import librarianMd from "../prompts/agents/librarian.md" with { type: "text" };
|
|
13
|
+
import oracleMd from "../prompts/agents/oracle.md" with { type: "text" };
|
|
12
14
|
import planMd from "../prompts/agents/plan.md" with { type: "text" };
|
|
13
15
|
import reviewerMd from "../prompts/agents/reviewer.md" with { type: "text" };
|
|
14
16
|
import taskMd from "../prompts/agents/task.md" with { type: "text" };
|
|
@@ -42,6 +44,8 @@ const EMBEDDED_AGENT_DEFS: EmbeddedAgentDef[] = [
|
|
|
42
44
|
{ fileName: "plan.md", template: planMd },
|
|
43
45
|
{ fileName: "designer.md", template: designerMd },
|
|
44
46
|
{ fileName: "reviewer.md", template: reviewerMd },
|
|
47
|
+
{ fileName: "oracle.md", template: oracleMd },
|
|
48
|
+
{ fileName: "librarian.md", template: librarianMd },
|
|
45
49
|
{
|
|
46
50
|
fileName: "task.md",
|
|
47
51
|
frontmatter: {
|
|
@@ -10,9 +10,14 @@ export interface PendingAction {
|
|
|
10
10
|
|
|
11
11
|
export class PendingActionStore {
|
|
12
12
|
#actions: PendingAction[] = [];
|
|
13
|
+
#pushListeners = new Set<(action: PendingAction, count: number) => void>();
|
|
13
14
|
|
|
14
15
|
push(action: PendingAction): void {
|
|
15
16
|
this.#actions.push(action);
|
|
17
|
+
const count = this.#actions.length;
|
|
18
|
+
for (const listener of this.#pushListeners) {
|
|
19
|
+
listener(action, count);
|
|
20
|
+
}
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
peek(): PendingAction | null {
|
|
@@ -23,10 +28,21 @@ export class PendingActionStore {
|
|
|
23
28
|
return this.#actions.pop() ?? null;
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
subscribePush(listener: (action: PendingAction, count: number) => void): () => void {
|
|
32
|
+
this.#pushListeners.add(listener);
|
|
33
|
+
return () => {
|
|
34
|
+
this.#pushListeners.delete(listener);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
clear(): void {
|
|
27
39
|
this.#actions = [];
|
|
28
40
|
}
|
|
29
41
|
|
|
42
|
+
get count(): number {
|
|
43
|
+
return this.#actions.length;
|
|
44
|
+
}
|
|
45
|
+
|
|
30
46
|
get hasPending(): boolean {
|
|
31
47
|
return this.#actions.length > 0;
|
|
32
48
|
}
|