Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions HANDOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# HANDOFF.md

## Current State

Built `pi-toolview` extension for pi (compact tool output display).

**Status**: Working, PR open at https://github.com/smarzban/pi-extensions/pull/11

**Branch**: `pi-toolview` (based on `main`)

**Latest commit**: `adb46b3` - renamed `/toolview on|off` to `/toolview compact|full` (with on/off as aliases)

## What's Done

### pi-toolview Extension

Compact tool output for pi's 7 built-in tools (bash, read, edit, write, grep, find, ls).

**Features**:
- One-line summaries instead of full output (e.g., `✓ · 42 lines · 12.3s`)
- Smart paths (relative inside cwd, `~/` under HOME)
- Bash timing (via render state, not parsed from text)
- Write file size display
- Error emphasis (✗ prefix in red)
- Edit context hint (shows enclosing function from diff)
- `/toolview compact|full` command to toggle (per-tool or global)
- Instant toggle (no `/reload` needed) via `setToolsExpanded()`

**Architecture**:
- Re-registers each built-in tool with same name
- `execute()` delegates to original `createXTool(cwd)` factory
- `renderShell: "self"` drops default Box padding for tight look
- `row()` helper re-applies success/error/pending background manually
- Off-mode renders full content through `row()` (not native renderers)

## Key Decisions & Gotchas

### renderShell: "self" Trade-off

Used `renderShell: "self"` on all 7 tools to drop Box padding for tight spacing. This means:
- ✅ Tight spacing when compact (on)
- ❌ Native renderers break in self container (lose background/padding)
- ✅ Solution: off-mode renders full content through `row()` instead of delegating to native

**Why not delegate to native renderers when off?**
Native renderers (bash/read/grep/find/ls) reuse `context.lastComponent` and call `.clear()`/`.addChild()` on it. After compact rendering, that slot holds a plain `Text` (not a Container), so delegation throws. Edit works because it's self-shell native and builds fresh components.

**Workaround implemented**: Off-mode renders `result.content[0].text` through `row()` with bg/padding. Edit off-mode renders colored diff through `row()`.

### Bash Timing

Pi doesn't put "Took Xs" in output text. The built-in tracks timing via `context.state` (startedAt/endedAt). Toolview uses the same mechanism:
```typescript
type BashRenderState = { startedAt?: number; endedAt?: number };
// In renderCall: state.startedAt = Date.now()
// In renderResult: state.endedAt ??= Date.now()
```

### Error Detection

Non-zero exits come back as error results (`isError: true`), not "exit code:" in text. Toolview checks `context.isError` and parses "Command exited with code N" from the status line.

### Instant Toggle

`/toolview` toggles apply immediately via:
```typescript
ctx.ui.setToolsExpanded(ctx.ui.getToolsExpanded())
```
This re-runs `renderCall`/`renderResult` on all existing blocks.

### State Persistence

State in `~/.pi/agent/toolview.json`:
```json
{
"enabled": true,
"tools": { "bash": false, "read": false }
}
```
- `enabled`: global toggle
- `tools`: per-tool overrides (false = full output)
- `/toolview compact` clears `tools` map (sets all compact)
- `/toolview full` clears `tools` map (sets all full)

### Command Verbs

- Primary: `compact` / `full`
- Aliases: `on` / `off` (for compatibility)
- Per-tool: `/toolview bash compact` or `/toolview bash full`
- Toggle: `/toolview bash` (toggles single tool)

## What's Not Done

### Wishlist Items (Deferred)

1. **ctrl+s draft stash** (Claude Code style) - Not built yet. Would need `pi.registerShortcut("ctrl+s")` + `getEditorText()`/`setEditorText()` + `appendEntry()` for persistence.

2. **Double-paste to expand** - Partially feasible. Extension can't easily see raw paste events. Could add a shortcut to re-insert clipboard via `setEditorText()` (bypasses collapse).

3. **Prompt pinning at top** - Partially feasible. `ctx.ui.setHeader()` exists but unverified if it stays pinned during streaming. No mouse support in pi-tui (can't click to jump).

### Testing

- No automated tests yet
- Manual testing via `pi install /path/to/pi-extensions/packages/pi-toolview`
- Verified all 7 tools render correctly in both compact and full modes

### Documentation

- README.md is comprehensive
- No usage guide or examples beyond README

## Next Steps

1. **Merge PR** - PR #11 is ready for review/merge
2. **Publish to npm** - After merge, tag `pi-toolview-v0.1.0` and push to trigger release workflow
3. **User feedback** - See if the tight spacing + instant toggle meets expectations
4. **Wishlist items** - If user wants ctrl+s stash or other features, build those next

## Files

```
packages/pi-toolview/
├── index.ts # Main extension (24KB, ~700 lines)
├── package.json # Package metadata
├── README.md # User documentation
└── LICENSE # MIT
```

## Testing Locally

```bash
# Install from local path
pi install /Users/saeed/Workspace/pi-extensions/packages/pi-toolview

# Or test without installing
pi -e /Users/saeed/Workspace/pi-extensions/packages/pi-toolview/index.ts

# After changes, /reload in pi
```

## PR Status

- Branch: `pi-toolview`
- Base: `main`
- Commits: 10 (see git log)
- Status: Ready for review
- URL: https://github.com/smarzban/pi-extensions/pull/11
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Each package under `packages/` is independent: its own `package.json`, entrypoin
|---------|-----|-------------|
| [pi-pacman](packages/pi-pacman) | [`@pi-extensions/pi-pacman`](https://www.npmjs.com/package/@pi-extensions/pi-pacman) | Pac-Man working / thinking indicator |
| [pi-statusline](packages/pi-statusline) | [`@pi-extensions/pi-statusline`](https://www.npmjs.com/package/@pi-extensions/pi-statusline) | Rounded editor box with bottom-right session name; model/effort, context, usage, git/PR footer |
| [pi-toolview](packages/pi-toolview) | [`@pi-extensions/pi-toolview`](https://www.npmjs.com/package/@pi-extensions/pi-toolview) | Compact tool output: one-line summaries (expandable) instead of raw output |

Package docs, install, and commands live in each package’s README (e.g. [packages/pi-pacman/README.md](packages/pi-pacman/README.md)).

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"pi": {
"extensions": [
"./packages/pi-pacman",
"./packages/pi-statusline"
"./packages/pi-statusline",
"./packages/pi-toolview"
]
},
"devDependencies": {
Expand Down
21 changes: 21 additions & 0 deletions packages/pi-toolview/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Saeed Marzban

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the Software), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
110 changes: 110 additions & 0 deletions packages/pi-toolview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# pi-toolview

Compact tool output display for [pi](https://github.com/earendil-works/pi).

Replaces pi's verbose built-in tool rendering with one-line summaries, expandable on demand (ctrl+o). Execution is fully delegated to the originals, so the LLM still sees complete output.

## Before / after

**Default pi** shows full tool output inline — every line of bash, every file read, full diffs, etc.

**With pi-toolview:**

```
$ npm test
✓ · 42 lines · 12.3s

read src/app.ts (offset=100, limit=50)
85 lines (truncated from 200)

edit src/utils.ts (3 changes)
+12 / -4 in parseConfig

write dist/output.js (156 lines · 4.2 KB)
Written

grep /TODO/ in src --glob=*.ts
7 matches

find *.test.ts in src
23 results

ls packages
12 entries

$ rm nonexistent
✗ exit 1 · 2 lines · 0.0s
```

Press **ctrl+o** to expand all and see actual output, diffs, or search matches.

## Install

```bash
pi install /path/to/pi-extensions/packages/pi-toolview
```

## Features

| Feature | Example |
|---------|---------|
| **Smart paths** | `src/utils.ts` inside project, `~/code/file.ts` under HOME, absolute otherwise |
| **Bash timing** | `✓ · 42 lines · 12.3s` — measured via render state, same as built-in |
| **Write file size** | `(156 lines · 4.2 KB)` — catch accidental huge writes |
| **Error emphasis** | `✗ exit 1` in red, based on the tool's isError flag |
| **Edit context hint** | `+12 / -4 in parseConfig` — enclosing function from diff |
| **Per-tool control** | `/toolview bash full` — that tool shows full output |

## Commands

| Command | Effect |
|---------|--------|
| `/toolview` | Show current status |
| `/toolview compact` | All tools compact (summaries) |
| `/toolview full` | All tools full output |
| `/toolview <tool>` | Toggle one tool (e.g. `/toolview bash`) |
| `/toolview <tool> compact` | One tool compact |
| `/toolview <tool> full` | One tool full output |

`on`/`off` are accepted as aliases for `compact`/`full`.

Tools: `bash`, `read`, `edit`, `write`, `grep`, `find`, `ls`

State persists in `~/.pi/agent/toolview.json`.

## Tools

| Tool | Collapsed | Expanded (ctrl+o) |
|------|-----------|-------------------|
| bash | `✓` / `✗ exit N` + lines + timing | First 30 lines |
| read | Line count + truncation | First 20 lines |
| edit | `+N / -N` + function hint | Full diff (40 lines) |
| write | `Written` | N/A |
| grep | Match count (0 = muted) | First 20 matches |
| find | Result count | First 20 paths |
| ls | Entry count | First 20 entries |

## Partial override

Want to keep some tools at default? Use `/toolview bash full` to show full output for just bash. Or copy `index.ts` and delete the `pi.registerTool()` block for any tool.

## How it works

- Re-registers each built-in tool with the same name (pi uses the last registration)
- `execute()` delegates to the original `create*Tool(cwd)` factory, behavior is identical
- Bash timing uses the same `context.state` mechanism the built-in renderer uses
- `renderShell: "self"` drops the default padded Box for a tighter look; the
success/error/pending background color is re-applied manually. Pi hardcodes one
blank line above every tool block, so a single separator remains.
- When a tool is toggled off, its full result content is drawn through the same
pill row. The native per-tool renderers are built for the Box shell and lose their
background in the tight self frame, so they are not delegated to.
- `/toolview` toggles re-render already-drawn blocks immediately, no `/reload` needed
- Only `renderCall()` and `renderResult()` are custom (TUI display only)
- The LLM still receives full, unmodified `result.content`
- When disabled via `/toolview`, falls back to the original tool's renderer
- State reconstruction from session history works normally

## License

MIT
Loading
Loading