Skip to content

feat: add pi coding agent session monitoring (--agent pi|claude|all)#8

Merged
nahime0 merged 15 commits intoillegalstudio:mainfrom
vdemeester:valiant-rifle
Mar 10, 2026
Merged

feat: add pi coding agent session monitoring (--agent pi|claude|all)#8
nahime0 merged 15 commits intoillegalstudio:mainfrom
vdemeester:valiant-rifle

Conversation

@vdemeester
Copy link
Copy Markdown
Contributor

Summary

Adds support for monitoring pi coding agent sessions alongside Claude Code sessions. Pi sessions are discovered from ~/.pi/agent/sessions/, parsed from their tree-structured JSONL format, and merged into the existing session list.

New --agent flag

lazyagent                    # monitors both (default)
lazyagent --agent claude     # Claude Code only
lazyagent --agent pi         # pi coding agent only
lazyagent --agent all        # both (default)

Pi sessions are marked with a π prefix in the session list and show an "Agent" row in the detail panel.

Architecture

The key design decision is reusing claude.Session as the shared session struct. The pi parser reads pi's JSONL and produces claude.Session objects, so zero changes were needed in core/, ui/, api/, or tray/ for session rendering.

New package: internal/pi/

  • types.go — Pi JSONL entry type definitions
  • jsonl.go — Single-pass parser (pi JSONL → claude.Session)
  • process.go — Session discovery from ~/.pi/agent/sessions/

Provider pattern

  • PiProvider — discovers pi sessions
  • MultiProvider — merges sessions from multiple providers, gracefully handles individual failures
  • WatchDirs() added to SessionProvider interface — providers declare which directories to watch

Other changes

  • NewProjectWatcher(dirs...) — refactored to accept variadic directories (was hardcoded to ~/.claude/projects/)
  • ToolActivity() — added pi snake_case tool name mappings as safety net
  • EstimateCost() — added Gemini and GPT model family rates
  • Session.Agent field — identifies which agent produced each session
  • Session.Name field — pi's /name command writes session_info entries; auto-populated into SessionNames without overwriting user-set names
  • NewModel() and api.New() now accept SessionProvider instead of demoMode bool

Testing

49 tests, all passing:

Package Tests Coverage
internal/pi/ 24 Parser, discovery, tool names, real data integration
internal/core/ 19 MultiProvider, watcher, activity, cost estimation, session names
internal/api/ 6 Existing tests updated for new API

Integration test discovers 867 real pi sessions in ~1.3s.

Performance note

Discovery reads all JSONL files on each cycle. For 869 pi sessions (130MB) this takes ~1.5s. This is comparable to Claude Code discovery (7s for 1783 files) and is a pre-existing pattern — both could benefit from mtime-based skipping or caching in a future optimization PR.

Files changed

  • 5 new files in internal/pi/
  • 5 new test files across internal/core/ and internal/pi/
  • 12 modified files (main.go, core, claude, ui, api, tray, demo, README)
  • +1505 / -56 lines

vdemeester and others added 15 commits March 9, 2026 17:00
Single-pass parser that reads pi's tree-structured JSONL format
and produces claude.Session structs. Handles:
- Session header (CWD, version)
- Model changes (tracks latest model)
- User/assistant message counting
- Cost and token accumulation from usage objects
- Tool call tracking with name normalization (snake_case → PascalCase)
- Status detection (thinking, waiting, executing tool, processing result)
- File write tracking (write/edit tools)
- Compaction detection
- Recent messages and tool calls
- Plain string and array content formats

14 tests covering all behaviors.
Scans pi session directories, reuses claude.IsWorktree()
for git worktree detection, and decodes pi's directory
name encoding as CWD fallback.

6 tests: dir path, name decoding, synthetic discovery,
fallback CWD, empty dir, nonexistent dir.
- Added WatchDirs() to SessionProvider interface
- PiProvider discovers pi coding agent sessions
- MultiProvider merges sessions from multiple providers,
  gracefully handling individual provider failures
- Updated LiveProvider and demo.Provider for new interface

5 tests covering merge, error handling, watcher delegation,
directory aggregation, and refresh interval selection.
Refactored NewProjectWatcher to accept variadic directory list.
SessionManager.StartWatcher now passes provider.WatchDirs().
Enables watching both ~/.claude/projects/ and
~/.pi/agent/sessions/ simultaneously.

4 tests: multi-dir events, no dirs, nonexistent skipped, all nonexistent.
Added snake_case tool names (bash, read, write, edit,
web_search, subagent, lsp, find, process) as fallback
mappings in ToolActivity() for pi coding agent.

2 test functions covering all Claude and pi tool names.
Refactored NewModel and api.New to accept SessionProvider
instead of demoMode bool. Provider selection now happens
in main.go based on --agent flag:
  - claude: Claude Code only (LiveProvider)
  - pi: pi coding agent only (PiProvider)
  - all: both via MultiProvider (default)

Updated usage text and tray service to support provider
injection.
Added Agent field to Session struct ('claude' or 'pi').
Pi sessions display a π prefix in the session list and
an 'Agent' row in the detail panel for identification
when monitoring multiple agents simultaneously.
Verifies DiscoverSessions against actual ~/.pi/agent/sessions
files. Skips gracefully when pi is not installed. Confirms
all discovered sessions have Agent='pi' set correctly.
Found 867 sessions in 1.36s on test machine.
Added rate estimates for Gemini, GPT-4, GPT-4o, and GPT-5
model families. Pi sessions may use multiple providers, so
cost estimation now covers a broader range of models.

6 tests: Claude tiers, relative pricing, Gemini, GPT-4/4o,
EffectiveCost direct vs fallback.
Documented --agent flag, multi-agent monitoring, pi session
discovery, π prefix indicator, and architecture changes.
Added v0.5 roadmap section for multi-agent support.
Pi stores session display names inline in JSONL via /name
command. These are now extracted during parsing and auto-
populated into the SessionNames system without overwriting
user-set names from the TUI rename feature.

Added Name field to claude.Session struct.
4 new tests: parse session_info, latest wins, auto-populate
on reload, user names preserved.
Move Session, SessionStatus, ToolCall, and ConversationMessage from
internal/claude to internal/model so that both claude and pi parsers
depend on a neutral package instead of pi importing claude for types.
@nahime0
Copy link
Copy Markdown
Member

nahime0 commented Mar 9, 2026

@vdemeester Based on your work, I've updated some logic for the tray and the api components. I've also extracted types into a new package.

I've tried some real-world usage with pi (which I wasn't familiar with, thanks for the discovery!) and it works fine.

I'll leave the PR in draft. Feel free to mark it as ready for review whenever you're happy with it, and I'll merge it.

@vdemeester vdemeester marked this pull request as ready for review March 10, 2026 14:12
@vdemeester
Copy link
Copy Markdown
Contributor Author

ah nice, thanks @nahime0 ! I think we can go ahead with it then 👼🏼

@nahime0 nahime0 merged commit bbbfd7b into illegalstudio:main Mar 10, 2026
@vdemeester vdemeester deleted the valiant-rifle branch March 10, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants