When `context` is set, the grep tool reads the file again to print the
lines around each match. It turned CRLF into LF and also turned every
lone CR into LF before splitting the text into lines. ripgrep, which
supplies the match line numbers, counts lines only at LF. A lone CR
therefore added an extra line on pi's side, so every later line was off
by one.
For example, in a file containing `before\rprogress\nneedle\nafter`, a
search for `needle` with `context: 1` showed `progress` as the line-2
match and left out `after`.
The fix keeps the CRLF normalization and splits only on LF, so pi
numbers lines the same way ripgrep does. Displayed lines already have
`\r` removed, so the line before the match now shows as
`beforeprogress`.
**Testing**
- Added a test in `packages/coding-agent/test/tools.test.ts` for a file
with a lone CR searched with `context: 1`. It fails without the fix and
passes with it.
- `test/tools.test.ts`: all 87 tests pass.
- `npm run check` passes.
- Reproduction from the issue. Before the fix:
```
f.txt-1- before
f.txt:2: progress
f.txt-3- needle
```
After the fix:
```
f.txt-1- beforeprogress
f.txt:2: needle
f.txt-3- after
```
Fixes#11398
Requested by @badlogic
Co-authored-by: triagent <triagent@localhost>
The built-in grep tool spawned rg with the inherited environment, so
RIPGREP_CONFIG_PATH (e.g. a config containing --fixed-strings) silently
changed tool semantics: literal: false searches became literal and the
agent got incomplete results with no error.
Pass --no-config so the tool contract is deterministic. Users who want
custom rg behavior can use the bash tool or wrap grep in an extension.
closes#11203
Structured bash results now hold the full output up to 1 MiB instead of the
model-facing 2000 lines or 50KB, keeping the first and last 512 KiB of longer
output, and add truncated and full_output_path.
This adds codemode and MCP support to pi. Codemode (packages/codemode) runs
model-written JavaScript in a QuickJS wasm VM inside a worker. From there the
script calls pi's tools as async functions, uses a per-session store, and can
read the model catalog and run classifiers. The codemode tool in coding-agent
is compatible with Codex's exec tool, and codemode.mode works like Codex's
tool modes (on or only). Large results are written to a JSON temp file. MCP
(packages/mcp) is a standalone client for stdio and streamable HTTP servers,
with OAuth.
The core only gets general mechanisms; codemode, tool_search and MCP are
built-in extensions that use them. Other extensions can replace these
built-ins. Tools declare an exposure: direct, model-only, codemode, deferred
or hidden. prepareLoadout() lets an orchestrating tool change which tool
declarations the model sees. ctx.executeTool() runs nested calls through
validation and the tool_call/tool_result hooks. Those calls emit events with
parentToolCallId and are recorded as bounded nestedCalls on the parent
result, which compaction and HTML export use. Tools can also return
structuredContent with an outputSchema, and can report errors with isError
instead of throwing. MCP servers come from mcp.json (global, or per project
after the project is trusted) or from pi.registerMcpServer(). They are
managed with /mcp and pi mcp list|login|logout, and their runtimes load on
first use.
Closes#10040
Map signal termination to conventional nonzero exit codes and reject null exit codes from custom shell operations while preserving partial output.
fixes#9577
When tools are registered by an extension (e.g. Kimchi) the execute callback
receives an ExtensionContext with the real session cwd. Use ctx.cwd || cwd so
tools resolve paths against the current session cwd instead of the cwd captured
at tool creation time.
Applies to read, write, edit, grep, find, ls, bash, and powershell.
Co-authored-by: Kimchi <noreply@kimchi.dev>
* fix(coding-agent): report edit access failures correctly closes#3894
- classify edit and edit-preview access errors by errno
- add regressions for missing, permission, and fallback cases
- document the fix in the coding-agent changelog
* chore: get rid of CHANGELOG.md entry
* refactor(coding-agent): apply review suggestions - use single error msg
* refactor(coding-agent): clean up test cases
When 429/500 errors occur during tool execution, empty assistant messages
with stopReason='error' get persisted. These break the tool_use -> tool_result
chain for Claude/Gemini APIs.
Added centralized filtering in transformMessages to skip assistant messages
with empty content and no tool calls. Provider-level filters remain for
defense-in-depth.
- Validate working directory exists before spawning to provide clear error message
- Add spawn error handler to prevent uncaught exceptions when shell not found or cwd invalid
- Add tests for both error scenarios
Without these fixes, spawn errors (e.g., ENOENT from missing cwd or shell) would
cause uncaught exceptions that crash the entire agent session instead of being
returned as clean tool errors.
Co-authored-by: robinwander <robinwander@users.noreply.github.com>
Add grep, find, and ls tools for safe code exploration without modification risk.
These tools are available via the new --tools CLI flag.
- grep: Uses ripgrep (auto-downloaded) for fast regex searching. Respects .gitignore,
supports glob filtering, context lines, and hidden files.
- find: Uses fd (auto-downloaded) for fast file finding. Respects .gitignore, supports
glob patterns, and hidden files.
- ls: Lists directory contents with proper sorting and directory indicators.
- --tools flag: Specify available tools (e.g., --tools read,grep,find,ls for read-only mode)
- Dynamic system prompt adapts to selected tools with relevant guidelines
Closes#74
- Add generateDiffString() function in edit tool to create unified diffs with line numbers and 4 lines of context
- Store only the formatted diff string in tool result details instead of full file contents
- Update tool-execution renderer to parse and colorize the diff string
- Filter out message_update events from session saving to prevent verbose session files
- Add markdown nested list and table rendering tests
**Improved output messages:**
1. File fits within limits: Just outputs content (no notices)
2. Lines get truncated: Shows "Some lines were truncated to 2000 characters for display"
3. File doesn't fit limit: Shows "N more lines not shown. Use offset=X to continue reading"
4. Offset beyond file: Shows "Error: Offset X is beyond end of file (N lines total)"
5. Both truncations: Combines both notices with ". " separator
**Comprehensive test coverage:**
- Files within limits (no notices)
- Large files (line truncation)
- Long lines (character truncation)
- Offset parameter
- Limit parameter
- Offset + limit together
- Invalid offset (out of bounds)
- Combined truncations (both notices)
All 17 tests passing ✓
Tool results now use content blocks and can include both text and images.
All providers (Anthropic, Google, OpenAI Completions, OpenAI Responses)
correctly pass images from tool results to LLMs.
- Update ToolResultMessage type to use content blocks
- Add placeholder text for image-only tool results in Google/Anthropic
- OpenAI providers send tool result + follow-up user message with images
- Fix Anthropic JSON parsing for empty tool arguments
- Add comprehensive tests for image-only and text+image tool results
- Update README with tool result content blocks API