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. 04SSH経由のリモートターミナル接続
  5. 05ブラウザーでの任意のリアルタイム表示

活用シーン

  1. 01TUIアプリの操作自動化
  2. 02スクリプト実行間の端末出力確認
  3. 03リモートシェルセッションのデバッグ

インストール

コマンドをコピーしてターミナルで実行します。

$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 環境変数。例:macOS では zsh、Linux では bash)
  • モード:ログインシェル(-l フラグ)— プロファイル全体(~/.zprofile、~/.bash_profile)を読み込みます

そのため、シェルのエイリアス、PATH、環境変数を利用できます。

シェルを上書きする:

// 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 エージェントソケットのパス

注意事項:

  • SSH ターミナルには pid がありません(値は undefined です)
  • すべての Terminal メソッドは同じように動作します(write、key、snapshot など)
  • リモートシェルはローカル設定ではなく、サーバーによって決まります

重要な原則

  1. 小さなスクリプト:各スクリプトは 1 つの処理だけを行います(アプリの起動、画面の確認、キーの送信など)
  2. 出力を確認する:現在の状態を把握するため、必ず snapshot() を確認します
  3. わかりやすい名前:「term1」ではなく、「claude-monitor」や「vim-edit」を使います
  4. ターミナルは維持される:disconnect() を呼び出すと、ターミナルは次のスクリプトでも実行されたままになります

ワークフローのループ

  1. 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 倍)が含まれます

コメント