ETTerms
A native Windows terminal workspace (C# .NET 8 WinForms) — SSH, Serial Port, and local Shell (ConPTY) in one window, with a TTL scripting engine ported from MyTeraTerm for automation, plus an optional Serial MCP server that lets AI agents (Kiro CLI / Claude CLI) drive the serial port directly. Standalone, no cloud, no login.
📖 Table of Contents
- ✨ Features
- 🖼️ Layout
- 💻 System Requirements
- 📥 Installation
- 🚀 Quick Start
- 📚 Usage Guide
- 🤖 TTL Scripting
- 👥 Group Sync Execution
- ⚡ PDU Power Control
- 🤖 AI / MCP Integration
- 🔐 Data & Security
- 🔧 Troubleshooting
- 🔨 Building from Source
- 📁 Project Structure
- 🤝 Contributing
- 📜 Version History
- 📄 License
- 🙏 Acknowledgments
✨ Features
Core Functionality
-
🖥️ Multiple protocols in one workspace
- SSH (
SSH.NET): password / private key / keyboard-interactive auth, with built-in SFTP - Serial Port (
System.IO.Ports): configurable COM port, baud, data bits, parity, stop bits, handshake - Local Shell (Windows ConPTY): launch PowerShell / Cmd / Bash directly
- SSH (
-
🪟 Tiling tabbed workspace
- One-click grid layouts:
1×1 / 1×2 / 2×1 / 2×2 / 2×3 / 3×3 - Per-pane manual split:
↔horizontal /↕vertical, drag splitters to resize proportionally - Each pane hosts its own mini tab strip for multiple connections
- One-click grid layouts:
-
🗂️ Editable connection sidebar (Saved Connections)
- Nested folders with connection-count badges, expand / collapse all
- Live filter by name / host, matching branches auto-expand
- Context menu & toolbar: add / rename / delete, drag to re-categorize
- Double-click to open in the active pane; ad-hoc quick connect (not saved)
-
🤖 TTL script automation
- TTL (Tera Term Language) interpreter ported and extended from MyTeraTerm
- Runs
.ttlagainst the active session, showing file / line / current command live - Supports
send/sendln/wait/if/while/ variable math / file logging - Long-running
wait/whilecan be aborted anytime with ■ Stop
-
👥 Group sync execution
- Assign tabs to Group 1/2/3 and run one script across the whole group at once
- Sync commands:
waitall(barrier),sendlnall(send after all arrive),sendlngroup(target a member)
-
⚡ PDU power control (optional)
- Control PDU outlets over SNMP (
SnmpSharpNet) to power-cycle devices during tests - Script commands:
pduconnect/pductrl
- Control PDU outlets over SNMP (
-
🤖 AI / MCP integration (optional, planned)
- A standalone stdio Serial MCP server exposes the serial port as AI-callable tools for Kiro CLI / Claude CLI
- Tools:
serial_list/serial_open/serial_write/serial_read/serial_close - Just tell the AI to open a COM port, send a command, and read the reply
-
📊 Session RX logging
logopen/logwrite/logclosewrite session output to file
Terminal Rendering
- 🎨 Owner-drawn VT100 / ANSI control, double-buffered cell grid
- 🌑 KKTerm-style dark theme (incl. DWM dark title bar)
- 🔤 Configurable font / size / palette / scrollback
- 📋 Select / copy / paste
🖼️ Layout
┌──────┬─────────────────────┬───────────────────────────────────────┐
│ ▣ T │ Saved Connections │ Workspace (tiling) │
│ ▣ S │ 🔍 search │ ┌─────────────────┬─────────────────┐ │
│ ▣ ⚙ │ ▾ 📁 Servers (2) │ │ [tab1][tab2] + │ [tab1] + │ │
│ │ ▸ SSH srv-01 │ │ │ │ │
│ icon │ ▸ SSH nas │ │ TerminalView │ TerminalView │ │
│ rail │ ▾ 📁 Boards (2) │ │ (owner-drawn) │ (owner-drawn) │ │
│ │ ▸ COM3 @115200 │ │ │ │ │
│ │ ▸ COM7 @9600 │ ├─────────────────┴─────────────────┤ │
│ │ │ │ [Group1-A] ▶ Script ■ Stop │ │
└──────┴─────────────────────┴───────────────────────────────────────┘
Activity Rail Sidebar Tabbed / Tiling Workspace
The Activity Rail switches the three main views — Terminal / Scripts / Settings. The sidebar manages saved connections; the workspace holds multiple sessions across tabs and tiled panes.
💻 System Requirements
| Item | Requirement |
|---|---|
| OS | Windows 10 (1809+) / Windows 11 |
| Runtime | .NET 8 Desktop Runtime (Microsoft.WindowsDesktop.App 8.0.x) |
| Display | 1600×900 or higher recommended |
| Optional hardware | USB-to-Serial adapter (Serial), SNMP-capable PDU (power control) |
Check the runtime:
dotnet --list-runtimes | findstr WindowsDesktop
📥 Installation
Source build for now (no binary release yet).
git clone <repo-url> ETTerms
cd ETTerms
dotnet build
dotnet run --project src\ETTerms\ETTerms.csproj
Full steps in Building from Source.
🚀 Quick Start
Create an SSH connection
- Launch ETTerms; on the left Activity Rail switch to Terminal
- In the sidebar click + New Connection and pick type SSH
- Enter
Host/Port(default 22) /Username, choose auth (password / private key) - Double-click the connection → opens a tab in the active pane
Create a Serial connection
- Sidebar + New Connection, type Serial
- Pick
COM portandBaudRate(e.g.COM3/115200) - Double-click to open
Run a TTL script
- With a tab connected, click ▶ Script on the script bar and pick a
.ttl - The left status bar shows the running line and command live
- Press ■ Stop to abort if needed
📚 Usage Guide
Activity Rail
Switches the three main views: Terminal (workspace), Scripts (edit & run), Settings (preferences).
Connection Sidebar (Saved Connections)
| Action | How |
|---|---|
| Add folder / connection | Toolbar button or context menu |
| Rename / delete | Context menu |
| Categorize | Drag a connection or folder into a target folder (cannot drop into its own descendant) |
| Search | Top search box, live filter by name / host |
| Open connection | Double-click → opens in the active pane |
Workspace Tiling
- One-click grid layouts:
1×1 / 1×2 / 2×1 / 2×2 / 2×3 / 3×3 - Per-pane actions (top-right):
↔split horizontally,↕split vertically,+new tab,✕close - Closing a pane collapses the split so siblings fill the space; at least one pane remains
- The active pane is outlined with an accent border
Terminal
- Incoming channel bytes → ANSI parser → screen buffer → painted
- Keyboard input is encoded to byte sequences sent to the remote
- Supports scrollback, select / copy / paste; font and palette under Settings
🤖 TTL Scripting
On any connection tab's script bar, click ▶ Script to load a .ttl and run it against that connection. Full syntax and examples in
docs/ttl-script-reference.md; sample scripts in tools/scripts/.
Supported Commands
| Command | Description |
|---|---|
send 'text' |
Send text (no newline) |
sendln 'text' |
Send text plus \r\n |
wait 'string' |
Wait until the string appears in the RX buffer (infinite by default, cancellable via Stop); sets result=1 on match |
flushrecv |
Clear the RX buffer |
pause seconds |
Pause N seconds (cancellable via Stop) |
timeout = seconds |
wait timeout; 0 = infinite, N>0 aborts the script with an error on timeout |
if … then / elseif … then / else / endif |
Conditional branching (nestable) |
while … / endwhile |
Loop (nestable, cancellable via Stop) |
name = value |
Variable assignment; integer + - * / and strings; built-in result |
logopen 'file' |
Open a log file (overwrite) |
logwrite 'text' |
Write one line to the log |
logclose |
Close the log (auto-closed at script end) |
messagebox 'msg' |
Show a topmost dialog |
; comment |
Inline comment (from ; to end of line) |
:label |
Label line (skipped) |
Comparison operators (if / elseif / while): >= <= > < == != =, or none (non-zero = true).
Example: auto login
; tools/scripts/login.ttl
timeout = 10
wait 'login: '
sendln 'root'
wait 'Password: '
sendln 'toor'
wait '# '
sendln 'uname -a'
👥 Group Sync Execution
Assign multiple tabs to one Group and run a single script across all of them — ideal for syncing multiple DUTs.
- Right-click a tab → set as Group 1 / 2 / 3 (or clear); the cell footer shows a
[Group1-A]label - Click ▶ Group1 / ▶ Group2 / ▶ Group3 on the toolbar to run across the whole group
| Command | Description |
|---|---|
waitall 'string' |
All members wait for the keyword, then continue together (System.Threading.Barrier) |
sendlnall 'text' |
Each member sends once all have arrived |
sendlngroup N 'text' |
Only the specified member sends |
⚠️ Group sync commands work only in Run Group mode;
▶ Scriptand▶ Run Allreject scripts containing them and show a warning.
⚡ PDU Power Control
Control PDU outlets over SNMP to power-cycle a DUT from within a script.
; connect PDU (device 2 = iPoMan II 1202, IP 192.168.1.21)
pduconnect 2 192.168.1.21
pductrl 2 1 0 ; turn port 1 off
pause 5
pductrl 2 1 1 ; turn port 1 on
wait 'login: '
| Command | Syntax | Description |
|---|---|---|
pduconnect |
pduconnect <device> <ip> |
Connect to a PDU |
pductrl |
pductrl <device> <port> <0|1> |
Set a port off(0) / on(1) |
🤖 AI / MCP Integration
🔜 Planned (Phase 9). Let AI agents (Kiro CLI / Claude CLI) drive the serial port directly — issue commands and read output.
ETTerms ships a standalone stdio MCP server (src/ETTerms.SerialMcp/) that wraps the serial port as AI-callable tools. It runs as its own process, kept alive by the MCP client for the whole session, so it holds the COM port open across calls and can capture asynchronous device output.
Why a separate process? Each CLI shell command is a fresh process, so
open→write→readcan't keep state across calls. A long-lived MCP server maintains one connection and accumulates received bytes.
| Tool | Params | Description |
|---|---|---|
serial_list |
— | List available COM ports |
serial_open |
portName, baudRate, dataBits, parity, stopBits, handshake, newLine | Open and hold the port |
serial_write |
text, appendNewLine? | Send text (optional newline) |
serial_read |
waitFor?, timeoutMs? | Drain the RX buffer; optionally wait for a string / timeout |
serial_close |
— | Close the port |
Register in Kiro CLI:
kiro-cli mcp add --name serial --command dotnet --args "run --project src\ETTerms.SerialMcp\ETTerms.SerialMcp.csproj"
Or add it to agent.json under mcpServers (Claude CLI uses an equivalent mcpServers config). Then just tell the AI: "list COM ports, open COM3 at 115200, send AT and read the reply."
⚠️ A COM port can be opened by only one process at a time — don't open the same port in both the GUI and the MCP server.
🔐 Data & Security
- Standalone, no cloud: connection metadata is stored in a local SQLite file (
%LocalAppData%\ETTerms\ettermsdb.sqlite); no telemetry is sent. - Passwords never stored in plaintext: SQLite holds only a
CredentialKeypointing to Windows Credential Manager; actual passwords / key passphrases are accessed viaCredentialVault. - SSH host keys: the fingerprint is shown for confirmation on first connect (trust-on-first-use), then compared; mismatches warn.
- Logs contain no secrets:
AppLoggerandlogopenrecord only host / port, never credentials. - No
secret/directory: a single-user desktop app with no server-side secrets, DB passwords, or compile-time secrets.
🔧 Troubleshooting
| Issue | Likely cause | Fix |
|---|---|---|
| Startup reports missing runtime | .NET 8 Desktop Runtime not installed | Install Microsoft.WindowsDesktop.App 8.0.x; verify with dotnet --list-runtimes |
| Serial connect fails / port busy | COM port held by another app | Close other terminal apps; a COM port can be opened by only one session at a time |
| SSH auth fails | Wrong credentials / key or host-key mismatch | Check auth settings; verify the host-key fingerprint |
Script stuck on wait |
Expected string never received | Check connection & baud, verify the wait string; set timeout or press ■ Stop |
| Group script rejected | Ran a script with Group commands via ▶ Script |
Use ▶ GroupN instead |
More runbooks in docs/runbooks/.
🔨 Building from Source
Prerequisites
- .NET 8 SDK (or SDK 9/10 + .NET 8 Desktop Runtime)
- Visual Studio 2022 or VS Code + C# extension
- Git
Build & Run
dotnet --list-sdks
dotnet build
dotnet run --project src\ETTerms\ETTerms.csproj
Key NuGet Packages
| Package | Purpose |
|---|---|
SSH.NET |
SSH shell + SFTP |
System.IO.Ports |
Serial connections |
Microsoft.Data.Sqlite |
Connection metadata storage |
SnmpSharpNet |
PDU control (optional) |
Publish (self-contained=false)
dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained false
📁 Project Structure
ETTerms/
├── README.md # English (default)
├── README.zh-TW.md # Traditional Chinese
├── ARCHITECTURE.md # Full architecture doc
├── CLAUDE.md # Project memory & dev commands
├── ETTerms.slnx
├── docs/
│ ├── ttl-script-reference.md # TTL command reference
│ └── runbooks/ # Runbooks / troubleshooting
├── tools/scripts/ # Sample .ttl scripts
├── src/ETTerms/ # Main application (WinForms)
│ ├── App/ # Window shell: MainForm / ActivityRail / ConnectionSidebar / Workspace
│ ├── Terminal/ # Owner-drawn VT100: TerminalView / AnsiParser / ScreenBuffer / TerminalInput
│ ├── Sessions/ # Connection abstraction: ISessionChannel / SshChannel / SerialChannel / ShellChannel
│ ├── Connections/ # Connection data: Connection / ConnectionStore(SQLite) / CredentialVault
│ ├── Scripting/ # TTL engine: TTLInterpreter / ScriptRunner / GroupSyncContext / Pdu
│ └── Infrastructure/ # AppLogger / AppSettings / NativeTheme
└── src/ETTerms.SerialMcp/ # 🔜 Serial MCP server (stdio) — lets AI agents drive the serial port
Core design: every connection implements ISessionChannel (Write(byte[]) + event DataReceived). TerminalView and TTLInterpreter only know this abstraction, so SSH / Serial / Shell look identical to upper layers — the key to "one script engine driving multiple connection types." See ARCHITECTURE.md.
🤝 Contributing
- Fork and create a feature branch:
git checkout -b feature/your-feature - Follow Conventional Commits:
feat(serial): add auto-reconnect - Push and open a Pull Request
Conventions: PascalCase types / methods, _camelCase private fields, filename = class name; the UI layer depends only on the ISessionChannel abstraction; channel I/O runs in the background and all UI updates go back to the UI thread via Control.Invoke.
📜 Version History
v0.1.0 (in development)
- Phases 1–8 complete: window shell, connection sidebar, tiling workspace, Serial / SSH / local Shell (ConPTY) / SFTP
- Owner-drawn VT100 terminal rendering
- TTL scripting engine (ported from MyTeraTerm) + Group sync execution
- PDU power control (SNMP)
- Settings / About
- Packaging TBD
Planned
- Phase 9: Serial MCP server (
ETTerms.SerialMcp) — let AI agents (Kiro CLI / Claude CLI) drive the serial port directly
📄 License
Licensed under the MIT License — see LICENSE.
🙏 Acknowledgments
- KKTerm — UI design reference (Activity Rail + tabbed workspace + Saved Connections)
- MyTeraTerm — source of the TTL scripting engine and
AppLogger - SSH.NET, SnmpSharpNet — open-source connectivity / SNMP libraries
- Microsoft — .NET 8, WinForms, ConPTY, Credential Manager