From brewtools
Connects to, configures, deploys, and administers remote Linux servers via SSH with safety gates, fail-fast rules, and robust error handling.
How this skill is triggered — by the user, by Claude, or both
Slash command
/brewtools:ssh <prompt describing what to do><prompt describing what to do>opusThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Manage remote Linux servers — connect, configure, deploy, administer with safety gates and persistent config.
Manage remote Linux servers — connect, configure, deploy, administer with safety gates and persistent config.
| Rule | Applies to |
|---|---|
Every Bash call MUST end with && echo "OK ..." || echo "FAILED ..." | ALL scripts |
On FAILED — stop phase, report error, do NOT retry same command blindly | ALL phases |
SSH commands MUST use -o ConnectTimeout=10 -o BatchMode=yes | ALL SSH calls |
| Max 2 retries per failed operation; after 2nd failure — report and stop | ALL phases |
| Non-zero script exit — read stderr, diagnose, fix root cause, retry ONCE | Scripts |
| Rule | Limit |
|---|---|
| Phase 2 (Connection Setup) — max 3 key attempts, then ask user | 3 keys |
| Phase 2 → Phase 5 round-trips — if sent back to Phase 2 more than once, stop and report | 1 re-entry |
| Phase 5 (Execute) — max 5 SSH commands per invocation; if more needed, delegate to ssh-admin agent via Task | 5 commands |
| update-agent mode — max 3 servers per run; process first 3 and report remaining | 3 servers |
| AskUserQuestion — max 3 questions per phase; summarize missing info in one combined question | 3 per phase |
| Operation | Timeout | Action on timeout |
|---|---|---|
| SSH connection test | 10s (ConnectTimeout=10) | Report "Server unreachable", stop |
| server-discover.sh | 30s (timeout 30 bash ...) | Report partial results, continue |
| Any single SSH command | 60s (timeout 60 ssh ...) | Kill, report "Command timed out", ask user |
| Entire skill invocation | 15 SSH calls total max | Stop, report progress, suggest manual continuation |
If a script fails and cannot be fixed:
| Failed script | Manual alternative |
|---|---|
| detect-mode.sh | Parse $ARGUMENTS yourself — keyword matching is simple |
| ssh-env-check.sh | Run ls ~/.ssh/id_* 2>/dev/null, ssh-add -l, cat ~/.ssh/config |
| server-discover.sh | Run individual SSH commands: uname -a, docker version, df -h |
| claude-local-ops.sh | Read/write CLAUDE.local.md directly with Read/Edit tools |
On ANY failure — before stopping or asking user — output:
SCRIPT_ERROR: <script-name>
EXIT_CODE: <code>
STDERR: <error message>
PHASE: <current phase>
ACTION: <what was attempted>
FALLBACK: <what will be tried next OR "asking user">
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/detect-mode.sh" "$ARGUMENTS"
Output format:
ARGS: [arguments received]
MODE: [detected mode]
Use the MODE value and GOTO that mode section below.
| Keyword in args | MODE |
|---|---|
| setup, new server, add server | setup |
| connect to, ssh to, login | connect |
| configure, config, harden | configure |
| update agent, refresh agent, refresh | update-agent |
| (any other text) | execute |
| (empty, no servers configured) | setup |
| (empty, servers configured) | execute (prompt user) |
Runs for ALL modes before branching.
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/ssh-env-check.sh" && echo "OK env-check" || echo "FAILED env-check"
STOP if FAILED -- fix SSH environment before continuing.
Parse output key=value pairs. Note available keys and ssh-agent status.
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/claude-local-ops.sh" list 2>/dev/null || echo "NO_SERVERS"
| Condition | Action |
|---|---|
NO_SERVERS AND mode=setup | GOTO Phase 2: Connection Setup |
NO_SERVERS AND mode=execute/connect | GOTO Phase 2 (need server first) |
1 server AND mode=connect/execute | Use as default, GOTO Phase 5 |
Multiple servers AND mode=connect/execute | AskUserQuestion: which server? Then GOTO Phase 5 |
mode=setup (servers exist) | GOTO Phase 2 (adding new server) |
mode=configure | AskUserQuestion: which server? Then GOTO Phase 5 |
mode=update-agent | GOTO Mode: update-agent |
Use AskUserQuestion:
header: "SSH Server Setup"
question: "Provide connection details for the new server."
Collect via follow-up questions if not in $ARGUMENTS:
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/ssh-env-check.sh" && echo "OK keys" || echo "FAILED keys"
Parse available keys. Try connection with each key (ed25519 first, then rsa, then ecdsa):
EXECUTE using Bash tool:
ssh-keyscan -p PORT HOST >> ~/.ssh/known_hosts 2>/dev/null && echo "OK keyscan" || echo "FAILED keyscan"
EXECUTE using Bash tool:
ssh -o BatchMode=yes -o ConnectTimeout=10 -p PORT USER@HOST echo "OK auth" 2>/dev/null || echo "FAILED auth"
Use AskUserQuestion:
header: "SSH Authentication"
question: "Key authentication failed. Choose auth method:"
options:
- label: "Password login (will set up key auth)"
description: "Connect with password, then install SSH key"
- label: "Specify key path"
description: "Provide path to an existing private key"
- label: "Cancel"
description: "Abort server setup"
If password login:
EXECUTE using Bash tool:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_SERVERNAME -N "" -C "claude@SERVERNAME" && echo "OK keygen" || echo "FAILED keygen"
Replace SERVERNAME with the server name alias.
Interactive command required. Run this in your terminal:
! ssh-copy-id -i ~/.ssh/id_ed25519_SERVERNAME.pub -p PORT USER@HOSTThis requires password entry which Claude Code cannot do non-interactively.
EXECUTE using Bash tool:
ssh -o BatchMode=yes -o ConnectTimeout=10 -i ~/.ssh/id_ed25519_SERVERNAME -p PORT USER@HOST echo "OK key-auth" 2>/dev/null || echo "FAILED key-auth"
STOP if FAILED -- key auth must work before proceeding.
EXECUTE using Bash tool:
grep -q "^Host SERVERNAME$" ~/.ssh/config 2>/dev/null && echo "EXISTS" || echo "NEW"
If NEW, add config entry using Edit/Write to ~/.ssh/config:
Host SERVERNAME
HostName HOST
User USER
Port PORT
IdentityFile ~/.ssh/id_ed25519_SERVERNAME
StrictHostKeyChecking accept-new
EXECUTE using Bash tool:
ssh -o BatchMode=yes -o ConnectTimeout=10 SERVERNAME echo "OK connection" 2>/dev/null || echo "FAILED connection"
STOP if FAILED -- connection must work before discovery.
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/server-discover.sh" "USER@HOST" PORT && echo "OK discovery" || echo "FAILED discovery"
Replace USER@HOST and PORT with actual values (or SSH config alias).
Parse output key=value pairs. Key fields:
OS, KERNEL, ARCHDOCKER_VERSION, DOCKER_COMPOSEDISK_INFO (data disks, mount points)RUNNING_CONTAINERS, SERVICESCURRENT_USER, USER_GROUPSEXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/claude-local-ops.sh" add "SERVERNAME" "HOST" "USER" "PORT" "KEYPATH" && echo "OK add" || echo "FAILED add"
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/claude-local-ops.sh" update "SERVERNAME" "OS_VALUE" "KERNEL_VALUE" "DOCKER_VALUE" "DISK_VALUE" "WORKDIR_VALUE" && echo "OK update" || echo "FAILED update"
Replace placeholders with discovered values from Phase 3.
EXECUTE using Bash tool:
grep -q "CLAUDE.local.md" .gitignore 2>/dev/null && echo "EXISTS" || (echo "CLAUDE.local.md" >> .gitignore && echo "ADDED")
EXECUTE using Bash tool:
cat "${CLAUDE_SKILL_DIR}/templates/ssh-admin-agent.md.template"
Replace placeholders in template:
{{SERVER_INVENTORY}} -- server table from CLAUDE.local.md{{SERVER_DETAILS}} -- discovered OS/Docker/disk info per server{{LAST_UPDATED}} -- current ISO timestampWrite result to .claude/agents/ssh-admin.md using Write tool.
If this is the first server, set as default automatically.
If multiple servers exist, use AskUserQuestion:
header: "Default Server"
question: "Set SERVERNAME as the default SSH server?"
options:
- label: "Yes"
- label: "No"
If yes: EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/claude-local-ops.sh" set-default "SERVERNAME" && echo "OK default" || echo "FAILED default"
Read references/safety-rules.md from skill directory for command classification rules.
Analyze $ARGUMENTS. Create execution plan:
| Step | Command(s) | Classification | Confirmation |
|---|---|---|---|
| 1 | ... | READ/CREATE/MODIFY/SERVICE/DELETE/PRIVILEGE | free/confirm |
For MODIFY/SERVICE commands -- use AskUserQuestion:
header: "SSH Action Confirmation"
question: "About to execute on SERVER:\n\n[command list]\n\nProceed?"
options:
- label: "Yes, execute"
- label: "Cancel"
For DELETE/PRIVILEGE commands -- use AskUserQuestion with explicit warning:
header: "DESTRUCTIVE SSH Action"
question: "WARNING: About to execute DESTRUCTIVE commands on SERVER:\n\n[command list]\n\nThis cannot be undone. Proceed?"
options:
- label: "Yes, I understand the risks"
- label: "Cancel"
For READ/CREATE commands -- execute freely, no confirmation needed.
For complex multi-step operations, delegate to ssh-admin agent via Task tool:
| Parameter | Value |
|---|---|
subagent_type | ssh-admin |
prompt | [Detailed task description with server info, safety classification, and specific commands to run] |
For simple single-command operations, execute directly:
EXECUTE using Bash tool:
ssh SERVERNAME "COMMAND" && echo "OK" || echo "FAILED"
If task involves Docker registry operations, read references/docker-auth-flow.md for auth patterns.
Use AskUserQuestion for registry credentials -- NEVER hardcode tokens.
| Field | Value |
|---|---|
| Server | SERVERNAME (HOST) |
| Mode | [detected mode] |
| Actions | [list of actions performed] |
| Changes | [list of changes made on server] |
| Status | success / partial / failed |
After execution: if new info discovered, update CLAUDE.local.md; if server state changed significantly, update ssh-admin agent.
bash "${CLAUDE_SKILL_DIR}/scripts/claude-local-ops.sh" update "SERVERNAME" ...
Re-discover all configured servers and refresh the ssh-admin agent.
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/claude-local-ops.sh" list
EXECUTE using Bash tool:
bash "${CLAUDE_SKILL_DIR}/scripts/server-discover.sh" "USER@HOST" PORT && echo "OK discovery" || echo "FAILED discovery"
Update CLAUDE.local.md with fresh data for each server. Regenerate .claude/agents/ssh-admin.md from template with updated inventory. Set {{LAST_UPDATED}} to current timestamp. Report what changed since last update.
# SSH [MODE]
## Detection
| Field | Value |
|-------|-------|
| Arguments | `$ARGUMENTS` |
| Mode | `[detected mode]` |
## Environment
| Component | Status |
|-----------|--------|
| SSH keys | [types found] |
| ssh-agent | [running/stopped] |
| SSH config | [exists/missing] |
| Servers configured | [N] |
## Server: [NAME]
| Property | Value |
|----------|-------|
| Host | [IP/hostname] |
| OS | [distribution] |
| Docker | [version] |
| Status | [connected/failed] |
## Actions Taken
- [action 1]
- [action 2]
## Status
[success / partial / failed]
npx claudepluginhub kochetkov-ma/claude-brewcode --plugin brewtoolsZero-knowledge SSH ops CLI for server health checks, Docker/systemd control, log tailing, Postgres introspection, and declarative deploys, without exposing credentials to the agent.
Establishes SSH connections to remote servers for executing commands, checking logs, restarting services, and managing Docker containers via bash scripts.