One skill about one bug: the report it produces reads as confident and detailed while being wrong.
What's inside
| Path | Purpose |
|---|---|
skills/remote-ops/ | How ssh flattens its arguments, the broken vs. correct forms, the base64 hop for nested quotes and SQL, and the hostname check. |
CLAUDE.md | A pointer block to paste into your project's CLAUDE.md. |
The bug
ssh joins all of its arguments into a single command string, so your local quoting is gone before
the remote shell sees it. A nested bash -lc then takes only the first word as its script:
# ✗ prints a blank line, then the OUTER HOST's hostname
ssh root@<host> -- pct exec <ctid> -- bash -lc 'echo hi; hostname'
# ✓ prints "hi", then the container's hostname
ssh root@<host> "pct exec <ctid> -- bash -lc 'echo hi; hostname'"
Both exit 0. The broken form either runs everything after a ; or && on the outer host, or runs
a bare first word with the rest discarded into $@. It has already turned a healthy production
container into a report that the app directory was missing, the service inactive and docker not
installed. Every one of those is true of the host and none of them is true of the container.
The same shape appears anywhere a remote command nests: ssh … docker exec … sh -c '…',
ssh … sudo -u app bash -lc '…', ssh … kubectl exec … -- sh -c '…'.
The rule worth memorising
When a remote result surprises you, run hostname before believing it. "Not found", "not
installed", "no such directory" is what a perfectly healthy outer host says when your command never
reached the container.
Install
unzip remote-ops.zip
mkdir -p .claude/skills
cp -R remote-ops/skills/. .claude/skills/
cat remote-ops/CLAUDE.md >> CLAUDE.md
No configuration. The examples use <host>, <ctid>, <app> and <container> placeholders.
Substitute your own, and keep the project's real hosts in a project-local skill rather than here.
Requirements
sshon PATH, andbase64on both ends for the payload form.