---
slug: "fahmiirsyadk-pi-minimal-toolcall"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/fahmiirsyadk/pi-minimal-toolcall@main/README.md"
repo: "https://github.com/fahmiirsyadk/pi-minimal-toolcall"
source_file: "README.md"
branch: "main"
---
<div align="center">

# pi-minimal-toolcall

Minimalist & less noisy tool-call rendering for [Pi](https://github.com/earendil-works/pi-coding-agent).

</div>

<div align="center">

https://github.com/user-attachments/assets/605cb58b-cbd3-4dfa-9a0d-41f46472038c

</div>

<table>
<tr>
<td align="center" width="50%">

![ss_1](https://github.com/fahmiirsyadk/pi-minimal-toolcall/raw/HEAD/ss_1.png)

</td>
<td align="center" width="50%">

![ss_2](https://github.com/fahmiirsyadk/pi-minimal-toolcall/raw/HEAD/ss_2.png)

</td>
</tr>
</table>
<div align="center">
  <i>another extensions you might want to try</i><br/>
  <a href="https://github.com/fahmiirsyadk/pi-record-term.git">pi-record-term</a>
  <span>|</span>
  <a href="https://github.com/fahmiirsyadk/pi-minimal-diff.git">pi-minimal-diff</a>
</div>

## Visual
```
Shell 1 command (grep -rn "friendlyLabel\|nounFor" ./tests ./src --include="*.test.ts" 2>/dev/null | head -10) ctrl+o to expand
Write 1 file (README.md) +148 -129 ctrl+o to expand
Shell 1 command & Read 1 file ctrl+o to expand
⠋ Read file
```

## Installation

Pi package manager:

```bash
pi install npm:@fahmiirsyadk/pi-minimal-toolcall
```

Git Repository:

```
pi install git:github.com/fahmiirsyadk/pi-minimal-toolcall
```

Local clone (for development or pinning to a fork):

```bash
git clone https://github.com/fahmiirsyadk/pi-minimal-toolcall.git ~/pi-minimal-toolcall
```

Then add to your `~/.pi/agent/settings.json`:

```json
{
  "extensions": [
    "~/pi-minimal-toolcall/index.ts"
  ]
}
```

## Quick start for customizing

Edit `~/.pi/agent/extensions/pi-minimal-toolcall/config.json` (or `$PI_CODING_AGENT_DIR/extensions/pi-minimal-toolcall/config.json`):

```json
{
  "toolsExpandedByDefault": true,
  "hiddenThinkingLabel": "",
  "registerToolOverrides": { "bash": false },
  "groupingMode": "proximity",
  "showArgOnSummary": "always"
}
```

Then `/reload` to apply.

Three starter presets ship in [`config/presets/`](https://github.com/fahmiirsyadk/pi-minimal-toolcall/tree/HEAD/config/presets) — copy the one you want to your config file:

- [`calm.json`](https://github.com/fahmiirsyadk/pi-minimal-toolcall/blob/HEAD/config/presets/calm.json) — the defaults (one row per group, collapsed tools, `thinking` label).
- [`verbose.json`](https://github.com/fahmiirsyadk/pi-minimal-toolcall/blob/HEAD/config/presets/verbose.json) — expanded previews, larger body cap, args on multi-tool rows.
- [`minimal.json`](https://github.com/fahmiirsyadk/pi-minimal-toolcall/blob/HEAD/config/presets/minimal.json) — bare frames, no args, no diff, no `✗`, empty thinking label.

## What you get (Configuration Option)

| Behavior | Default | How to change |
| --- | --- | --- |
| Tool rows | collapsed | `toolsExpandedByDefault: true` |
| Thinking | hidden behind `thinking` label | `hiddenThinkingLabel: "..."` |
| Grouping mode | `proximity` (any tool call joins until text/thinking) | `groupingMode: "consecutive" \| "none"` |
| Latest arg on summary | single-tool groups only | `showArgOnSummary: "always" \| "never"` |
| Aggregated `+N -M` | on | `showDiffSuffix: false` |
| Per-tool `✗` | on | `showErrorMark: false` |
| Write expand | full content, syntax-highlighted | `writeExpandMode: "summary" \| "both"` |
| Expanded body cap | 200 lines | `expandedBodyMaxLines: <n>` |
| Spinner | `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏` @ 80ms | `spinnerFrames: ["a","b"]`, `spinnerIntervalMs: <n>` |
| Per-tool ownership | all 7 built-ins | `registerToolOverrides: { "read": false, ... }` |
| Batch tools (`read_files`, `edit_files`, `grep_files`, `find_files`) | on | `batchToolsEnabled: false` |
| Debug log | off | `debug: true` (writes to `<agent-dir>/.../debug/debug.log`) |
| `customToolOverrides` | loaded + normalized; not yet rendered | (awaiting SDK support — see below) |

### `customToolOverrides` (not yet wired)

The `customToolOverrides` config field is parsed and normalized (built-in tool names are rejected, `_comment*` keys are dropped, booleans are coerced, invalid `outputMode` values fall back to `"summary"`, entries are capped at 256 to guard against accidental JSON imports). The SDK's public `pi.getAllTools()` API currently returns `ToolInfo` (name + description + parameters) only — not the full `ToolDefinition` (no `execute` / `renderCall` / `renderResult`) — so the package cannot wrap a non-builtin tool's `execute` from the extension layer. Your config is preserved on disk and will activate the moment the SDK exposes the full definition.

## Doctor command

`/minimal-toolcall-doctor` is a read-only diagnostic command. It prints the resolved config (loaded from disk or the shipped defaults) and flags likely footguns:

- `spinnerIntervalMs < 50` (CPU peg)
- `expandedBodyMaxLines ≤ 0` (collapses every expanded view)
- All 7 built-in tools disabled (the package becomes a no-op)
- `batchToolsEnabled: false` (the batch tools won't be registered)
- Non-empty `customToolOverrides` (the field is parsed but doesn't yet affect rendering)

The command replaces the dropped 0.2.0 `/minimal-toolcall` runtime. It does not mutate state — reloads and config changes go through the file + `/reload`.

## How grouping works

A **group** is a run of tool calls with no text or thinking between them, regardless of tool name. Frozen when a text or thinking block appears (`message_update` with `text_start` / `thinking_start`), or when a new agent loop starts (`agent_start` — separates prompts).

```
read, read, read                  → Read 3 files
read, read, bash, read            → one row (proximity)
text or thinking
bash, bash                        → Shell 2 commands (new group)
```

`groupingMode: "consecutive"` restores the old "different tool name = new group" behavior. `"none"` makes every call its own row.

## Batch tools

The package registers four batch tools the model can call instead of repeating built-ins:

| Tool | Input |
| --- | --- |
| `read_files` | `{ paths, offset?, limit? }` |
| `edit_files` | `{ edits: [{ path, oldText, newText }] }` |
| `grep_files` | `{ queries: [{ pattern, path? }] }` |
| `find_files` | `{ queries: [{ pattern, path? }] }` |

Each renders as one row; `ctrl+o` expands to per-item `✓`/`✗` status plus aggregated output. Partial failures surface per item.

## Reload

`/reload` re-reads `config.json` and re-registers. Tool ownership, the per-tool grouping session, and the debug-log path all take effect on the next `session_start`.

## Compatibility

- Pi `0.79.0+`. Peer dependencies: `@earendil-works/pi-coding-agent`, `pi-ai`, `pi-tui` (all `*` range — pi bundles these).
- This package only changes tool-call rendering. We touch two pieces of global UI state on `session_start` (and only these two — we don't touch the working indicator, the prompt box, or anything else outside our scope):
  - `ctx.ui.setToolsExpanded(config.toolsExpandedByDefault)` — the resting state of tool output (collapsed vs expanded). `ctrl+o` toggles it during a session.
  - `ctx.ui.setHiddenThinkingLabel(config.hiddenThinkingLabel)` — the label on the thinking block.
- Tools registered by other extensions render with their own renderer and break proximity groups.

## Development

```bash
npm install
npm run check        # typecheck + test
npm run pack:dry     # preview the published tarball
```

Uses `tsc` for typecheck and `tsx --test` for tests. No bundler, no
formatter, no linter — keep the dep tree small.

## License

MIT
