Skip to content

Latest commit

 

History

History
256 lines (204 loc) · 10.5 KB

File metadata and controls

256 lines (204 loc) · 10.5 KB

pi-coding-agent — Development Guide

Emacs frontend for the pi coding agent. Two-window UI: markdown chat buffer + prompt composition buffer. Communicates with the pi CLI via JSON-over-stdio (RPC).

Module Architecture

Eight source modules with a strict dependency chain (no cycles):

pi-coding-agent.el              ← entry point, autoloads
  ├── pi-coding-agent-menu.el   ← transient menu, session management
  ├── pi-coding-agent-input.el  ← input buffer, history, completion
  └── pi-coding-agent-render.el ← chat rendering, tool output
        ├── pi-coding-agent-table.el  ← display-only table decoration
        └── pi-coding-agent-ui.el ← shared state, faces, modes
              ├── pi-coding-agent-core.el ← JSON/RPC protocol
              └── pi-coding-agent-grammars.el ← tree-sitter grammar recipes

External package dependency:

  • md-ts-mode ← tree-sitter markdown major mode used by chat buffers (loading pi-coding-agent must not globally claim unrelated Markdown files)

menu.el and input.el are siblings — neither requires the other. They communicate through shared variables in ui.el (e.g., --commands).

table.el and ui.el are siblings under render.el. table.el depends on ui.el for visible-text extraction and scroll preservation.

Cross-module state mutations use accessor functions defined in ui.el (e.g., --set-process, --set-aborted, --push-followup). Within a module, direct setq is fine.

Source Files

File Purpose
pi-coding-agent.el Entry point, autoloads, --setup-session
pi-coding-agent-core.el JSON parsing, line buffering, RPC protocol
pi-coding-agent-ui.el Shared state, faces, customization, modes, display primitives, header-line
pi-coding-agent-render.el Streaming chat rendering, tool output, fontification, diffs
pi-coding-agent-table.el Display-only pipe table decoration, wrapping, overlay management
pi-coding-agent-input.el Input history, isearch, send/abort, file/path/slash completion, queuing
pi-coding-agent-menu.el Transient menu, session management, model selection, commands
pi-coding-agent-grammars.el Tree-sitter grammar recipes, install prompts, M-x pi-coding-agent-install-grammars

Test Files

File Covers
test/pi-coding-agent-core-test.el Core/RPC protocol
test/pi-coding-agent-ui-test.el Buffer naming, modes, session dir, startup header, grammar install
test/pi-coding-agent-render-test.el Response display, tools, diffs
test/pi-coding-agent-table-test.el Table decoration, overlays, streaming, resize
test/pi-coding-agent-input-test.el History, send/abort, queuing, completion
test/pi-coding-agent-menu-test.el Session management, transient menu, reconnect
test/pi-coding-agent-build-test.el Batch helper scripts for dependency and grammar installation
test/pi-coding-agent-test.el Entry point / cross-module integration
test/pi-coding-agent-test-common.el Shared fixtures: mock-session macro, toolcall helpers, fake-pi launch helpers
test/pi-coding-agent-integration-test-common.el Shared integration backend helpers and contract macros
test/pi-coding-agent-integration-test-common-test.el Unit tests for shared integration helper macros
test/pi-coding-agent-integration-rpc-smoke-test.el Cheap shared fake/real RPC canaries
test/pi-coding-agent-integration-prompt-contract-test.el Shared fake/real prompt lifecycle + abort contracts
test/pi-coding-agent-integration-session-contract-test.el Shared fake/real session-file persistence contract
test/pi-coding-agent-integration-steering-contract-test.el Shared fake/real steering contract
test/pi-coding-agent-integration-tool-contract-test.el Shared fake/real tool execution contract
test/pi-coding-agent-integration-test.el Integration suite entry point (loads all shared contract modules)
test/pi-coding-agent-gui-tests.el GUI tests (require display or xvfb)

Other Files

File Purpose
Makefile Build, test, lint targets
bench/pi-coding-agent-bench.el Table rendering benchmark harness (xvfb GUI or batch)
bench/run-bench.sh Benchmark runner script; --batch for headless lane
bench/fixtures/tables.md Sample pipe tables used by the benchmark
scripts/check.sh Pre-commit hook: byte-compile + lint + tests
scripts/pi-coding-agent-build.el Shared batch helpers for dependency and grammar installation
scripts/install-deps.el Batch script: install required Emacs package dependencies
scripts/install-ts-grammars.el Batch script: install tree-sitter grammars

Running Tests

Run all unit tests:

make test

Run tests for a single module:

make test-core
make test-ui
make test-render
make test-input
make test-menu
make test-build

Run shared integration contracts:

make test-integration          # fake + real
make test-integration-fake     # fake only, fast
make test-integration-real     # real only

Run a filtered subset by ERT pattern:

make test SELECTOR=fontify-buffer-tail
make test SELECTOR=toolcall-delta
make test SELECTOR='abort\|followup'
make test-integration-fake SELECTOR=rpc-smoke
make test-integration-real SELECTOR=steering-contract

The SELECTOR value is an ERT selector string — a substring match against test names.

make test is intentionally terse on green runs (summary-focused output). For full raw ERT output, use:

make test VERBOSE=1
make test VERBOSE=1 SELECTOR=toolcall-delta

When tests intentionally trigger minibuffer message output, capture/mock message in the test and assert on it. This keeps batch logs concise without losing behavioral coverage.

Benchmarks

make bench             # GUI lane via xvfb (font metrics matter)
make bench-batch       # batch lane (no display, faster but no font engine)

The GUI lane is the primary measurement; batch is a quick sanity check. Fixtures live in bench/fixtures/tables.md.

Linting

make lint              # checkdoc + package-lint
make lint-checkdoc     # docstring warnings only
make lint-package      # MELPA package conventions only
make check-parens      # verify balanced parentheses in all source files
make check             # byte-compile + lint + all tests (= pre-commit hook)

Dependencies

make test auto-installs Emacs package deps (transient, md-ts-mode) on first run and caches via .deps-stamp. To force reinstall: make clean then make test.

Pre-commit Hook

The git pre-commit hook runs scripts/check.sh (byte-compile + checkdoc + package-lint + all unit tests, ~12s). Install with make install-hooks.

To skip for WIP commits: git commit --no-verify

Tmux Testing (Spike Scripts)

For reproducing visual bugs or testing interactive behavior, write a spike script in ./tmp/ (gitignored) and load it into Emacs inside tmux.

Every spike script needs this boilerplate at the top (use the actual absolute path to your checkout):

(setq inhibit-startup-screen t)
(add-to-list 'load-path "/absolute/path/to/pi-coding-agent")
(require 'package)
(package-initialize)
(require 'pi-coding-agent)

package-initialize is required here so installed dependencies like md-ts-mode and transient are on load-path before require runs.

Launch with (from the project root):

tmux new-session -d -s test -x 120 -y 40 \
  "emacs -nw -Q -l $PWD/tmp/spike.el 2>tmp/spike.log"
sleep 2 && tmux capture-pane -t test -p

To start a full interactive pi-coding-agent session in tmux:

tmux new-session -d -s test -x 120 -y 40 \
  "emacs -nw -Q --eval \"(progn (require 'package) (package-initialize) \
    (add-to-list 'load-path \\\"$PWD\\\") \
    (require 'pi-coding-agent) (pi-coding-agent))\""

Common gotchas:

  • -Q is required but skips package init — the boilerplate above fixes that
  • Sleep timing: use sleep 2 for UI ops, sleep 10+ for LLM responses
  • Buffer names follow *pi-coding-agent-{chat,input}:<dir>* (abbreviated), e.g. *pi-coding-agent-chat:~/co/pi-coding-agent/*
  • Window focus: the pi-coding-agent layout has two windows; C-x o switches between them. Prefer spike scripts over interactive tmux send-keys when possible — they're reproducible, debuggable, and don't require tracking focus state

Reference: pi CLI and RPC Protocol

The pi CLI (TypeScript) is the reference implementation. When implementing new RPC commands, understanding event formats, or checking how the TUI handles something, consult its source.

Finding the checkout: Look for a local clone, or clone one:

PI_MONO=$(find ~/co ~/src ~/projects /tmp -maxdepth 2 -name "pi-mono" -type d 2>/dev/null | head -1)
if [ -z "$PI_MONO" ]; then
  git clone git@github.com:badlogic/pi-mono.git /tmp/pi-mono
  PI_MONO=/tmp/pi-mono
fi

Key files (under $PI_MONO/packages/coding-agent/):

File When to consult
docs/rpc.md RPC command/event format, protocol overview
src/modes/rpc/rpc-types.ts Type definitions for all RPC commands and events
src/modes/rpc/rpc-mode.ts How RPC commands are dispatched and events emitted
src/modes/interactive/interactive-mode.ts How the TUI handles events, tool display, streaming
src/modes/interactive/components/tool-execution.ts TUI tool output rendering
src/core/agent-session.ts Session lifecycle, forking, message handling

Other useful packages:

Path Contents
$PI_MONO/packages/agent/src/agent-loop.ts Core agent loop, tool execution
$PI_MONO/packages/agent/src/types.ts Agent-level type definitions
$PI_MONO/packages/ai/src/types.ts AI provider types (messages, tools, content blocks)

When to look: Before implementing a new RPC command, when an event format is unclear, when the Emacs behavior should match the TUI, or when debugging protocol mismatches.

Git Hygiene

Always use git add <specific-files> instead of git add -A. The latter stages everything including spike scripts, test artifacts, and .pi/ files. Run git status before committing to verify what's staged.

Key Conventions

  • All public symbols are prefixed pi-coding-agent-
  • Internal symbols use pi-coding-agent-- (double dash)
  • Tests are named pi-coding-agent-test-<description>
  • Test files require pi-coding-agent-test-common for shared fixtures