gpty-core
Library crate for the gpty multi-PTY emulator. This is the engine - all terminal lifecycle, ANSI parsing, concept matching, capture buffering, and grid management live here.
Module Layout
| Module | Purpose | Key Types |
|---|---|---|
types |
Data vocabulary shared across all modules | Concept, Action, TerminalConfig, CaptureMode, CapturedOutput, PaneType |
concept |
Trigger matching (gated by a combined RegexSet) and capture routing |
ConceptMatcher, match_line() |
agent_state |
Tiered, display-only agent-state detection (events / OSC / heuristics) | AgentState, StateTier, AgentStateTracker |
engine |
Runtime orchestrator; spawns terminal tasks, capture state machine | WorkspaceEngine, PtyTerminalHandle, SpawnedTerminal, TaskContext |
pty |
Cross-platform PTY lifecycle via portable-pty |
PtyHandle |
cli |
cli_view backend: one argv child (never a shell); stdout+stderr merged into a bounded line queue |
CliProcess |
parser |
Strips ANSI escape sequences; extracts plain-text lines | LineParser |
term |
Full terminal grid + damage tracking via alacritty_terminal |
TermGrid, CellInfo, GridUpdate |
color |
ANSI color mapping - named, indexed, true-color → RGB | color_to_rgb() |
keymap |
Keyboard event → byte sequence translation | key_event_to_bytes() |
lock |
Poison-tolerant locking: skip the work, report the first occurrence | lock_or_warn(), read_or_warn(), write_or_warn() |
history |
SQLite-backed scrollback history store and its write-behind thread | HistoryStore, PaneHistory |
vault |
SQLite + FTS5 index over a directory of Markdown notes - the wiki pane’s list and search | VaultStore, scan_vault(), index_vault() |
links |
[[wikilink]] parsing (occurrences with their line; code and escapes are literal) and resolution against a note set (stem or path, exact case then a unique case-insensitive match, ambiguity never silently picked) |
parse_links(), Link, NoteIndex, Resolution |
Concept System
Concepts capture terminal output and route it to a pane: a regular expression trigger, optional conditions (extra predicates over the same line), a stop condition, and a target pane kind.
Concept {
name: "cat_command",
trigger_regex: Regex::new(r"(?:^|[$#>]\s)\bcat\s+\S").unwrap(),
conditions: vec![],
enabled: true,
capture_mode: CaptureMode::UntilStop { stop_timeout_ms: 300, stop_on_input: true },
destinations: vec![Action { target_label: "code_viewer".into() }],
}How it works:
- PTY output bytes stream through the
vteparser - The parser strips ANSI escape sequences and extracts visible text lines
- Each line is tested against every registered concept’s
trigger_regex, then against itsconditions(all must match the same line). One combinedRegexSetpass answers “could anything match” first, so a line that matches no trigger - the common case - never enters the per-concept loop - On an
UntilStopmatch the terminal enters capture mode and buffers raw bytes; aSingleLinematch is queued as a notice and stops here - The capture ends on timeout (silence for N ms) or user input
- GDScript drains the completed capture and routes its text to the first pane
whose type matches the action’s
target_label- with no receiver, the raw bytes are replayed into the grid
Concepts never execute anything. A concept definition is data: it starts a
capture and chooses which pane kind displays the result. It cannot write to a
PTY. That is deliberate - the trigger is a regex over terminal output, which is
untrusted, so a concept that could act would be an execution primitive driven by
whatever a program happens to print. capture_mode is UntilStop (capture and route) or SingleLine
(notify-only: the match is published on the event socket and nothing is
captured, routed, or shown); a missing or unknown value captures, and a legacy
cmd key in an action is ignored.
Key functions: match_line(), finalize_capture(), handle_command(), feed_grid(), store_line().
Use Cases
- Read the code you just catted:
cat README.mdin a shell pane opens the file in a code viewer pane. - Error triage: route a compiler error block, a traceback, or a failing test log to the Inspector - gated behind its own opt-in (
accept_concept_captures), because captured output leaves the terminal. - Diff and log review:
git diff/git logoutput lands in a viewer pane instead of eating scrollback. - Documentation lookup: match a compiler error code and open the relevant snippet or link in an adjacent pane.
Why Flat?
All modules are single files in src/. When a module grows beyond ~200 lines or needs helper files, it will be promoted to src/<name>/mod.rs. This keeps navigation simple during prototyping while leaving room for future nesting.
Key Dependencies
| Crate | Version | Role |
|---|---|---|
portable-pty |
0.9 | Cross-platform PTY (Linux /dev/ptmx, Windows ConPTY) |
vte |
0.15 | ANSI/VT100 escape sequence parser |
alacritty_terminal |
0.26 | Full terminal grid emulator |
tokio |
1.52 | Async runtime + broadcast channel |
regex |
1.12 | Concept trigger patterns |
rusqlite |
0.31 | SQLite storage: scrollback history and the vault index |
serde |
1 | Serialization framework (derive support) |
serde_json |
1 | JSON encoding for IPC + schema output |