mcpField
D
dev-terminal

dev-terminal

github.com/parkerhancock开发247 次安装42 周前更新
代码仓库

关于 dev-terminal

dev-terminal 是用于自动化本地及 SSH 终端会话的技能。开发者可通过简短的 TypeScript 脚本运行并检查交互式 TUI 应用。

主要功能

  1. 01持久化命名 PTY 会话
  2. 02发送文本和特殊按键
  3. 03捕获终端屏幕快照
  4. 04通过 SSH 连接远程终端
  5. 05可选浏览器实时监看

适用场景

  1. 01自动操作 TUI 应用
  2. 02在脚本运行间检查终端输出
  3. 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 选项:

选项类型说明
hoststring远程主机名或 IP(必填)
portnumberSSH 端口(默认:22)
usernamestringSSH 用户名(必填)
passwordstring密码身份验证
privateKeystring私钥内容(而非路径)
passphrasestring加密密钥的密码短语
agentstringSSH Agent 套接字路径

注意:

  • SSH 终端没有 pid(值为 undefined)
  • 所有 Terminal 方法的用法相同(write、key、snapshot 等)
  • 远程 Shell 由服务器决定,而非本地设置

核心原则

  1. 脚本精简:每个脚本只做一件事(启动应用、检查屏幕、发送按键)
  2. 观察输出:始终检查 snapshot() 以查看当前状态
  3. 使用描述性名称:使用 "claude-monitor"、"vim-edit",而非 "term1"
  4. 终端会持久运行:disconnect() 会让终端继续运行,供下一个脚本使用

工作流循环

  1. 编写脚本以执行一项操作
  2. 运行脚本并观察屏幕输出
  3. 评估——显示了什么?操作成功了吗?
  4. 决定——继续发送输入,还是任务已完成?
  5. 重复以上步骤,直到完成

客户端 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+Xctrl+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