Terminal Commander
БесплатноНе проверенTerminal Commander: local MCP-operated terminal/file signal channel with native Linux, Windows, and macOS packages.
Описание
Terminal Commander: local MCP-operated terminal/file signal channel with native Linux, Windows, and macOS packages.
README

Terminal Commander
Structured terminal signals for AI coding agents — bounded receipts, never silence, local-first.
npm CI License: PolyForm Noncommercial Rust MCP Platforms
Install · Why · How It Works · Tools · Innovations
Terminal Commander is a local MCP control plane for coding agents. It gives Cursor, Codex CLI, Claude Code, Claude Desktop, and other MCP clients a bounded tool surface for commands, files, PTYs, persistent shell sessions, runtime state, and signal context.
The goal is omni: an agent should never need a separate raw terminal tool.
The same runtime is available through either five compact, action-dispatched
facades (command, session, files, registry, status) or the full
51-tool granular surface. It covers one-shot commands and shell pipelines,
persistent stateful sessions, interactive PTYs (unix and Windows ConPTY),
unknown-output rule suggestion, and operator-gated remote hosts. The agent's
lane-selection map is docs/mcp/OMNI_PLAYBOOK.md.
Raw terminal output stays out of the model transcript. The agent defines keyword/regex rules, runs the command, and receives only the matching signal events plus the exit state. A quiet command (zero matches) returns a bounded receipt — exit code, suppressed-line count, short tail — so no result is ever silent or misleading.
[!IMPORTANT] The default command lane is argv-only:
argv[0]is the program, shell interpreters (sh,bash,cmd,powershell, …) are denied, and there is no string-concatenated shell anywhere in the path. Pipelines and redirects live behind the separateshell_exectool, which is disabled unless the operator enables theallow_shellpolicy capability. When that lane is enabled and no shell override is supplied, Terminal Commander follows the highest-ranked interpreter route proven bysystem_discover.
Contents
- Why Terminal Commander
- Innovations
- Quick Start
- Architecture
- The Life Of A Command
- How LLMs Should Use It
- MCP Tool Surface
- Per-Harness Sessions
- Harness Configuration
- Platform Support
- Admin CLI
- Doctor And Repair
- Update
- Environment
- Local State
- Safety Posture
- Develop From Source
- Repository Layout
- License
Why Terminal Commander
Coding agents run terminal commands constantly, and raw terminal output is hostile to them: unbounded, noisy, and token-expensive. A test suite that prints 2,802 lines costs a context-window fortune to scroll — when the only information the agent needed was "exit 0" and five matched lines. That is the product: in real use, Terminal Commander condensed exactly such a run into a five-line receipt with the exit code.
| Instead of | Use | Effect |
|---|---|---|
| Reading 2,800 lines of test scrollback | command_start_combed + rules + command_status |
Matched signals + a bounded exit receipt |
| Polling raw stdout in a loop | bucket_wait (long-poll with cursor) |
Wake on signal, heartbeat on quiet |
| Re-running a command to "see that error again" | event_context on the event pointer |
Bounded context window around the line |
tail -f build.log in a terminal you can't see |
file_watch_start + rules |
Structured events as lines append |
| One watcher loop per running job | subscription_open over many sources |
One multiplexed pull for everything |
| Pasting a whole file for one section | file_read_window / file_search |
Bounded windows and match pointers |
Three properties rank above everything else, in this order:
- Trust — every response is honest about state (
running/exited/failed/degraded), counters are near-real-time and never lie, receipts are accurate, and errors teach the caller how to recover. - Reliability — the daemon self-manages (idle self-reap with live-work veto), processes are torn down as whole trees on stop, and degraded states are disclosed loudly with a recovery path.
- LLM ergonomics — a fresh model with only the tool schemas succeeds on the first call: minimal required fields, defaults that work, lenient parameter coercion for real-world MCP clients, and one complete teaching error instead of a guessing game.
Innovations
The engineering decisions that distinguish Terminal Commander from "run a command over MCP".
Structured signals over raw streams
Every captured line flows through the sifter runtime: an Aho-Corasick pass
for keyword rules and a compiled regex-set pass for pattern rules, one event
per matching rule per frame, with named captures, severity, tags, and a
summary template. The agent reads events, not scrollback. Rules can be passed
inline per command (minimal: [{"pattern": "ERROR"}] — everything else has a
sane default), or persisted in a versioned registry and activated
globally or scoped to one bucket/job/probe. Activating a new version of a rule
supersedes the old one in that scope, so one line never fires twice.
Bounded receipts that never go silent
A command whose output matched zero rules is not an error and not an empty
success — it returns a receipt: exit code, how many lines were suppressed,
and a short tail. The same no-silence rule runs through the whole surface:
command_status for a finished quiet job carries the receipt; a stopped job
reports its real counters (snapshotted from the live probe metrics, not
zeroes); truncation is always flagged (truncated_lines, truncated_bytes,
evicted_frames).
Degraded-state disclosure with recovery hints
If an IPC error interrupts a run_and_watch wait, the response does not
become a bare error — the job handle is preserved and the result arrives with
degraded: true, the last observed state, and a recover_hint that tells the
agent exactly how to re-attach (command_status with the returned job_id).
Mutating RPCs are never auto-retried; idempotent ones may be. A daemon restart
is detectable via boot_id on subscriptions.
Evidence-backed environment beachheads
system_discover probes the host under hard deadlines instead of inferring a
route from the OS name. It reports terminal evidence, shell and PowerShell
paths/versions, WSL state, common tools, and confirmed execution status.
Confirmed programs become direct_argv routes, confirmed WSL adds a
wsl_argv route, and successful interpreter sentinels become shell routes. The
daemon filters that ranked set against the active shell capability before
returning access_routes; when allow_shell is off, it cannot advertise a
shell beachhead that the validator will reject. beachhead repeats the best
surviving route with the exact argv template an LLM can follow. A path that
merely exists is not advertised as executable, and failed or timed-out probes
remain explicit evidence. Normal command/path policy checks still apply when a
caller supplies its concrete argv and cwd.
In-process embedders construct DaemonState with DaemonState::bootstrap and
call DaemonState::discover_environment; IPC discovery uses that same method,
so embedded and daemon clients receive identical capability filtering without
opening a socket.
Self-healing daemon transport
The adapter re-probes stale availability with a bounded Health call and single-flights concurrent recovery. If the daemon disappeared, it can reuse the original supervisor plan to restore the local session. Read-only/idempotent requests may retry once after recovery; mutating requests are never replayed automatically. Version skew is refreshed through Health, and a mid-call loss returns a structured recovery contract instead of an opaque pipe error.
Buckets, context rings, and subscriptions
Signals land in per-job buckets read by cursor (bucket_events_since,
bucket_wait) so nothing is lost between polls and nothing is re-sent.
Each probe keeps a bounded context ring of raw frames so event_context
can resolve a pointer into surrounding lines on demand — context is fetched
when needed, never pushed. Subscriptions multiplex many sources behind one
predicate (severity floor, kind allowlist, tag, source set) with per-source
liveness in every pull, and sources: all auto-joins future probes.
A thin-facade adapter that cannot surprise you
terminal-commander-mcp is a stdio adapter in which every tool forwards 1:1
to a daemon IPC method. CI guards assert the adapter source contains no
process spawn, no network socket, and no direct filesystem access — the policy
gate in the daemon is the single choke point. Tool schemas are typed plainly
(no ["integer","null"] unions that real MCP clients strip), and parameters
arriving as stringified numbers or JSON-encoded arrays are coerced with
teaching errors — schema honesty for clients that aren't.
Per-session daemons with disciplined lifecycles
Each harness gets its own daemon keyed by a deterministic TC_SESSION token —
two harnesses never share state. An idle daemon self-reaps after
TC_IDLE_TTL_SECS (default 1800 s) of no real IPC, but live work vetoes the
reap: a still-running command, file watch, or PTY job keeps the daemon up so
children are never orphaned and receipts never lost. command_stop kills the
whole process tree, identity-gated so a recycled PID is never signalled.
Policy gate, audit trail, and the argv-only contract
Every command start passes a policy engine (profile-based: deny lists, path
suffix guards, per-call caps) and emits a durable audit row with
credential-redacted argv. The shell lane (shell_exec) is a separate policy
action (allow_shell, default off) — enabling it is an explicit operator
decision, never an agent's.
Rule packs: expert signal extraction in one call
registry_import_pack ships 25 curated packs so an agent gets expert
rules without authoring JSON: ansible, apt, bundler, cargo, choco,
cleanup, docker, dotnet, gcc, generic.terminal, git, go,
kubectl, make, msbuild, npm, pip, pnpm, pytest, ssh,
systemd, terraform, uv, winget, yarn. Pack rules label honestly: a
generic warning: matcher claims no language it cannot verify. When a known
tool runs without its pack, a command-start response can carry a
pack_available hint pointing at registry_import_pack.
For output whose format you do not know yet, registry_suggest_from_samples
proposes DRAFT rules from raw samples (pure-Rust heuristics). It NEVER
auto-activates: the loop is always suggest -> registry_test ->
registry_upsert -> registry_activate.
[!TIP] Prefer scoped activation (
{"kind":"job", "job_id": …}) or per-command inline rules over global activation. Globally-activated rules see every command's streams — awarning:pattern meant for cargo will also fire on git's CRLF notices.
Quick Start
Install from npm:
npm install -g terminal-commander@latest
The npm install is intentionally passive: no postinstall bootstrap, no MCP
config writes, no daemon start, no WSL install, no shell wrapper, no
hidden-window helper spawn.
Configure detected harnesses explicitly:
terminal-commander setup harness
Choose the MCP surface explicitly when the harness benefits from a smaller tool list:
terminal-commander setup harness --surface compact
compact exposes five action-dispatched facade tools. full exposes all 51
granular tools and is the server default when TC_SURFACE is unset. Both views
reach the same daemon operations and enforce the same policy.
Or target one harness:
terminal-commander setup harness --provider cursor
terminal-commander setup harness --provider codex-cli
terminal-commander setup harness --provider claude-code
terminal-commander setup harness --provider claude-desktop
Verify:
terminal-commander doctor harness
terminal-commander doctor daemon
terminal-commander session list
terminal-commander --version
When a harness starts terminal-commander-mcp, the adapter resolves the
endpoint from the inherited TC_SESSION (or TC_SOCKET override) and talks
to terminal-commanderd over local IPC. If the daemon is not already running,
the adapter spawns its own session daemon and reports the result on stderr.
Architecture
flowchart LR
subgraph harness["MCP harnesses (each env.TC_SESSION)"]
Cursor["Cursor"]
Codex["Codex CLI"]
Claude["Claude Code / Desktop"]
Other["Other MCP clients"]
end
subgraph mcp["terminal-commander-mcp"]
Stdio["rmcp stdio server\n5 compact facades or 51 full tools\n1:1 facade over IPC"]
end
subgraph sup["terminal-commander-supervisor (shared lib)"]
Ensure["ensure_daemon\nprobe + spawn-if-absent"]
Replace["replace_if_stale\nversion-gated swap"]
Session["session tokens\n+ endpoint resolution"]
end
subgraph daemon["terminal-commanderd (one per session)"]
IPC["IPC server\nUDS or named pipe"]
Policy["policy gate\nargv deny · allow_shell cap"]
Dispatch["request dispatch"]
Router["router"]
Sift["sifter runtime\nkeyword AC + regex set"]
Store["SQLite store\nevents · registry · audit"]
Buckets["buckets + context rings\n+ subscriptions"]
Idle["idle self-reap\nTC_IDLE_TTL_SECS\n(live work vetoes)"]
end
subgraph runtimes["Probe runtimes"]
Cmd["argv commands"]
Files["file read · search · watch"]
Pty["PTY sessions"]
end
Cursor --> Stdio
Codex --> Stdio
Claude --> Stdio
Other --> Stdio
Stdio --> Ensure
Ensure --> Replace
Replace -. "health handshake\n(probe_endpoint)" .-> IPC
Replace -. "spawn terminal-commanderd\nif endpoint absent" .-> IPC
Stdio <--> IPC
IPC --> Policy
Policy --> Dispatch
Dispatch --> Router
Router --> Cmd
Router --> Files
Router --> Pty
Cmd --> Sift
Files --> Sift
Pty --> Sift
Sift --> Buckets
Buckets --> Store
Dispatch --> Store
Idle --> IPC
Session -. "TC_SESSION / TC_SOCKET" .-> IPC
The MCP adapter does not spawn arbitrary commands and does not open network sockets — CI guards enforce this on the adapter source. It forwards tool calls to the daemon over local IPC; the daemon applies policy before starting argv commands or returning bounded file/context data.
probe_endpoint performs a bounded health IPC handshake, not a bare
connect. A pre-bound or stale socket that does not answer with our protocol is
rejected; ensure_daemon may spawn a fresh session daemon instead.
In-process embedding
Host applications can depend on the terminal-commanderd library and build the
full engine with DaemonState::bootstrap without starting IPC, MCP, or the CLI.
The embedding guide documents the exact boundary, the
capability-filtered discovery API, direct command path, OS restrictions, and
revision-pinning requirement.
The Life Of A Command
sequenceDiagram
participant A as Agent
participant M as terminal-commander-mcp
participant D as terminal-commanderd
participant P as Process probe
A->>M: run_and_watch argv=["cargo","test"] rules=[{"pattern":"FAIL"}]
M->>D: command_start_combed (IPC)
D->>D: shell-interpreter guard, policy gate, audit row
D->>P: spawn child (argv, no shell)
P-->>D: stdout/stderr frames (line-bounded)
D->>D: sifter combs each frame against active ∪ inline rules
D->>D: matches → signal events in the job's bucket
D->>D: non-matches → suppressed counters + context ring
M->>D: bucket_wait slices (cursor, bounded by wait_ms)
P-->>D: child exit → lifecycle event + final counters + audit
alt rules matched
M-->>A: signals[] + exit_code + state (complete:true)
else zero matches (quiet command)
M-->>A: receipt: exit code, lines_suppressed, short tail
else wait budget spent, still running
M-->>A: wait_exhausted:true + cursor → poll command_status
else IPC interrupted mid-wait
M-->>A: degraded:true + last observed state + recover_hint
end
[!NOTE] Every branch of that
altreturns something useful. There is no path where a started command yields a bare error that loses the job handle, and no path where output disappears without a count of what was suppressed.
How LLMs Should Use It
Start by discovering the host. Follow the returned beachhead rather than assuming Bash, PowerShell, WSL, or a particular executable is available:
status action=system_discover
→ environment.access_routes[] + environment.beachhead.argv_template
→ direct_argv/wsl_argv: command action=run|run_and_watch
→ shell/wsl_shell: only returned when allow_shell enables the shell lane
Use Terminal Commander whenever raw terminal scrollback would waste context or hide the signal. Compact-surface examples are shown below; full-surface callers can use the granular action names documented in the linked tool contract.
One-shot (most common):
command action=run_and_watch argv=["npm","test"] rules=[{"pattern":"FAIL"}]
→ signals + exit_code in one call; quiet runs return a receipt
Long-running, with live monitoring:
command action=run argv=["cargo","nextest","run"] rules=[{"pattern":"^\\s+FAIL"}]
command action=wait bucket_id=<returned> cursor=0 timeout_ms=10000 max_signals=50
command action=status job_id=<returned> # near-real-time counters
command action=event_context bucket_id=… event_id=…
command action=output_tail job_id=<returned> strip_ansi=true # bounded clean tail
command action=stop job_id=<returned> # kills the whole process tree
Agent rules:
- Prefer
command action=run_and_watchfor commands that finish within a minute. Compact callers may also sendcommand action=runwithwait_ms; TC honors that request through the samerun_and_watchcontract. Plainrunremains immediate; pair it withcommand action=waitfor longer jobs. - A minimal rule is just
{"pattern": "ERROR"}— id, version, matcher, severity, and summary default sanely.kindmay be the matcher override (regex/keyword) or a natural emitted event label such astest_result; explicitevent_kindwins. Severity acceptserror/warn/fatalaliases. - Use
command action=output_tailfor exploratory commands where you don't know what to match yet — bounded to 200 lines / 64 KiB, truncation-flagged. Optionalstrip_ansicleans only the returned rendering; stored frames stay raw. files action=searchaccepts an absolute file or directory. Directory searches recurse deterministically with per-file policy checks, never follow links, and remain bounded by match, byte, and entry caps.- Use
command action=sub_open+command action=sub_pullinstead of N polling loops when watching several jobs at once. wait_exhausted: truemeans STILL RUNNING — callcommand action=status; do not treat it as finished.degraded: truemeans follow therecover_hint.- Keep interpreters out of the argv lane. For a pipeline or compound command,
use
command action=execonly whenstatus action=policy_statusconfirmsallow_shell; otherwise follow a returneddirect_argv/wsl_argvroute and run each program directly as argv. - Do not pipe into shell-side
tail,head, orgrepwhen Terminal Commander needs full evidence. Use rules,command action=output_tail, orfiles action=searchso discarded lines remain observable. - Do not ask for unbounded output. Every response is intentionally capped.
MCP Tool Surface
Terminal Commander offers two schema views over the same runtime:
| Surface | Tools advertised | Intended use |
|---|---|---|
compact |
command, session, files, registry, status |
Small, stable action-dispatched surface for LLM harnesses. |
full |
51 granular tools | Explicit per-operation names for clients that prefer a broad schema. |
Set the view with TC_SURFACE=compact|full or setup harness --surface ....
Unset or unrecognized values select full. Compact calls are validated against
the chosen action before they reach the same handlers used by the full surface.
| Compact facade | Responsibilities |
|---|---|
status |
Discovery, health, policy, audit, runtime/probe state, and targets. |
command |
Start/watch/status/stop, shell execution, buckets/context, and subscriptions. |
registry |
Search, test, version, activate, deactivate, import, and suggest rules. |
files |
Bounded read/search/list/write, file watches, and workspace snapshots. |
session |
PTY commands and persistent shell sessions. |
status action=system_discover advertises the full live capability catalogue
and per-operation availability. All daemon-backed operations return a structured
daemon_unavailable error when the daemon is down instead of leaking raw
pipe/socket errors, and system_discover itself remains callable to explain
per-tool availability (requires_daemon, available, unavailable_reason).
When the daemon is reachable it also probes the execution environment with
hard time bounds: OS/architecture, terminal evidence, shell and PowerShell
paths/versions, WSL execution, and core tools. Confirmed interpreters become
ranked access_routes; beachhead is the highest-ranked route and includes the
exact argv template an LLM can follow. Unavailable or timed-out candidates stay
truthful evidence, never inferred availability. Discovery also carries the
honest omni_status capability matrix (see below).
Full contract: docs/mcp/TOOL_CONTROL_SURFACE.md. Agent lane-selection map: docs/mcp/OMNI_PLAYBOOK.md.
Omni capability matrix. system_discover.omni_status reports, honestly
from live state, which omni capabilities are wired on THIS host: shell_exec,
sessions (unix-only), pty (with a platform of posix / windows_conpty
/ unavailable), remote_targets (count + reachable), and privileged_helper
-- which is always { available: false, reason: "threat_review_pending" }
because the privileged helper is plan-only (no code shipped; blocked on a
threat review). The matrix never claims a capability that is not actually
wired.
health is a non-bumping, audit-free peek: it returns uptime_secs plus
optional idle_secs and never resets the daemon's idle timer or writes an
audit row. All other IPC requests bump the idle clock and audit normally.
[!WARNING]
shell_execexists for pipelines/compounds/redirects, but it is gated by theallow_shellpolicy capability, which is off by default and lives in the operator's config TOML — it is not an MCP-flippable parameter. On the default profile,shell_execreturnsPolicyDenied.
[!CAUTION] PTY tools are a dual backend: unix
pty-processand Windows ConPTY (portable-pty).system_discover.omni_status.pty.platformreports the live backend (posix,windows_conpty, orunavailable) per host before you call. Honest caveat: ConPTY lifecycle is live-verified on Windows, but full live ConPTY child-output end-to-end remains gated behindTC_CONPTY_E2E=1and is not yet closed on every dev host -- checksystem_discoveron native Windows before relying on it.
[!TIP] Persistent shell sessions (
shell_session_*) and workspace snapshots (workspace_snapshot_*) let an agent run multi-step work that shares cwd/env. They are gated byallow_session(default off) and are UNIX-ONLY; on a non-unix daemon they returnUnsupportedPlatform. See docs/runtime/SHELL_SESSION.md.
Per-Harness Sessions
Each harness gets a distinct daemon, keyed by an opaque token. The token is
minted by setup harness (deterministic per harness id + machine) and emitted
as env.TC_SESSION in the harness's MCP stanza.
Endpoint resolution precedence (in both the daemon at bind time and every client at connect time):
TC_SOCKET(full path/pipe override — operator escape hatch)TC_SESSION(opaque token; ASCII[A-Za-z0-9._-], 1–64 chars, ≥1 alphanumeric,defaultreserved)- Per-user default (one shared daemon)
Malformed TC_SESSION falls back to the per-user default with a stderr
warning — it never names a kernel object.
On idle, the daemon self-reaps. Each daemon tracks last-IPC time in memory
and exits gracefully after TC_IDLE_TTL_SECS of no real IPC (default 1800
seconds; 0 disables). Health/probe peeks never reset the idle clock.
Live work defers the reap: a still-running command, file watch, or PTY job
keeps the daemon alive so children are never orphaned and their receipts,
exit events, and audit rows are never lost. The shutdown path stops accepting
new connections, drains in-flight requests, then exits 0 and removes the
pidfile.
Inspect and reap sessions:
terminal-commander session list
terminal-commander session reap <token>
terminal-commander session reap --all
session reap sends a graceful Shutdown over IPC and waits for the endpoint
to go unreachable. If a daemon is wedged, the force path is identity-gated by
pid_belongs_to_daemon (daemon image + session state_dir in the live
cmdline) before any kill signal, and on Unix is re-checked again immediately
before the SIGKILL leg — a PID recycled mid-grace is never signalled.
Harness Configuration
terminal-commander setup harness detects installed harnesses and writes MCP
config for supported providers, minting a per-harness TC_SESSION. Use
--provider to restrict the write.
| Harness | Server key | Config style | Status |
|---|---|---|---|
| Cursor | terminal-commander |
JSON mcpServers |
Live |
| Codex CLI | terminal_commander |
TOML [mcp_servers.terminal_commander] |
Live |
| Claude Code | terminal_commander |
JSON mcpServers |
Live |
| Claude Desktop | terminal_commander |
JSON mcpServers |
Live |
| Gemini | terminal_commander |
Stub | Path verification pending |
| Kimi | terminal_commander |
Stub | Path verification pending |
Generated Cursor stanza (with per-harness session token):
{
"mcpServers": {
"terminal-commander": {
"type": "stdio",
"command": "terminal-commander-mcp",
"args": [],
"env": {
"TC_SESSION": "tc-<12 hex chars>"
}
}
}
}
Re-running setup harness for the same provider produces the same token; your
daemon is not churned. A malformed token in the stanza is rejected at write
time by both the JS validator and the Rust resolver.
Guides: docs/integrations/cursor.md · docs/integrations/codex-cli.md · docs/integrations/claude-code.md · docs/integrations/README.md
Platform Support
| Platform | Package | IPC | Notes |
|---|---|---|---|
| Linux x64 | @terminal-commander/linux-x64 |
Unix domain socket | Native daemon and MCP adapter |
| Linux arm64 | @terminal-commander/linux-arm64 |
Unix domain socket | Native daemon and MCP adapter |
| Windows x64 | @terminal-commander/windows-x64 |
Named pipe | Native by default; PTY via ConPTY (full child-output e2e gated by TC_CONPTY_E2E=1); shell sessions are unix-only |
| macOS x64 | @terminal-commander/mac-x64 |
Unix domain socket | Native package published |
| macOS arm64 | @terminal-commander/mac-arm64 |
Unix domain socket | Native package published |
macOS native packages are published, but the omni platform-parity work for
macOS is code plus a smoke script only -- it is NOT live-verified on a Mac
host (no Mac host available to the program), so treat the macOS runtime as
unverified until a Mac smoke run lands. Likewise, native-Windows ConPTY
child-output e2e is gated behind TC_CONPTY_E2E=1 and must be run on
CI/desktop to fully close it.
The legacy Windows-to-WSL bridge is still available for operators who
explicitly set TC_USE_LEGACY_WSL_BRIDGE=1. It is not the default Windows
path. When the bridge is used, only TC_SESSION/u crosses into WSL via
WSLENV; the ambient operator WSLENV is dropped so credential-shaped vars
cannot cross the trust boundary.
Admin CLI
| Command | Role |
|---|---|
terminal-commander |
Admin CLI: status, doctor, setup, session, rules, jobs, probes, policy, audit, update |
terminal-commander-mcp |
MCP stdio adapter launched by Cursor/Codex/Claude |
terminal-commanderd |
Local daemon for IPC, probes, policy, buckets, audit, and graceful shutdown |
Admin CLI subcommands (terminal-commander <cmd>):
| Subcommand | Purpose |
|---|---|
status |
High-level daemon status (reachable / unavailable). |
doctor harness |
Per-provider detection + configuration audit (warns on shared-daemon mode). |
doctor daemon |
Native daemon diagnostics (binary, pidfile, endpoint). |
doctor wsl |
WSL distro + runtime diagnostics. |
setup harness [--provider <id>] [--force] |
Write MCP stanzas (mint + emit env.TC_SESSION). |
setup daemon-autostart |
Install Linux/WSL daemon autostart (systemd/profile). |
session list |
Enumerate sessions (default + seeded), columns: SESSION/PID/STATE/IDLE/ENDPOINT. |
session reap [<token>] [--all] [--idle --idle-secs N] |
Graceful Shutdown-IPC; identity-gated force fallback. |
rules { list | show <id> }, jobs, probes, policy, audit [--limit N] |
Daemon-backed inspection (exit 69 when daemon unavailable; no fake data). |
update |
Run npm install -g terminal-commander@latest after a scoped Windows lock preflight. |
The Rust admin CLI does not synthesize fake daemon data. Daemon-backed
inspection commands exit 69 with an unavailable message rather than
returning empty or not-found success.
Doctor And Repair
terminal-commander doctor harness
terminal-commander doctor daemon
terminal-commander doctor wsl
terminal-commander session list
doctor harness warns "shared daemon mode" when multiple harnesses are
present and at least one is not yet configured. Repair is explicit — there is
no hidden auto-repair during npm install:
terminal-commander setup harness --force
terminal-commander setup daemon-autostart
terminal-commander session reap --all
Update
terminal-commander update
update runs the same public npm command (npm install -g terminal-commander@latest). On Windows it first runs a native preflight that
terminates only Terminal Commander binaries whose executable path is inside
the current npm platform package bin directory — no cmd.exe, PowerShell,
taskkill, hidden windows, broad process-name matches, or downloaded helper
scripts.
On startup the adapter calls ensure_daemon, then replace_if_stale when
spawn is allowed — a running daemon older than the installed adapter is
swapped (identity-gated) before tool calls proceed.
Environment
| Variable | Effect |
|---|---|
TC_SESSION |
Opaque per-harness session token; selects the endpoint and state subdir. |
TC_SOCKET |
Full endpoint override (pipe name / socket path). Wins over TC_SESSION. |
TC_DATA |
State-dir base override (default: %LOCALAPPDATA%\terminal-commanderd\state on Windows, ~/.local/share/terminal-commanderd on Unix). |
TC_IDLE_TTL_SECS |
Idle self-reap TTL in seconds (default 1800; 0 disables). |
TC_SURFACE |
MCP schema view: compact (five facades) or full (51 granular tools; default). |
TC_USE_LEGACY_WSL_BRIDGE |
1 opts into the legacy Windows→WSL bridge. |
TC_WSL_DISTRO |
Selects the WSL distro for the legacy bridge. |
TC_SKIP_DAEMON_AUTOSTART |
1 skips daemon autostart during setup harness. |
Local State
Everything lives under the per-session state dir
(<TC_DATA>/<TC_SESSION> when a session token is set):
| Path | Contents |
|---|---|
terminal-commander.toml |
Optional conventional daemon/policy config, auto-loaded when --config is omitted. |
terminal-commander.db |
SQLite store: events, rule registry (versioned, FTS5), durable activations, audit rows, workspace snapshots. |
logs/terminal-commanderd.log |
Daemon log (bind, self-checks, idle-reap decisions). |
terminal-commanderd.pid |
Pidfile: pid, version, endpoint (the probe cross-checks it). |
terminal-commanderd.lock |
Bring-up single-flight lock. |
Safety Posture
- npm install is passive; wrapper scripts use direct process spawn with
shell:false; no hidden subprocess windows. - The MCP adapter speaks stdio and local IPC only — CI guards assert no spawn/socket/fs calls in the adapter source.
- Command execution is argv-first and policy-gated; the shell lane is a separate, default-off capability with its own audit labels.
- The omni opt-in capabilities are all default-DENY and config-only (never
MCP-flippable):
allow_shell(shell_exec),allow_session(persistent sessions, unix-only),allow_remote(remote targets via an operatorssh -Lforward, no public TCP).allow_privilegedis wired but gates a PLAN-ONLY helper -- no privileged code ships (blocked on a threat review; see docs/security/PRIVILEGE_HELPER_THREAT_REVIEW.md). - Tool responses are bounded JSON, not raw stream dumps; credential-shaped argv values are redacted in audit metadata and probe rows.
ensure_daemonrequires a real Health handshake — a connectable but non-Terminal-Commander socket/pipe (squatter, stale bind, wrong process) is rejected, not silently accepted.- Force-kill on reap/replace is identity-gated at both signal legs; a PID recycled mid-grace is never signalled.
- Win→WSL forwarding is a TC-only allowlist (
TC_SESSION/u); ambientWSLENVis dropped. - Daemon idle self-reap reclaims abandoned daemons without an external watcher; live work (running commands, watches, PTYs) defers it.
- Stale daemon availability and version-skew state are refreshed through bounded Health probes; recovery is single-flight, and mutating calls are never replayed automatically.
Security model: docs/security/PRIVILEGE_MODEL.md and SECURITY.md.
Develop From Source
git clone https://github.com/special-place-ai-heaven/terminal-commander.git
cd terminal-commander
The PR gate CI runs is scripts/linux-gate.sh (plus
scripts/windows-gate.ps1 for Windows-only regressions) — running them
locally is the same check that gates your PR:
bash scripts/linux-gate.sh # linux/mac (or via WSL on Windows)
pwsh scripts/windows-gate.ps1 # Windows-only regression gate
Fast inner loop:
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace
npm --prefix packages/terminal-commander test
Local package testing:
cd packages/terminal-commander
npm link
terminal-commander --version
terminal-commander setup --help
terminal-commander session list
Testing doctrine: TESTING.md. Contributor guide: CONTRIBUTING.md.
Repository Layout
crates/ Rust workspace (9 crates; 8 published to crates.io)
core/ ids, buckets, context rings, events, activation
sifters/ rule evaluation + noise dedupe
probes/ process / file / PTY probe runtimes
store/ SQLite (events, registry, audit) + FTS5 + rule packs
supervisor/ ensure_daemon, replace_if_stale, session tokens, pidfile
ipc/ wire protocol + framing + clients (UDS / named pipe)
daemon/ terminal-commanderd — IPC, policy, router, runtimes
mcp/ terminal-commander-mcp — 5-facade compact / 51-tool full surface
cli/ terminal-commander admin CLI (local only, not on crates.io)
packages/
terminal-commander/ npm root wrapper (@latest)
terminal-commander-{linux-x64,linux-arm64,windows-x64,mac-x64,mac-arm64}/
docs/ architecture, integrations, audits, release docs
examples/provider-harness/ copy-paste MCP config examples
scripts/ CI, release, and smoke helpers
License
Licensed under the PolyForm Noncommercial License 1.0.0
(SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0). You may inspect,
study, and use the source code for noncommercial purposes. Commercial use
requires a separate license — contact the licensor for commercial terms.
Установить Terminal Commander в Claude Desktop, Claude Code, Cursor
unyly install terminal-commanderСтавит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.
Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh
Или настроить вручную
Выполни в терминале:
claude mcp add terminal-commander -- npx -y terminal-commanderFAQ
Terminal Commander MCP бесплатный?
Да, Terminal Commander MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Terminal Commander?
Нет, Terminal Commander работает без API-ключей и переменных окружения.
Terminal Commander — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Terminal Commander в Claude Desktop, Claude Code или Cursor?
Открой Terminal Commander на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Terminal Commander with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
