| name | ssh-terminal-capture |
| description | Execute commands on a remote Linux server over SSH, capture stdout/stderr and exit codes, and render a selected subset of the terminal transcript into an image. Use when Codex needs to probe a remote host, run shell commands remotely, collect the command results, and present the useful command/output pairs as a terminal-style PNG or SVG image. |
SSH Terminal Capture
Use a config-plus-plan workflow. Put reusable SSH connection defaults in config.json, keep the command list in a small plan file, then run the bundled script once.
Workflow
- Save reusable connection fields in
config.json:
host
port
user
- exactly one auth method:
password_env, password, or key_file
- optional default remote
cwd
- optional render defaults and transcript path
- Start with read-only probes before any mutating command:
- Put only the task-specific commands into the plan file in execution order.
- Mark low-value probe commands with
"include_in_image": false so they still run and remain in the JSON transcript, but do not clutter the final image.
- Override
render or connection fields in the plan only when a one-off task needs different values.
- Ask for confirmation before adding destructive commands such as
rm, mv, chmod, database writes, or production jobs.
- Run the script and inspect the generated artifacts.
Experiment Replay Rule
When the goal is to produce coursework screenshots that must match a lab handout, use a two-layer execution model:
- Restore the remote environment to the expected baseline first.
- Example: delete an old lab directory, remove leftover users, or reset a file owner.
- These reset commands are allowed to run in the background, but should usually be marked with
"include_in_image": false.
- After the environment is clean, execute the handout commands one by one in the exact order shown by the guide.
- Do not merge visible commands with
&&.
- Do not replace a visible handout command with a longer convenience command.
- If the handout says
mkdir ~/linux_basic_lab, render exactly that command.
- When a visible command depends on being inside a directory, use command-level
"cwd" so the rendered command can stay short and match the guide.
- Example: show
mkdir -p docs logs/2024 scripts with command-level cwd set to /home/demo-user/linux_basic_lab instead of showing a rewritten absolute-path variant.
- Treat setup/reset commands and screenshot commands as different concerns.
- Setup/reset ensures the command can succeed.
- Screenshot commands must reflect what the student would type according to the handout.
- If a guide command cannot run literally in a non-interactive SSH session, keep the visible command as close as possible to the guide and document the reason for any equivalent substitution in the report.
Practical Lessons
These points were learned from repeated experiment-report generation and should be treated as default operating rules for lab screenshots:
- A clean baseline matters more than command convenience.
- If the lab directory, users, groups, files, or ownership already exist, reset them first in hidden setup commands.
- Do not leak reset logic into the visible screenshot command list.
- Visible commands must be one command per transcript record.
- Do not combine visible commands with
&&, ;, or long wrapper chains.
- If the guide lists five commands, the screenshot should normally show five separate prompts.
- Use command-level
cwd to preserve handout wording.
- Example: show
ls -l with command-level cwd instead of rewriting it to ls -l ~/linux_basic_lab.
- Example: show
mkdir -p docs logs/2024 scripts with command-level cwd instead of rewriting it to absolute paths.
- Remote command
cwd must use an absolute path.
- Do not pass
~/linux_basic_lab as command-level cwd, because the implementation uses shell quoting for cd, and quoted ~ will not expand.
- Use
/home/<user>/linux_basic_lab style absolute paths for command-level cwd.
cd ... in a screenshot line is display-only evidence, not stateful shell history.
- Each SSH command runs in its own non-interactive shell.
- If a later visible command depends on a directory change, set that command's
cwd explicitly even if the previous visible line already showed cd.
- Keep setup commands hidden unless the handout explicitly requires showing them.
- Good hidden examples: deleting an old lab directory, removing old users, reinstalling a missing package, resetting ownership.
- Good visible examples: the actual commands written in the handout.
- Prefer real output over prettified output.
- The transcript JSON should always preserve the real
stdout, stderr, exit_code, and timestamps.
- Visual cleanup should only affect rendered display, not the underlying execution record.
- Non-interactive SSH has limits; document equivalent substitutions.
- Commands like interactive
top, su - user1, or manual password entry may need a non-interactive equivalent.
- When substitution is unavoidable, keep the screenshot command as close as practical to the handout and explain the substitution in the report.
- Password-feeding helpers are implementation details, not screenshot content.
- Hide helper fragments such as
printf 'password\n' |, heredoc markers, and similar scaffolding from rendered screenshots when they are only there to drive sudo.
- Always inspect both transcript and image after generation.
- First verify that the transcript commands match the handout wording and actually succeeded.
- Then verify the image for overlap, truncation, incorrect wrapping, or hidden setup leakage.
- For non-ASCII output such as Chinese, Japanese, or Korean, verify the rendered PNG itself, not only the transcript.
- The transcript may contain correct UTF-8 text while the image shows tofu boxes or garbled characters if the renderer selected a Latin-only font.
- Prefer CJK-capable fonts before terminal monospace fonts, and measure PNG layout using real pixel width rather than display-cell width.
Config File
Use config.json at the skill root for the fixed connection data:
{
"connection": {
"host": "203.0.113.10",
"port": 22,
"user": "demo-user",
"password_env": "SSH_PASSWORD",
"cwd": "/home/demo-user/linux_basic_lab",
"timeout": 10
},
"render": {
"path": "artifacts/linux-lab.png",
"font_size": 18,
"padding": 24,
"max_output_lines": 60,
"include_exit_code": false
},
"transcript_path": "artifacts/linux-lab.json"
}
Plan File
Use this shape:
{
"render": {
"path": "artifacts/linux-lab.png",
"include_exit_code": false
},
"transcript_path": "artifacts/linux-lab.json",
"commands": [
{
"command": "pwd",
"include_in_image": false
},
{
"command": "ls logs/",
"include_in_image": true
},
{
"command": "rm -r logs/2024",
"include_in_image": true
},
{
"command": "ls logs/",
"include_in_image": true
}
]
}
Rules:
- Put stable SSH settings in
config.json for reuse and speed.
- Prefer
password_env in config.json for a reusable/public skill.
- Use
"include_in_image": false for setup checks that should stay out of the screenshot.
- Use
"include_in_image": false for reset commands that only restore the lab state and are not part of the handout itself.
- Use command-level
"cwd" only when one command needs a different directory.
- Prefer command-level
"cwd" over rewriting a handout command into a longer absolute-path command.
- Use absolute remote paths for command-level
"cwd" values.
- Use command-level
"output_limit" when one noisy command should render only a few lines.
- Prefer
password_env over inline password if you do not want credentials written to disk.
- Plan values override
config.json values when both are present.
Run
python "skills/ssh-terminal-capture/scripts/ssh_terminal_capture.py" `
--plan "skills/ssh-terminal-capture/references/example-plan.json"
If you want to use another config file:
python "skills/ssh-terminal-capture/scripts/ssh_terminal_capture.py" `
--config "D:/ssh/config.json" `
--plan "skills/ssh-terminal-capture/references/example-plan.json"
The script creates:
- a full JSON transcript with every executed command, stdout, stderr, exit code, and timing
- a terminal-style image containing only the commands marked for display
Operating Rules
- Read before write. Probe the host and directory first.
- Do not store plaintext private keys in the skill.
- Keep destructive commands out of the image unless they are essential to the story you want to show.
- If the user only needs a quick visual artifact, keep the rendered command count small.
- Default to PNG output.
- If Pillow is unavailable on Windows, the script falls back to PowerShell
System.Drawing to keep PNG generation dependency-free.
- Use SVG only when you explicitly want a text-based artifact.
Resources
scripts/ssh_terminal_capture.py
- load
config.json and merge it with a task plan
- run SSH commands from the merged config
- save a complete transcript
- render selected commands as a PNG terminal image by default, with SVG as an optional format
config.json
- store reusable SSH connection and render defaults
- keep public examples on placeholder hosts and users only
references/example-plan.json
- minimal task-specific plan