Control CLI
Exercise an interactive terminal program through a small, repeatable harness.
Prefer a repository-native demo or test harness; only assemble a temporary PTY
or terminal session when the project has no suitable one.
Harness Loop
- Identify the command, smallest fixture, expected ready marker, and cleanup
condition.
- Discover existing package scripts, PTY helpers, expect scripts, demo
recorders, or TUI tests.
- Launch in an isolated environment with deterministic variables and local
disposable data.
- Capture the initial screen or transcript.
- Send one action at a time and wait for a concrete prompt or screen marker.
- Capture the resulting transcript and any requested profile artifact.
- Stop the process and remove temporary sessions, ports, and profiles.
On Windows, prefer the project's own test runner or a checked-in Python/Node
probe. Use ConPTY or an already-installed PTY helper when available; do not add
a dependency just to run a one-off probe. On other systems, tmux, pty, or a
repository-supported terminal harness may be appropriate.
Evidence
For a bug fix or regression, run the same deterministic interaction against the
baseline and treatment and pass the captures to verify-this. For a hang, keep
the last screen, process exit state, timeout, and a stack/CPU sample when
available. For memory growth, use equal repetitions and record before/after
snapshots or a bounded allocation metric.
Prefer stable text markers and accessibility-aware terminal probes over sleeps.
If a sleep is unavoidable, state why and keep it bounded.
Safety
- Never send credentials, destructive commands, or production paths into the
controlled session.
- Do not rely on stale screen state after navigation, resize, or a prompt change.
- Do not hard-code paths, ports, or commands from another repository.
- Keep transcripts and profiles private when they contain prompts, source, or
user data.
Gotchas
- A process that exits successfully before receiving input is not proof that the
interactive flow works; assert the ready marker and the expected state change.
- Fixed sleeps hide race conditions and make a green run non-repeatable.
- A terminal transcript can miss rendering defects; use a real UI surface for
graphical claims.
- Cleanup must be verified, especially after a timeout or forced interrupt.
Troubleshooting
| Symptom |
Likely cause |
Action |
| Harness hangs |
Wrong ready marker or child process owns the terminal |
Capture the screen, inspect the process tree, then terminate cleanly |
| Input is ignored |
Program is not in the expected prompt state |
Wait for a fresh marker and send one action only |
| Works manually, fails in harness |
Hidden environment, terminal size, or timing dependency |
Record env/size and replace sleeps with state-based waits |
| Transcript is empty |
Output is on another stream or the PTY was detached |
Capture stdout and stderr through the repo-native harness and verify file size |
Source
Adapted from Cursor Team Kit's MIT-licensed control-cli workflow:
https://github.com/cursor/plugins/tree/main/cursor-team-kit/skills/control-cli
1---2name: control-cli3description: Drive and inspect an interactive CLI or TUI with a repeatable local harness, deterministic input, transcripts, and optional profiling. Use for CLI UX checks, prompt flows, startup regressions, hangs, interrupts, resize behavior, or memory growth. Do not use for a non-interactive command that a normal test can cover.4---56# Control CLI78Exercise an interactive terminal program through a small, repeatable harness.9Prefer a repository-native demo or test harness; only assemble a temporary PTY10or terminal session when the project has no suitable one.1112## Harness Loop13141. Identify the command, smallest fixture, expected ready marker, and cleanup15 condition.162. Discover existing package scripts, PTY helpers, expect scripts, demo17 recorders, or TUI tests.183. Launch in an isolated environment with deterministic variables and local19 disposable data.204. Capture the initial screen or transcript.215. Send one action at a time and wait for a concrete prompt or screen marker.226. Capture the resulting transcript and any requested profile artifact.237. Stop the process and remove temporary sessions, ports, and profiles.2425On Windows, prefer the project's own test runner or a checked-in Python/Node26probe. Use ConPTY or an already-installed PTY helper when available; do not add27a dependency just to run a one-off probe. On other systems, `tmux`, `pty`, or a28repository-supported terminal harness may be appropriate.2930## Evidence3132For a bug fix or regression, run the same deterministic interaction against the33baseline and treatment and pass the captures to `verify-this`. For a hang, keep34the last screen, process exit state, timeout, and a stack/CPU sample when35available. For memory growth, use equal repetitions and record before/after36snapshots or a bounded allocation metric.3738Prefer stable text markers and accessibility-aware terminal probes over sleeps.39If a sleep is unavoidable, state why and keep it bounded.4041## Safety4243- Never send credentials, destructive commands, or production paths into the44 controlled session.45- Do not rely on stale screen state after navigation, resize, or a prompt change.46- Do not hard-code paths, ports, or commands from another repository.47- Keep transcripts and profiles private when they contain prompts, source, or48 user data.4950## Gotchas5152- A process that exits successfully before receiving input is not proof that the53 interactive flow works; assert the ready marker and the expected state change.54- Fixed sleeps hide race conditions and make a green run non-repeatable.55- A terminal transcript can miss rendering defects; use a real UI surface for56 graphical claims.57- Cleanup must be verified, especially after a timeout or forced interrupt.5859## Troubleshooting6061| Symptom | Likely cause | Action |62|---|---|---|63| Harness hangs | Wrong ready marker or child process owns the terminal | Capture the screen, inspect the process tree, then terminate cleanly |64| Input is ignored | Program is not in the expected prompt state | Wait for a fresh marker and send one action only |65| Works manually, fails in harness | Hidden environment, terminal size, or timing dependency | Record env/size and replace sleeps with state-based waits |66| Transcript is empty | Output is on another stream or the PTY was detached | Capture stdout and stderr through the repo-native harness and verify file size |6768## Source6970Adapted from Cursor Team Kit's MIT-licensed `control-cli` workflow:71https://github.com/cursor/plugins/tree/main/cursor-team-kit/skills/control-cli