lazycomd

Agents

An AI agent’s shell dies when its turn ends, so anything it starts dies with it. That is the same problem lazycomd solves for you, applied to something that cannot press q: the agent starts your API, runs the tests, reads the crash log, and the process is still up on its next turn.

Two ways in — the CLI with --json, or MCP.

MCP

claude mcp add lazycomd -- lazycomd mcp

lazycomd over MCP

That is the whole setup. The server speaks line-delimited JSON-RPC on stdio and talks to the same daemon the TUI does, so what the agent starts shows up in your TUI and survives its session.

Tool What it is for
list_commands What exists, what is running, restart counts, health
get_logs The tail of one command’s output, after a failure
start_command Start; wait_sec blocks until ready, so the next step can assume it is up
stop_command Stop one command
restart_command Restart, for example after changing a config it reads at boot
who_owns_port Which process holds a port, and whether it is one of ours
add_project Read a directory’s lazycomd.yaml too, so commands can live with the code
get_metrics cpu and memory over time — whether memory is climbing or a spike settled
doctor What will fail to start, before spending an hour on something that cannot run
create_command Register a new command in the config file, group and all
get_command_config One command’s full spec — read this before updating it
update_command Replace a command’s spec
delete_command Remove a command from the config for good

The read-only tools — list_commands, get_logs, who_owns_port, doctor, get_command_config, get_metrics — are safe to auto-approve. The rest change processes or your config file; leave those prompting.

update_command replaces the whole spec, so an agent should read get_command_config first and send back what it is not changing. A spec the daemon rejects is never written: the config it refuses is the config it can no longer load.

What an agent does with it

Verify a change end to end. Edit the code, start_command with wait_sec: 30, run the test suite, get_logs on failure. The stack is still running for the next question instead of being rebuilt every turn.

Explain a blank page. list_commands shows web failed with three restarts; get_logs shows the missing module. No copy-pasting stack traces.

Settle a port fight. “Port 3000 is in use” becomes who_owns_port 3000: held by datagrip, pid 15921, not a lazycomd command. Now it can tell you who to kill rather than guessing.

Pre-flight a long task. doctor first. A depends_on pointing at a command that was renamed is a five-second fix before the work, and a confusing half-hour after it.

Register what it scaffolded. A new service gets create_command with its cwd, port, health and depends_on, so the work outlives the conversation and starts with everything else tomorrow.

Catch a crash loop it caused. Between edits, list_commands and watch restarts climb — found while the change that caused it is still on screen.

Without MCP

Every verb has --json, so an agent with a terminal needs nothing else:

lazycomd doctor --json                 # anything broken before we start?
lazycomd start api --wait 30s      # up, or a non-zero exit
lazycomd ls --json                     # what is running now
lazycomd logs api -n 50 --json     # why it is not
lazycomd port 3000 --json              # who has the port

start --wait exits non-zero if the command never becomes ready, so a shell && chain is enough control flow for most of it.

The protocol, briefly

lazycomd mcp implements initialize, tools/list, tools/call and ping over stdio. A tool failure comes back as isError content rather than a JSON-RPC error — an unknown command name is something to read and act on, not a broken session. Try it by hand:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | lazycomd mcp