关于 dev-terminal
dev-terminal 是用于自动化本地及 SSH 终端会话的技能。开发者可通过简短的 TypeScript 脚本运行并检查交互式 TUI 应用。
主要功能
- 01持久化命名 PTY 会话
- 02发送文本和特殊按键
- 03捕获终端屏幕快照
- 04通过 SSH 连接远程终端
- 05可选浏览器实时监看
适用场景
- 01自动操作 TUI 应用
- 02在脚本运行间检查终端输出
- 03调试远程 Shell 会话
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ parkerhancock/ dev-terminal --skill dev-terminal安装后在 Claude Code 中输入/dev-terminal确认已加载。
详细介绍
Dev Terminal Skill
终端自动化,可在脚本执行期间保持 PTY 会话。运行 TUI 应用、发送按键、捕获屏幕输出并调试终端应用,所有状态都会持久保留。
设置
在后台终端中启动服务:
cd dev-terminal && npm install && ./server.sh &
运行脚本前,请等待 Ready 消息。
有头模式(可选)
如需进行可视化调试,请通过浏览器 UI 启动:
cd dev-terminal && ./server.sh --headed &
这会打开一个浏览器窗口,实时显示所有终端。适用于:
- 观察 AI 操作的实时过程
- 必要时手动介入
- 调试 TUI 交互
编写脚本
在 dev-terminal/ 目录中使用 heredoc 内联运行脚本:
cd dev-terminal && npx tsx <<'EOF'
import { connect, sleep } from "./src/client.js";
const client = await connect();
// Create or get a named terminal
const term = await client.terminal("my-app", {
command: "python",
args: ["-m", "my_module"],
cols: 120,
rows: 40,
});
// Wait for app to start
await sleep(1000);
// Get screen snapshot
const snap = await term.snapshot();
console.log("=== SCREEN ===");
console.log(snap.text);
client.disconnect();
EOF
Shell 默认设置
默认情况下,终端使用:
- Shell:用户的默认 Shell(
$SHELL环境变量,例如 macOS 上的 zsh、Linux 上的 bash) - 模式:登录 Shell(
-l标志),会加载完整配置文件(~/.zprofile、~/.bash_profile)
这意味着可以使用你的 Shell 别名、PATH 和环境变量。
覆盖 Shell:
// Use a specific shell
const term = await client.terminal("my-term", {
command: "bash",
args: ["-l"], // keep as login shell
});
// Non-login shell (only loads ~/.bashrc, not ~/.bash_profile)
const term = await client.terminal("my-term", {
command: "bash",
args: [], // no -l flag
});
// Run a command directly (not a shell)
const term = await client.terminal("my-term", {
command: "python",
args: ["-m", "my_app"],
});
SSH 远程终端
通过 SSH 连接到远程服务。API 与本地终端完全相同。
import { connect } from "./src/client.js";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
const client = await connect();
// SSH with private key
const term = await client.terminal("remote-server", {
ssh: {
host: "192.168.1.100",
username: "deploy",
privateKey: fs.readFileSync(path.join(os.homedir(), ".ssh/id_rsa"), "utf8"),
},
});
// SSH with password
const term = await client.terminal("remote-server", {
ssh: {
host: "example.com",
username: "admin",
password: "secret",
},
});
// SSH with agent (uses SSH_AUTH_SOCK)
const term = await client.terminal("remote-server", {
ssh: {
host: "example.com",
username: "admin",
agent: process.env.SSH_AUTH_SOCK,
},
});
// SSH with custom port and encrypted key
const term = await client.terminal("remote-server", {
ssh: {
host: "example.com",
port: 2222,
username: "admin",
privateKey: fs.readFileSync("/path/to/key", "utf8"),
passphrase: "key-passphrase",
},
});
SSH 选项:
| 选项 | 类型 | 说明 |
|---|---|---|
host | string | 远程主机名或 IP(必填) |
port | number | SSH 端口(默认:22) |
username | string | SSH 用户名(必填) |
password | string | 密码身份验证 |
privateKey | string | 私钥内容(而非路径) |
passphrase | string | 加密密钥的密码短语 |
agent | string | SSH Agent 套接字路径 |
注意:
- SSH 终端没有
pid(值为undefined) - 所有 Terminal 方法的用法相同(write、key、snapshot 等)
- 远程 Shell 由服务器决定,而非本地设置
核心原则
- 脚本精简:每个脚本只做一件事(启动应用、检查屏幕、发送按键)
- 观察输出:始终检查
snapshot()以查看当前状态 - 使用描述性名称:使用
"claude-monitor"、"vim-edit",而非"term1" - 终端会持久运行:
disconnect()会让终端继续运行,供下一个脚本使用
工作流循环
- 编写脚本以执行一项操作
- 运行脚本并观察屏幕输出
- 评估——显示了什么?操作成功了吗?
- 决定——继续发送输入,还是任务已完成?
- 重复以上步骤,直到完成
客户端 API
const client = await connect();
// Get or create named terminal (uses default shell as login shell)
const term = await client.terminal("name");
// With options
const term = await client.terminal("name", {
command: "python", // override shell/command
args: ["-m", "my_app"], // override args (clears default -l flag)
cols: 120,
rows: 40,
cwd: "/path/to/dir",
env: { MY_VAR: "value" },
});
// List all terminal names
const names = await client.list();
// Close/kill a terminal
await client.close("name");
// Disconnect (terminals persist)
client.disconnect();
终端方法
// Send raw input
await term.write("hello");
// Send special keys
await term.key("enter");
await term.key("up");
await term.key("ctrl+c");
// Send a line (adds Enter)
await term.writeLine("ls -la");
// Get screen state
const snap = await term.snapshot();
console.log(snap.text); // Plain text (no ANSI codes)
console.log(snap.lines); // Array of lines
console.log(snap.alive); // Process still running?
// Get SVG rendering (for visual analysis)
const svgSnap = await term.snapshot({ format: "svg" });
console.log(svgSnap.svg); // SVG string
// Resize
await term.resize(80, 24);
// Clear buffer
await term.clear();
// Wait for text to appear
const found = await term.waitForText("Ready", { timeout: 5000 });
// Wait for process to exit
const exitCode = await term.waitForExit({ timeout: 10000 });
特殊按键
使用 term.key() 并传入以下名称:
| 类别 | 按键 |
|---|---|
| 方向键 | up、down、left、right |
| 控制键 | enter、tab、escape、backspace、delete |
| Ctrl+X | ctrl+c、ctrl+d、ctrl+z、ctrl+l、ctrl+a、ctrl+e、ctrl+k、ctrl+u、ctrl+w、ctrl+r |
| 功能键 | f1 - f12 |
| 导航键 | home、end、pageup、pagedown、insert |
示例:调试 TUI 应用
cd dev-terminal && npx tsx <<'EOF'
import { connect, sleep } from "./src/client.js";
const client = await connect();
// Start the TUI
const term = await client.terminal("claude-monitor", {
command: "../.venv/bin/python",
args: ["-m", "claude_monitor"],
cols: 120,
rows: 40,
cwd: "..",
});
// Wait for it to render
await sleep(2000);
// Capture screen
const snap = await term.snapshot();
console.log("=== SCREEN OUTPUT ===");
console.log(snap.text);
console.log("=== ALIVE:", snap.alive, "===");
client.disconnect();
EOF
示例:交互式会话
# Script 1: Start app
cd dev-terminal && npx tsx <<'EOF'
import { connect, sleep } from "./src/client.js";
const client = await connect();
const term = await client.terminal("my-tui", {
command: "htop",
});
await sleep(1000);
const snap = await term.snapshot();
console.log(snap.text);
client.disconnect();
EOF
# Script 2: Send keys (terminal persists!)
cd dev-terminal && npx tsx <<'EOF'
import { connect, sleep } from "./src/client.js";
const client = await connect();
const term = await client.terminal("my-tui"); // Reconnect to existing
await term.key("down");
await term.key("down");
await sleep(500);
const snap = await term.snapshot();
console.log(snap.text);
client.disconnect();
EOF
# Script 3: Quit
cd dev-terminal && npx tsx <<'EOF'
import { connect } from "./src/client.js";
const client = await connect();
const term = await client.terminal("my-tui");
await term.write("q");
client.disconnect();
EOF
错误恢复
如果遇到问题,请检查终端状态:
cd dev-terminal && npx tsx <<'EOF'
import { connect } from "./src/client.js";
const client = await connect();
// List all terminals
const terminals = await client.list();
console.log("Active terminals:", terminals);
// Check specific terminal
if (terminals.includes("my-app")) {
const term = await client.terminal("my-app");
const snap = await term.snapshot();
console.log("Alive:", snap.alive);
console.log("Exit code:", snap.exitCode);
console.log("Last output:", snap.lines.slice(-20).join("\n"));
}
client.disconnect();
EOF
提示
- TUI 应用需要时间:启动后使用
sleep(),等待其完成渲染 - 检查运行状态:TUI 可能会崩溃,请检查
snap.alive - 清除内容以获取最新状态:在重要的快照前使用
term.clear() - 输出较长时:
snap.lines会返回最后约 120 行(终端高度的 3 倍)
评论
支持 Markdown