repl

package
v0.0.40 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package repl drives a child program (typically `claude`) through an in-process pseudo-terminal so callers can type into it and read its rendered output programmatically. The interactive runner ships its per-step prompts as PTY keystrokes (Write to the master end + Enter), and reads claude's rendered output through a VT-grid emulator. API surface: NewSession / KillSession / HasSession / CapturePane / SendText / SendEnter / SendCommand / WaitForReady, keyed by session name. PLAN-8 (2026-05-22) replaced the prior `internal/tmux` shim that shelled out to a `tmux` binary; the keystroke-delivery shape is the same (writing bytes to the PTY master is what `tmux send-keys -l` did under the hood), only the PTY ownership moved in-process.

The PTY backend is github.com/aymanbagabas/go-pty, which transparently uses Unix PTYs on Linux/macOS and ConPTY on Windows — so ape works natively under Git Bash on Windows 11 without WSL or a tmux binary on PATH.

PTY output is parsed through a github.com/hinshun/vt10x VT100/xterm emulator. CapturePane returns the rendered grid as plain text — no ANSI escape sequences, no cursor-positioning noise — matching tmux's `capture-pane -p` semantics rather than dumping raw bytes.

Known limitations:

  • No external attach: a session lives and dies with the ape process. There is no equivalent of the old `tmux attach -t …` for an in-flight run. Pipeline runs that want live introspection should tail the per-step ndjson event log instead.
  • Visible-grid scrollback only. vt10x's grid is sized to claude's perceived terminal (200×50), matching the ioctl winsize claude reads. Lines that scroll off the top via `\n` at the bottom are gone — tmux's history buffer (default 2000 lines) isn't reproduced. In practice the manifest's per-step `step-out` field and the debug stderr mirror are the consumers, and both look at the most recent screenful; the authoritative stream of model output flows through the bridge's hook events, not pane capture.

Index

Constants

View Source
const PromptSettle = 300 * time.Millisecond

PromptSettle is the wait between typing a command and pressing Enter. Long prompts otherwise submit before the REPL has finished loading them — confirmed by the community /pmux pattern and anthropics/claude-code#40168. 300ms is the well-known safe value.

View Source
const ReadyGlyph = "❯"

ReadyGlyph is the prompt glyph claude renders when ready.

View Source
const ReadyPollInterval = 250 * time.Millisecond

ReadyPollInterval is how often we capture pane output while waiting for the ❯ glyph that signals the claude REPL is accepting input.

Variables

This section is empty.

Functions

func CapturePane

func CapturePane(_ context.Context, name string) (string, error)

CapturePane returns the rendered VT grid as plain text. ANSI escape sequences are interpreted, not included in the output.

func HasSession

func HasSession(_ context.Context, name string) bool

HasSession reports whether the session is alive: registered AND the child process hasn't exited yet.

func KillSession

func KillSession(_ context.Context, name string) error

KillSession terminates the child and tears down the PTY. Not-found is treated as success so callers can use it as a pre-check (`_ = KillSession(...)` before `NewSession`).

func NewSession

func NewSession(_ context.Context, name, dir string, argv []string) error

NewSession spawns argv attached to a PTY, registers it under name, and starts background readers that accumulate pane output for CapturePane / WaitForReady. argv[0] is the program; argv[1:] its arguments. dir becomes the child's working directory.

func ScrubClaudeCodeEnv added in v0.0.33

func ScrubClaudeCodeEnv(env []string) []string

ScrubClaudeCodeEnv returns env with the parent Claude Code session's nesting markers removed, so a spawned claude persists its own session transcript (the source ape scans for telemetry):

  • CLAUDECODE — the top-level "running inside Claude Code" flag;
  • CLAUDE_CODE_* — the whole parent-injected family (CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_CHILD_SESSION, CLAUDE_CODE_SSE_PORT, …). The persistence-suppressing marker is in this set; stripping the family is robust across claude versions;
  • CLAUDE_EFFORT — so the child's effort comes from ape's flags, not the parent session's inherited effort.

Everything else — ANTHROPIC_* auth included — passes through untouched. Exported so non-PTY interactive spawns (`ape chat`) can apply the same scrub.

func SendCommand

func SendCommand(ctx context.Context, name, text string) error

SendCommand types text, settles for PromptSettle, then presses Enter. The canonical "send a slash command" helper.

func SendEnter

func SendEnter(_ context.Context, name string) error

SendEnter writes a carriage return to the PTY. The kernel's ICRNL termios setting on the slave converts CR into NL for the child, so the spawned program sees a real Enter keypress.

func SendText

func SendText(_ context.Context, name, text string) error

SendText writes text literally to the PTY input without submitting it. Successor to tmux's `send-keys -l`: every byte goes through verbatim so leading slashes and special chars survive.

func SessionDone added in v0.0.24

func SessionDone(_ context.Context, name string) <-chan struct{}

SessionDone returns a channel that is closed when the session's child process exits. Returns a nil channel if the session is not registered; callers should treat nil as "never closes".

func WaitForReady

func WaitForReady(ctx context.Context, name string) error

WaitForReady polls CapturePane until the claude REPL is genuinely accepting input or ctx cancels. Known blocking modals (see blockingModals) are dismissed along the way; after a dismissal the loop re-polls before testing readiness. On timeout the returned *NotReadyError includes the last pane snapshot.

Types

type NotReadyError added in v0.0.27

type NotReadyError struct {
	Name string
	Pane string
	Err  error
}

NotReadyError is returned by WaitForReady when the REPL did not become ready before ctx expired. Pane carries the last captured snapshot so an unrecognized blocking modal is diagnosable from the error text instead of a silent stall (no-silent-caps principle).

func (*NotReadyError) Error added in v0.0.27

func (e *NotReadyError) Error() string

func (*NotReadyError) Unwrap added in v0.0.27

func (e *NotReadyError) Unwrap() error

Jump to

Keyboard shortcuts

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