Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ permissions:
contents: read

concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
# A new push to a pull request replaces its running check. Every push to master finishes.
# A new push to a pull request replaces its running check. Each push to master gets a group of
# its own. GitHub cancels a pending run when a newer run joins its group, even with
# cancel-in-progress off, so one group for master let a quick series of merges cancel most of
# their checks.
group: ci-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
Expand Down
10 changes: 5 additions & 5 deletions .greptile/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"includeBranches": ["**"],
"statusCheck": true,
"ignorePatterns": "**/Cargo.lock\nresearch/*.json\nresearch/*.txt\nresearch/*.html\nbenchmarks/bin/**\ntarget/**\ndocs/screenshots/**",
"instructions": "t3term is a Rust terminal client for T3 Code's orchestrator V2 (protocol 2). It is one binary: with no arguments it opens a ratatui TUI, and its subcommands form a CLI. It connects to the T3 server already running on the machine. It never starts a server or opens T3's database. Its purpose is to replace the Electron desktop app with a client that uses far less CPU, memory and energy while still behaving like T3, so idle CPU and per-command cost are requirements, not polish. Layout: discovery.rs finds the server from ~/.t3/userdata/server-runtime.json and checks the protocol version. auth.rs issues and revokes bearer sessions through the server's own `t3 auth session` command and keeps one 30-day login per environment in the macOS Keychain through keychain.rs. http.rs and rpc.rs speak HTTP and Effect RPC over one /ws WebSocket, with chunk acks, pings and resume by afterSequence. client.rs joins them. projection.rs reduces V2 events into thread state. transcript.rs turns thread state into text. main.rs is the CLI, with documented exit codes and --json output. src/tui/ is the TUI. tests/fake_server.rs runs the real RPC client against a fake server. research/ and benchmarks/ hold measurements and the scripts that produced them. Wire formats and field names come from T3's TypeScript source (pingdotgg/t3code), so a change to them should say which T3 file or recorded event it matches. Review for correctness and credential safety first, then for CPU and memory cost, then for clarity. macOS is the only supported platform for now, and Linux or Windows code must not change the macOS build. Point out claims in README.md or research/ that the code or measurements in the PR do not support. PRs that change how the TUI looks include screenshots or recordings. Review those for behavior, not visual taste.",
"instructions": "t3term is a Rust terminal client for T3 Code's orchestrator V2 (protocol 2). It is one binary: with no arguments it opens a ratatui TUI, and its subcommands form a CLI. It connects to the T3 server already running on the machine. It never starts a server or opens T3's database. Its purpose is to replace the Electron desktop app with a client that uses far less CPU, memory and energy while still behaving like T3, so idle CPU and per-command cost are requirements, not polish. Layout: discovery.rs finds the server from ~/.t3/userdata/server-runtime.json and checks the protocol version. auth.rs issues and revokes bearer sessions through the server's own `t3 auth session` command and keeps one 30-day login per environment in the macOS Keychain through keychain.rs. http.rs and rpc.rs speak HTTP and Effect RPC over one /ws WebSocket, with chunk acks and pings. client.rs holds the operations the CLI and TUI share. Its watchers resubscribe after a dropped connection. The shell and thread streams resume with afterSequence, and subscribeServerConfig has no cursor, so it starts again from a fresh snapshot. projection.rs reduces V2 events into thread state. transcript.rs turns thread items into blocks that the CLI prints and the TUI styles. models.rs checks model, effort and mode choices against the server's config. settings.rs keeps t3term's own preferences in ~/.config/t3term/settings.json. main.rs is the CLI, with documented exit codes and --json output. src/tui/ is the TUI. tests/fake_server.rs runs the real RPC client against a fake server, and tests/cli.rs runs the built binary with a temporary home against fake servers on 127.0.0.1. docs/parity.md is the feature plan pinned to one T3 nightly, and docs/sprint.md records the PRs that build it. research/ and benchmarks/ hold measurements and the scripts that produced them. Wire formats and field names come from T3's TypeScript source (pingdotgg/t3code), so a change to them should say which T3 file or recorded event it matches. Review for correctness and credential safety first, then for CPU and memory cost, then for clarity. macOS is the only supported platform for now, and Linux or Windows code must not change the macOS build. Point out claims in README.md or research/ that the code or measurements in the PR do not support. PRs that change how the TUI looks include screenshots or recordings. Review those for behavior, not visual taste.",
"rules": [
{
"id": "credential-handling",
Expand Down Expand Up @@ -33,8 +33,8 @@
},
{
"id": "protocol-fidelity",
"rule": "Method names, message shapes, field names and enum values must match T3 orchestrator V2 protocol 2 as defined in T3's TypeScript source. Flag field names that look guessed, fields T3 requires that are left out, and enum string literals that are neither cited nor tested. The client must keep acking stream chunks, keep the 10-second ping and the 30-second dead-socket timeout, and resume with afterSequence after a reconnect. It must refuse servers that do not report protocol 2.",
"scope": ["src/rpc.rs", "src/http.rs", "src/client.rs", "src/projection.rs", "src/discovery.rs", "tests/**"],
"rule": "Method names, message shapes, field names and enum values must match T3 orchestrator V2 protocol 2 as defined in T3's TypeScript source. This covers the fields the CLI, models.rs, transcript.rs and the TUI read from snapshots, events and server config, as well as what the client sends. Flag field names that look guessed, fields T3 requires that are left out, and enum string literals that are neither cited nor tested. The client must keep acking stream chunks and keep the 10-second ping and the 30-second dead-socket timeout. After a reconnect, orchestration.subscribeShell and orchestration.subscribeThread resume with afterSequence. subscribeServerConfig has no cursor, so it resubscribes with an empty payload and starts again from the snapshot T3 sends first. The client must refuse servers that do not report protocol 2.",
"scope": ["src/rpc.rs", "src/http.rs", "src/client.rs", "src/projection.rs", "src/discovery.rs", "src/main.rs", "src/models.rs", "src/transcript.rs", "src/tui/**", "tests/**"],
"severity": "high"
},
{
Expand All @@ -51,8 +51,8 @@
},
{
"id": "idle-cost",
"rule": "Low CPU and memory use is the reason t3term exists. The TUI draws only after input or a server event, at most 30 times a second, and copies only the visible rows into each frame. Flag timers or polling loops that wake while idle, redraws that no input or event caused, rewrapping or re-rendering the whole transcript on each frame, per-frame allocations that grow with transcript length, and caches without a bound.",
"scope": ["src/tui/**", "src/client.rs", "src/rpc.rs"],
"rule": "Low CPU and memory use is the reason t3term exists. The TUI draws only after input or a server event, at most 30 times a second, and copies only the visible rows into each frame. Flag timers or polling loops that wake while idle, redraws that no input or event caused, rewrapping or re-rendering the whole transcript on each frame, per-frame allocations that grow with transcript length, and caches without a bound. In the CLI, `watch`, `wait` and `send --wait` handle every event of a stream. Flag work beyond a cheap check that each event repeats for items already printed, such as describing them again or looking up their runs, and history that `read` formats more than once per item.",
"scope": ["src/tui/**", "src/client.rs", "src/rpc.rs", "src/main.rs", "src/transcript.rs"],
"severity": "high"
},
{
Expand Down
16 changes: 13 additions & 3 deletions .greptile/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
"files": [
{
"path": "README.md",
"description": "What t3term does, the CLI commands and exit codes, TUI keys, how discovery, auth, RPC, state and rendering work, and what is not done yet"
"description": "What t3term does today: the CLI commands, flags, exit codes and --json output, TUI keys, how discovery, auth, RPC, state and rendering work, and what is not done yet. It is the current command contract"
},
{
"path": "research/next-steps.md",
"description": "The agreed plan: milestones from the shared V2 connection and read-only CLI to desktop-independent operation, T3 workflows and measured resource savings"
"path": "docs/parity.md",
"description": "The parity plan, pinned to T3 Code nightly v0.0.46-nightly.20261007.2787: the desktop's features and the order the sprint builds them in. Its yes, partial and no statuses are the baseline at that pin, not current progress. docs/sprint.md records what has merged since"
},
{
"path": "docs/parity-inventory.md",
"description": "The source inventory behind parity.md at the same pin: the desktop's commands, features, keys and layout, each cited to a file and line in T3 Code's source. Its statuses describe t3term at commit a244ccd",
"scope": ["src/tui/**", "src/transcript.rs"]
},
{
"path": "docs/sprint.md",
"description": "How the parity sprint runs: the audit after every ten merged sprint PRs, the merged sprint PRs with their merge commits, the maintenance PRs and each audit's outcome",
"scope": ["docs/**", ".github/**", ".greptile/**"]
},
{
"path": "research/agent-opus.md",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ Set `T3TERM_NO_SAVED_LOGIN=1` to use a session that lasts one run instead. t3ter
cargo test
```

The unit tests cover the reducers, Markdown wrapping, the composer, the model menus, the transcript and the lines under a file change, the tasks drawer, the thread error banner, hiding and resizing the sidebar and auth command parsing. The TUI tests draw frames on ratatui's test backend with a client that never connects, and a test build keeps saved settings in memory, so no test writes `~/.config/t3term/settings.json`. `tests/fake_server.rs` drives the real RPC client against a fake Effect RPC server, dropping the socket mid-stream to check the resume cursor, chunk acks, batched frames, duplicate suppression and error decoding. Its config tests check the subscription's exact request, a fresh snapshot with no cursor after each drop, the backoff, stopping when the TUI goes while a watch connects or waits, and both ways T3 refuses the subscription. The TUI's config tests run on a runtime that never polls its tasks, so they can count each model-list read the TUI starts and show which changes reach the chips and menus without one. `tests/cli.rs` runs the built binary with a temporary home and checks the `--json` error and exit code when the server is gone, when it isn't on protocol 2 and when the TUI has no terminal. It also runs `t3term read` against a fake server and a fake `t3` that issues a made-up session. It checks that the plain output drops the control characters of a checklist, a compaction summary and an edit's file name, and that `--json` writes them as escapes that decode to the text T3 sent. Each edit prints as one row, without its operations, error or patch. Another test runs `t3term send --wait` against a fake server that also plays T3's side of the WebSocket. It checks that each finished handoff in the turn prints its marker and endpoints on stderr once, even when T3 sends the item again, and that `--json` prints only the result. A test of `t3term wait` checks that each edit in the turn prints its row on stderr once, when it finishes, however often T3 sends it again. None of them reach a real T3 server or the Keychain, and CI runs them on macOS for every pull request.
The unit tests cover the reducers, Markdown wrapping, the composer, the model menus, the transcript and the lines under a file change, the tasks drawer, the thread error banner, hiding and resizing the sidebar and auth command parsing. The TUI tests draw frames on ratatui's test backend with a client that never connects, and a test build keeps saved settings in memory, so no test writes `~/.config/t3term/settings.json`. `tests/fake_server.rs` drives the real RPC client against a fake Effect RPC server, dropping the socket mid-stream to check the resume cursor, chunk acks, batched frames, duplicate suppression and error decoding. Its config tests check the subscription's exact request, a fresh snapshot with no cursor after each drop, the backoff, stopping when the TUI goes while a watch connects or waits, and both ways T3 refuses the subscription. The TUI's config tests run on a runtime that never polls its tasks, so they can count each model-list read the TUI starts and show which changes reach the chips and menus without one. `tests/cli.rs` runs the built binary with a temporary home and checks the `--json` error and exit code when the server is gone, when it isn't on protocol 2 and when the TUI has no terminal. It also runs `t3term read` against a fake server and a fake `t3` that issues a made-up session. It checks that the plain output drops the control characters of a checklist, a compaction summary and an edit's file name, and that `--json` writes them as escapes that decode to the text T3 sent. Each edit prints as one row, without its operations, error or patch. Another test runs `t3term send --wait` against a fake server that also plays T3's side of the WebSocket. It checks that each finished handoff in the turn prints its marker and endpoints on stderr once, even when T3 sends the item again, and that `--json` prints only the result. A test of `t3term wait` checks that each edit in the turn prints its row on stderr once, when it finishes, however often T3 sends it again. Three more check how a wait ends. `t3term wait` exits 7 with the outcome `needs-attention` when T3 asks for an approval, and 1 with the outcome `failed` when the run fails, with the reply so far on stdout. `t3term send --wait` exits 6 with `THREAD_WAIT_TIMEOUT` when the turn outlasts `--timeout`, and the error names the message it sent. None of them reach a real T3 server or the Keychain, and CI runs them on macOS for every pull request.

`docs/screenshots/` also holds `before-tui.png`, `after-tui.png`, `approval.png`, `streaming.gif`, `picker-model.png` and `tool-calls-failed.png`.

Expand Down
Loading
Loading