pi

package
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 18, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package pi implements the Agent interface for the pi coding agent (https://github.com/earendil-works/pi-mono). The npm package the embedded extension imports a type from is `@earendil-works/pi-coding-agent`.

This is an in-tree port of the previously-external entire-agent-pi plugin (github.com/entireio/external-agents/agents/entire-agent-pi). The behaviour matches the external version — most notably the active-branch resolution for Pi's tree-shaped sessions — but the integration is plumbed directly through the in-tree Agent / HookSupport / TokenCalculator / TranscriptAnalyzer interfaces rather than the external JSON-over-stdio protocol.

Index

Constants

View Source
const (
	HookNameSessionStart     = "session_start"
	HookNameBeforeAgentStart = "before_agent_start"
	HookNameAgentEnd         = "agent_end"
	HookNameSessionShutdown  = "session_shutdown"
)

Hook names — these match Pi's native event names exactly (snake_case), because the embedded TypeScript extension forwards `pi.on(<event>)` events directly. Keeping the names identical avoids a translation layer in the extension.

Variables

This section is empty.

Functions

func NewPiAgent

func NewPiAgent() agent.Agent

NewPiAgent returns a new Pi agent instance.

Types

type PiAgent

type PiAgent struct{}

PiAgent implements agent.Agent for the pi coding agent.

func (*PiAgent) AreHooksInstalled

func (a *PiAgent) AreHooksInstalled(ctx context.Context) bool

AreHooksInstalled returns true when the extension file exists and is recognisable as Entire-owned (contains the marker string).

func (*PiAgent) CalculateTokenUsage

func (a *PiAgent) CalculateTokenUsage(transcriptData []byte, fromOffset int) (*agent.TokenUsage, error)

CalculateTokenUsage sums per-assistant-message token usage from a Pi JSONL transcript starting at the given line offset. Only assistant messages on the active conversation branch contribute to the totals — see pijsonl.ResolveActiveBranch for the rationale.

func (*PiAgent) ChunkTranscript

func (a *PiAgent) ChunkTranscript(_ context.Context, content []byte, maxSize int) ([][]byte, error)

ChunkTranscript splits a Pi JSONL transcript at line boundaries.

func (*PiAgent) Description

func (a *PiAgent) Description() string

func (*PiAgent) DetectPresence

func (a *PiAgent) DetectPresence(ctx context.Context) (bool, error)

DetectPresence reports whether pi is configured for *this repo*. We only check repo-local config (.pi/) and intentionally ignore $PATH — in-tree agents follow the convention used by Claude/Gemini/OpenCode where detection means "this repo is set up for this agent", not "this agent is installed somewhere on this machine". The external plugin uses the broader $PATH check because it can't see repo state; we don't have that limitation.

func (*PiAgent) ExtractModifiedFilesFromOffset

func (a *PiAgent) ExtractModifiedFilesFromOffset(path string, startOffset int) ([]string, int, error)

ExtractModifiedFilesFromOffset scans Pi assistant tool calls from startOffset onward and returns file paths touched by file-modifying tools (`write`, `edit`). Branch-aware: only counts entries on the active conversation branch.

func (*PiAgent) ExtractPrompts

func (a *PiAgent) ExtractPrompts(sessionRef string, fromOffset int) ([]string, error)

ExtractPrompts returns user-message text from the transcript starting at the given line offset. Branch-aware (drops abandoned-branch prompts).

func (*PiAgent) FormatResumeCommand

func (a *PiAgent) FormatResumeCommand(sessionID string) string

FormatResumeCommand returns the shell command to resume a specific Pi session by ID. Pi accepts a partial UUID via `pi --session <id>`. When no session is specified, fall back to `pi --continue` which reopens the most recent session.

func (*PiAgent) GetSessionBaseDir

func (a *PiAgent) GetSessionBaseDir() (string, error)

GetSessionBaseDir returns the base directory containing per-project session subdirectories. Used by attach's cross-project fallback (searchTranscriptInProjectDirs) when a session was started from a different cwd than the current worktree root.

func (*PiAgent) GetSessionDir

func (a *PiAgent) GetSessionDir(repoPath string) (string, error)

GetSessionDir returns the directory where Pi natively stores session transcripts for repoPath: <piHome>/sessions/<encoded-repo-path>/.

Pointing this at the native store (rather than the per-repo .entire/tmp/pi/ cache populated by the agent_end hook) is what lets `entire session attach <id>` resolve cold sessions — sessions that were never hooked, or whose hook capture failed. attach falls through to GetSessionDir + ResolveSessionFile when no SessionRef is recorded in metadata, and the live Pi store is the only place that always has the transcript on disk.

Resolution order:

  1. ENTIRE_TEST_PI_SESSION_DIR (test override; no encoding applied)
  2. PI_CODING_AGENT_DIR (Pi's own override; encoding still applies)
  3. ~/.pi/agent (default)

The .entire/tmp/pi/ cache stays as a hook-internal detail — captureTranscript writes there and the TurnEnd event records that path as SessionRef in checkpoint metadata, so subsequent operations on hooked sessions go through the recorded SessionRef and never call GetSessionDir.

func (*PiAgent) GetSessionID

func (a *PiAgent) GetSessionID(input *agent.HookInput) string

GetSessionID extracts the session ID from a hook input.

func (*PiAgent) GetSupportedHooks

func (a *PiAgent) GetSupportedHooks() []agent.HookType

GetSupportedHooks maps Pi's native events to normalised lifecycle types.

  • session_start → SessionStart
  • before_agent_start → TurnStart
  • agent_end → TurnEnd
  • session_shutdown → (cleanup-only, no lifecycle event — see ParseHookEvent)

func (*PiAgent) GetTranscriptPosition

func (a *PiAgent) GetTranscriptPosition(path string) (int, error)

GetTranscriptPosition returns the JSONL line count of the file at path. Used by the strategy as the offset for incremental ExtractModifiedFiles calls. Missing files report 0 (consistent with Claude Code).

func (*PiAgent) HookNames

func (a *PiAgent) HookNames() []string

HookNames returns the verbs registered as `entire hooks pi <name>`.

func (*PiAgent) InstallHooks

func (a *PiAgent) InstallHooks(ctx context.Context, localDev bool, force bool) (int, error)

InstallHooks writes the Entire pi extension to .pi/extensions/entire/index.ts. Returns 1 if the extension was written, 0 if already up-to-date (idempotent). If the file exists but content differs (e.g., localDev vs production), it is rewritten as long as it is recognisable as Entire-owned (contains the marker). A foreign file at the same path is left untouched unless force is true — this protects user-authored extensions that happen to live at .pi/extensions/entire/index.ts.

func (*PiAgent) IsPreview

func (a *PiAgent) IsPreview() bool

func (*PiAgent) Name

func (a *PiAgent) Name() types.AgentName

func (*PiAgent) ParseHookEvent

func (a *PiAgent) ParseHookEvent(ctx context.Context, hookName string, stdin io.Reader) (*agent.Event, error)

ParseHookEvent translates a Pi hook invocation into a normalised lifecycle event. Implements agent.HookSupport.

func (*PiAgent) ProtectedDirs

func (a *PiAgent) ProtectedDirs() []string

func (*PiAgent) ProtectedFiles

func (a *PiAgent) ProtectedFiles() []string

func (*PiAgent) ReadSession

func (a *PiAgent) ReadSession(input *agent.HookInput) (*agent.AgentSession, error)

ReadSession loads a captured Pi transcript and returns it as an AgentSession.

func (*PiAgent) ReadTranscript

func (a *PiAgent) ReadTranscript(sessionRef string) ([]byte, error)

ReadTranscript reads a captured Pi JSONL session transcript from disk. SessionRef is the absolute path returned by captureTranscript().

func (*PiAgent) ReassembleTranscript

func (a *PiAgent) ReassembleTranscript(chunks [][]byte) ([]byte, error)

ReassembleTranscript concatenates JSONL chunks with newlines.

func (*PiAgent) ResolveSessionFile

func (a *PiAgent) ResolveSessionFile(sessionDir, agentSessionID string) string

ResolveSessionFile returns the path to the Pi session file for agentSessionID in sessionDir. Pi names files <timestamp>_<id>.jsonl, so glob for the matching ID; on multiple matches the lexicographically latest (most recent timestamp) wins.

Absolute paths pass through unchanged so hook payloads carrying live pi paths work without re-resolution. When no match exists, fall back to a deterministic non-existent path so downstream stat checks fail cleanly rather than panicking on an empty path.

func (*PiAgent) Type

func (a *PiAgent) Type() types.AgentType

func (*PiAgent) UninstallHooks

func (a *PiAgent) UninstallHooks(ctx context.Context) error

UninstallHooks removes the entire pi extension directory (if present).

func (*PiAgent) WriteSession

func (a *PiAgent) WriteSession(_ context.Context, session *agent.AgentSession) error

WriteSession writes a captured Pi transcript back to disk so Pi can resume from it. Pi loads sessions from arbitrary paths via `pi --session <path>`, so a plain write is sufficient.

Directories

Path Synopsis
Package pijsonl provides shared parsing primitives for Pi's session JSONL format.
Package pijsonl provides shared parsing primitives for Pi's session JSONL format.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL