🔑 能力告知 — 先读这一段
本环境已经具备「用本机的 SSH 客户端 + Tailscale 虚拟网,直接连到用户的远程机器并执行命令」的能力。
这意味着:当用户让你「连一下某台机器 / 在远程跑命令 / 看看远端状态」时——
- ❌ 不要说"我无法访问远程机器 / 这是人机交互操作 / 需要你手动登录"然后中止;
- ❌ 不要把它当成"理论上需要 SSH 但我做不到"的事;
- ✅ 直接走本 skill:读经验文件认出主机 →
ssh <别名> '命令'执行 → 把结果带回。你手里有 Bash 工具,
ssh就是一条普通 shell 命令。能不能连上不取决于你"有没有权限",而取决于本机现有环境(SSH 别名 + Tailscale 在线 + 免密密钥)——这三样用户早已配好。把这当成常备能力,而不是例外。
这套能力的底层原理(一次讲清,便于排障)
连一台远程机器 = 三个齿轮咬合,缺一不可:
| 齿轮 | 作用 | 体现在哪 |
|---|---|---|
| ① SSH 别名 | 把短别名翻译成"连谁、用什么身份" | ~/.ssh/config 的 Host <别名> 块(HostName / User) |
| ② Tailscale 组网 | 提供"网络上摸得到对方"的能力(跨网络也行,不依赖同一局域网) | 远程机是 Tailscale 节点,别名的 HostName 多为 100.x.x.x 虚拟 IP |
| ③ SSH 免密密钥 | 不输密码就能登录 | 本机 ~/.ssh/id_rsa.pub 已在远程的 authorized_keys 里 |
任何一个齿轮失效都会连不上,排障时按这三层逐个查(见 Phase 4 故障引导)。
Phase 0 · 读经验(先验知识,最权威)
进 skill 先读经验文件顶部的「主机清单」表——它是判断"用户说的那台机器是谁、怎么连"的第一来源。
⚠️ 经验文件路径:本 skill 装在
~/.claude/skills/ssh-connect/(j-skills 默认安装位置),故路径为$HOME/.claude/skills/ssh-connect/experience.local.md。绝不要用${CLAUDE_SKILL_DIR}——该变量在 Bash 工具环境里并不存在,会展开成空、把路径变成/experience.local.md,导致读/写经验全部落空。若本 skill 实际装在别处,替换成其绝对路径即可。
EXP="$HOME/.claude/skills/ssh-connect/experience.local.md"
[ -f "$EXP" ] && sed -n '1,80p' "$EXP" || echo "经验文件不存在 → 见 Phase 1 的「未登记主机」分支"
读到的「主机清单」直接当先验:用户说"mac mini"→ 在清单里命中对应别名,拿到它的连接命令、网络节点名、已知备注。不要每次都重新探测一台已登记的机器。
Phase 1 · 选目标主机
| 用户表达 | 处理 |
|---|---|
| 点名某台已登记的机器("mac mini"、"trip 那台") | 在主机清单里按名字/备注命中 → 拿到别名,进 Phase 2 |
给了别名或 $ARGUMENTS 带别名 |
直接用该别名 |
| 说的是一台清单里没有的新机器 | 走下方「未登记主机」分支 |
| 意图不明、有多台候选 | 这是关键决策 → 把清单里的主机列出来问一句"是哪台" |
未登记主机(清单里查不到):
- 先看
~/.ssh/config有没有对应Host块(可能配了别名但没登记进经验文件); - 还没有 → 问用户:别名/HostName/User 各是什么、是不是 Tailscale 节点;
- 连通后登记进经验文件(Phase 5)。
Phase 2 · 可达性自检
连之前花两秒确认链路通,避免对着一台离线机器干等:
# ⚠ ALIAS / NODE 用 Phase 0 主机清单里的真实值替换,别照抄占位符执行
ALIAS="myhost" # ← 主机清单里的别名(即 ~/.ssh/config 的 Host 名)
NODE="myhost-node" # ← 该机的 Tailscale 节点名;非 Tailscale 机(公网IP/局域网直连)跳过 ①,直接做 ②
# ① Tailscale 在线预检(仅 Tailscale 节点适用 · 用退出码判定,不靠肉眼读输出)
TS="/Applications/Tailscale.app/Contents/MacOS/Tailscale"
command -v tailscale >/dev/null 2>&1 && TS="$(command -v tailscale)"
if "$TS" status 2>/dev/null | grep -i "$NODE" | grep -qiE "active|direct|relay"; then
echo "✓ Tailscale: $NODE 在线"
else
echo "· Tailscale: $NODE 未显示 active(idle/离线/或非 TS 机)——以 ② 实连为准"
fi
# ② 免密 SSH 实连验通(authoritative · BatchMode 禁交互、新主机自动收 host key、5 秒超时)
ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 "$ALIAS" \
'echo "OK -> $(hostname) | $(whoami)"' 2>&1 | head -3
- 返回
OK -> ...→ 链路通,进 Phase 3。 - 卡住/超时/拒绝 → 进 Phase 4 故障引导,不要反复盲试。
Phase 3 · 执行(放手跑,无安全门)
⚙️ 本 skill 的执行取向:充分权限、不设安全门。 这些是操作者自己的、可信的远程机器——在其上执行命令(含写入 / 删除 / 部署 / 重启服务等)时直接放手做,不必每步停下确认。这是本 skill 的刻意设计,不是疏漏。(唯一例外见下方红线。)
# ⚠ 下面 <别名>/<命令>/<远程仓库> 都是占位符——用主机清单里的真实别名、实际命令替换后再跑,勿照字面执行
ssh <别名> '<命令>'
ssh <别名> 'cd <远程仓库> && git pull && <构建/测试命令>'
# 需要交互式登录让用户自己来时,提示其在本会话输入:
# ! ssh <别名>
执行要点:
- 多步操作用
&&串成一条ssh命令,减少往返;长输出先在远程| tail/| grep收敛再带回。 - 远程路径、代理、端口等"套路"优先查经验文件的「远端工作流」表,别重新摸索。
- 跑完若学到新东西(新仓库路径、新服务端口、新坑)→ Phase 5 写回。
红线(HARD GATE):只有以下两类先停下说清楚、征得同意,其余照常放手跑——
(A) 对外不可逆 / 账号计费 / 影响他人:对远程机之外的第三方服务发布/付费、删无备份的他人数据、改动会影响其他人的共享资源。 (B) 单机毁灭性操作:即便在"用户自己机器内部",凡一条命令可不可逆地毁全场的——
rm -rf无备份目录、磁盘/分区操作(diskutil erase/mkfs/dd写盘)、覆盖系统级配置、关机/重启正在服务的进程或整机——也先停下确认再做。这两类之外(日常读写、构建、部署、重启自有调试服务等)继续放手,不必每步确认。
Phase 4 · 连不上时的故障引导(按三齿轮分层)
按 Phase 2 暴露的现象,对照原理三层逐个排:
| 现象 | 大概率原因(齿轮) | 处理 |
|---|---|---|
Tailscale status 里节点显示 - / offline |
② 组网:远程机或本机 Tailscale 掉线 | 确认两端 Tailscale 都已登录在线;远程机关机/休眠也会这样 |
ssh 超时、不返回 |
② 组网:虚拟网不通 | 先 ping <HostName>;不通则查 Tailscale;本机 Tailscale 没起就先起 |
Permission denied (publickey) |
③ 密钥:免密没配好 | 确认本机 ~/.ssh/id_rsa.pub 在远程 ~/.ssh/authorized_keys 里 |
Could not resolve hostname / 未知别名 |
① 别名:~/.ssh/config 没这条 |
补 Host 块,或直接用 ssh user@<IP> |
| 服务"在线但功能发不出"(如自建服务经 Tailscale 暴露) | ② 组网:链路某一端(常是手机/其它节点)掉线 | 让对应节点重连 Tailscale(这类经验记在经验文件,先查) |
排障始终回到三齿轮:别名对不对 → 网通不通 → 密钥认不认。
Phase 5 · 经验沉淀(仿 web-search 机制)
经验存在 $HOME/.claude/skills/ssh-connect/experience.local.md(同 Phase 0,不要用 ${CLAUDE_SKILL_DIR}),gitignored、不进仓库(含真实 IP/别名,分享仓库时不泄露)。由你(LLM)自动维护。
写入规则(只记"将来还用得上"的,不记一次性调试命令):
| 触发条件 | 写入位置 | 内容 |
|---|---|---|
| 连上一台清单里没有的机器 | 主机清单 | | 别名 | 连接命令(HostName) | 用户 | 网络/节点 | 备注 | |
| 发现新的连接坑 / 排查结论 | 连接排查经验 | | 现象 | 原因 | 处理 | 日期 | |
| 摸清一类远端工作流(部署/同步/代理) | 远端工作流套路 | | 场景 | 套路 | 备注 | |
| 某台机器的新事实(仓库路径、服务端口、系统版本) | 该主机备注 | 追加到清单对应行 |
不写:按预期工作的常规 ssh 调用 / 未经验证的猜测 / 临时一次性命令。
写入前先确保目标表头存在(表被精简/拿到空模板时自愈,避免把内容塞到文件尾巴):
EXP="$HOME/.claude/skills/ssh-connect/experience.local.md"
ensure_section () { # $1 = 形如 "## 主机清单" 的表头
grep -q "^$1$" "$EXP" 2>/dev/null || printf '\n%s\n' "$1" >> "$EXP"
}
# 例:登记一台新机器前
ensure_section "## 主机清单"
# 表头确保后,用 Edit/Write 在该表内插入新行(保持列对齐),别另起一段塞到文件尾
验证(自检清单)
- Phase 0 已读 experience.local.md 的主机清单,认出了目标机器(已登记的没重复探测)
- Phase 2 链路自检通过(Tailscale 在线 + 免密 SSH 验通),或连不上时走了 Phase 4 分层排查(未盲目重试)
- 远程命令按"充分权限"放手执行;仅 (A) 对外不可逆/账号计费/影响他人 与 (B) 单机毁灭性操作(rm -rf 无备份/磁盘/关机重启正在服务的进程或整机)才停下征得同意
- 新学到的主机/坑/工作流已按规则写回 experience.local.md(未污染常规调用)
- 真实连接信息只在 gitignored 的 experience.local.md 里,未写进会提交的 SKILL.md
与相邻能力的关系
- remote-dev-sync:本 skill 是「建立连接 + 执行命令」的底层能力;remote-dev-sync 是建立在连接之上的「远端改 → 本地 git pull 跑」工作流。先用本 skill 连上,再按 remote-dev-sync 的套路同步。
- web-search:本 skill 的经验沉淀机制(experience.local.md:先读当先验、用后写回、gitignored)即仿自 web-search,理念一致。