| name | ssh-remote-executor |
| description | Connect to a remote host over SSH using a visible config file, verify authentication, confirm that the configured working directory is accessible, and run commands safely in the remote environment. Use when a skill-enabled agent needs a reusable plugin style SSH entrypoint with configurable host, port, user, cwd, fingerprint, and password environment settings instead of hardcoded values. Treat `config/remote-target.json` as the single source of truth for the remote root and target identity unless the user explicitly approves an override. |
SSH Remote Executor
Overview
Follow a probe-first workflow. Verify local SSH capability and authentication first, validate the remote directory second, and only then run the real command.
Prefer the config-driven entrypoint scripts/autoai_remote.py. It wraps the generic scripts/ssh_exec.py client and reads the target from the visible config file config/remote-target.json.
Treat config/remote-target.json as the only authoritative source for remote root information. Do not infer remote absolute paths from local absolute paths. Use the configured cwd as the remote root unless the user explicitly approves a target override.
This skill adds three guardrails against misleading instructions:
- The remote host is pinned by SSH fingerprint.
- Destructive command patterns are blocked unless explicitly unlocked.
- Host, port, and user overrides are blocked unless explicitly unlocked.
Workflow
- Read the visible config from
config/remote-target.json:
- host
- port
- username
- cwd
- password environment variable name
- host fingerprint
- Provide one auth method:
- password through an environment variable
- key file path
- Start with read-only probing:
- run
pwd
- verify the target directory exists
- list the first few directory entries
- Only after probe success, run the real command:
- environment checks:
python --version, nvidia-smi
- project checks:
ls, git status
- real tasks: training, testing, inference, log inspection
- If a step fails, read
references/troubleshooting.md.
Waiting Strategy
- For short remote commands, a normal command timeout is fine.
- For long-running remote jobs such as training, inference, evaluation, downloads, or large data processing, wait longer than you normally would for a local command.
- Do not give up early just because a remote job is quiet for a short period.
- Prefer a longer wait window first, and if the task is clearly long-running, switch to log-based monitoring instead of repeatedly restarting commands.
- When possible, run long jobs in a way that preserves logs, then poll those logs until completion.
- Treat remote training style workloads as minutes-to-hours tasks, not seconds-to-minutes tasks.
Remote Root Rule
- Treat
config/remote-target.json cwd as the remote project root.
- Build remote paths relative to that
cwd, not from local absolute paths.
- Treat local paths and remote paths as different coordinate systems.
- When editing code, modify local files first, then sync by relative project path.
- When executing code remotely, always enter the configured
cwd through the skill entrypoint instead of manually reconstructing remote absolute paths.
- Only change host, port, user, or cwd when the user explicitly confirms the override.
Quick Start
Pass the password through an environment variable instead of writing it into the skill, config file, or shell history:
$env:SSH_PASSWORD="your-password"
python "skills/ssh-remote-executor/scripts/autoai_remote.py" `
--password-env "SSH_PASSWORD" `
-- "pwd"
Pull or push files with the shared .gitignore rules:
python "skills/ssh-remote-executor/scripts/sync_remote_tree.py" pull
python "skills/ssh-remote-executor/scripts/sync_remote_tree.py" push
Both commands default to dry-run. Add --apply only after reviewing the plan:
python "skills/ssh-remote-executor/scripts/sync_remote_tree.py" pull --apply
Show the currently resolved non-secret target before using it:
python "skills/ssh-remote-executor/scripts/autoai_remote.py" --show-config
If you need to override a default temporarily, pass the explicit override:
python "skills/ssh-remote-executor/scripts/autoai_remote.py" `
--password-env "SSH_PASSWORD" `
--allow-target-override `
--cwd "/home/example-user/other-project" `
-- "ls -la | head -n 20"
When multiple checks are needed, run them as separate commands so failures stay easy to locate.
If a task really requires a dangerous command, get explicit user confirmation first, then opt in deliberately:
python "skills/ssh-remote-executor/scripts/autoai_remote.py" `
--password-env "SSH_PASSWORD" `
--allow-unsafe `
-- "rm -rf ./build-cache"
Operating Rules
- Read before write. Validate the remote directory, environment, and project layout before any mutating command.
- Never store plaintext passwords, private key contents, or production credentials inside the skill.
- If the goal is only connectivity validation, use read-only commands.
- If the user only provides an SSH FS alias and not a system
ssh alias, connect with explicit host, port, and username instead of depending on ~/.ssh/config.
- If the remote path contains non-ASCII characters, avoid piping raw path text through lossy shell layers. Prefer:
- pass the path directly as a Python argument
- or construct it inside Python with Unicode escapes
- Keep a confirmation step for high-impact commands such as deletes, bulk moves, database changes, or production jobs.
- Prefer the config-driven entrypoint script over the generic script unless an override is necessary.
- Keep all unique target information in the config file instead of hardcoding it into the entrypoint.
- Treat
config/remote-target.json as the single source of truth for remote root and target identity.
- Never derive remote absolute paths from local drive paths such as
F:/....
- Reuse the project root
.gitignore for pull and push filtering. Do not introduce a second ignore file.
- Do not override host, port, or username unless the user explicitly confirms the target change.
- Treat
--allow-unsafe as a deliberate escape hatch, not a convenience flag.
- Rely on host fingerprint pinning to reject unexpected servers, even if DNS, SSH FS aliases, or user messages point somewhere else.
- Wait longer for remote long-running jobs than for local short commands.
- Prefer log polling and completion checks over abandoning a remote job early.
Resources
scripts/autoai_remote.py
- project-local plugin entrypoint driven by config
- reads host, port, username, cwd, password env, and fingerprint from config
- supports
--show-config for visibility
- pins the expected host fingerprint
- blocks target overrides unless explicitly allowed
- accepts password env, key file, timeout, and cwd overrides
config/remote-target.json
- visible project configuration
- stores example non-secret remote target settings only
scripts/sync_remote_tree.py
- pulls or pushes files through SFTP
- honors the project root
.gitignore
- defaults to dry-run and requires
--apply for real transfer
scripts/ssh_exec.py
- connect with password or key file
- optionally validate the remote working directory
- blocks built-in destructive command patterns unless explicitly allowed
- execute any remote command and return exit code, stdout, and stderr
references/troubleshooting.md
- common failure modes and fixes