Tools
Auto Claude Code
Openclaw plugin that launches a persistent Claude Code worker in tmux and watchdogs it via cron
Install
openclaw plugins install ./auto-claude-code-plugin
README
# auto-claude-code
OpenClaw plugin that launches a persistent Claude Code worker inside a
tmux session and babysits it from outside โ so long-running tasks
("refactor X", "migrate Y", "build the Z pipeline") run autonomously
while you check in from chat.
One plugin, one worker. `openclaw` chat messages route `/acc โฆ`
(alias for `/auto-claude-code โฆ`) to the plugin, which drives a single
`auto-claude-worker` tmux session, watches its state, and streams new
output back to whatever channel you launched from (Feishu / Telegram /
terminal).
---
## Quick start
```bash
git clone <this repo>
openclaw plugins install ./auto-claude-code-plugin
openclaw daemon restart # gateway caches plugin modules โ restart after install / updates
```
Prerequisites:
- `tmux` on PATH
- `claude` CLI (Claude Code 2.x) on PATH
- Node.js โฅ 20
- `openclaw` CLI + gateway installed (this plugin runs inside openclaw โ
it does not stand alone; openclaw provides the slash-command routing,
cron scheduler, and chat-channel delivery)
Verify:
```bash
openclaw plugins list | grep auto-claude-code
openclaw auto-claude-code help
```
---
## Usage
Every command works as either `/acc <sub> โฆ` in chat or
`openclaw auto-claude-code <sub> โฆ` on the CLI.
| Subcommand | What it does |
|---|---|
| `launch "<task>"` | start a worker, install watchdog, begin streaming |
| `status` | tmux alive? task progress? heartbeat age? |
| `attach` | print the `tmux attach` command |
| `stop` | soft-exit the worker (Ctrl-C ladder), retire state, remove cron |
| `send <msg>` | paste a free-form message into the running REPL |
| `form <csv>` | answer a multi-tab form in one shot (also auto-invoked when a chat reply looks like form answers) |
`launch` flags: `--cwd <path>`, `--model <name>`, `--force`,
`--instant` / `--summary`, `--resume <sessionId>`, `--continue`,
`--max-continues <n>`, `--every <dur>`.
### Answering Claude Code's multi-tab forms from chat
Claude Code's `AskUserQuestion` tool renders a tab bar like
`โ โ Scope โ Format โ Submit โ`. The watcher detects the bar, walks
each tab capturing the question + options, and pushes the whole thing
to your chat channel. You reply with a CSV:
```
/acc 1, 2, 3, 4 # pick numbered options per tab
/acc 1, "my-repo", skip, submit
```
Tokens: `\d{1,2}` (numbered option), `"quoted text"` (text fields),
`skip`, `submit`. The last answer auto-Enters, so `submit` is optional.
Malformed tokens are rejected with a specific position error โ you get
a retry prompt instead of garbage in the TUI.
In `--force` mode the watcher skips the chat round-trip and answers the
form itself via a one-shot `openclaw agent` call.
---
## Architecture
```
โโโโโโโโโโโโโโโโโโโโโโโโ /acc launch ...
โ Chat (Feishu/TG/โฆ) โโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโ โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ openclaw-gateway (long-running systemd svc) โ
โ โโ routes /acc โ index.js โ
โ โโ spawns watcher, schedules cron โ
โ โโ forwards plain chat โ `acc send` โ
โโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโ
โผ โผ โผ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ tmux pane โ โ bin/watcher.mjsโ โ openclaw system โ
โ auto-claude-โโโโค (one per โโโถโ event โ chat โ
โ worker โ โ launch) โ โ channel โ
โ running โ โ โ โโโโโโโโโโโโโโโโโโโโ
โ `claude` โ โ tails jsonl, โ
โ โโโถโ parses TUI, โ
โ (dangerouslyโ โ auto-fills โ
โ skip perms)โ โ forms, reports โ
โโโโโโโโโโโโโโโ โ DONE / DEAD โ
โโโโโโโโโโโโโโโโโโ
```
### Components
- **`index.js`** โ subcommand router. Parses `/acc <sub> <args>` and
dispatches to `src/commands.js`.
- **`src/commands.js`** โ `cmdLaunch`, `cmdStatus`, `cmdAttach`,
`cmdStop`, `cmdSend`, `cmdForm`. Decides when to spawn a fresh
watcher / install a cron / stream or batch.
- **`src/launch.js`** โ creates (or reuses) the `auto-claude-worker`
tmux session, pre-accepts Claude Code's per-project trust prompt in
`~/.claude.json`, runs `claude --dangerously-skip-permissions`, waits
until a session jsonl appears.
- **`src/tmux.js`** โ typed wrapper around the `tmux` CLI. Every
recoverable failure becomes a `TmuxError` with a `.kind` discriminator
(`no-session`, `timeout`, `buffer`, `permission`, โฆ). The key
primitive `paneCurrentCommand()` reads `#{pane_current_command}` โ
used to answer *is claude still running in this pane?* without
guessing from pane text (which the old sentinel-based check got wrong
when claude echoed the sentinel).
- **`src/tui.js`** โ pane-text parsers: `detectMultiStepForm(pane)`
recognises the `โ โ tab โ tab โ Submit โ` bar; `extractModalBlock`
pulls the modal question + options; `modalSignature` gives a compact
fingerprint used to detect TUI auto-advance across keypresses.
- **`src/claude-state.js`** โ reads
`~/.claude/projects/<slug>/<sessionId>.jsonl` (turn stream) and
`~/.claude/tasks/<sessionId>/*.json` (Claude Code 2.1's TaskCreate
store; the old TodoWrite-todos path is gone).
- **`bin/watcher.mjs`** โ one long-running subprocess per launch. Tails
the jsonl every `POLL_MS`, batches per-turn output, pushes chat
events through openclaw, detects modals / forms and either surfaces
them for the user (non-force) or auto-fills them (force). Enforces
single-instance via `state.watcherPid` + `process.kill(pid, 0)` probe
at startup.
- **`bin/tick.mjs`** โ cron entry for `--summary` mode. Called every
`every` (default 5 min), produces a digest of progress + a
QUIET/AWAITING/DONE branch report.
- **`src/decide.js`** โ decides the branch (`DEAD`, `AWAITING`,
`DONE`) from a jsonl+tasks+tmux snapshot.
- **`src/poke.js`** โ builds the "keep working" prompt; used by
force-continue and recovery pokes.
- **`src/state.js`** โ JSON state file at
`~/.state/auto_claude_code/state.json`: `workerSessionId`,
`workerTmuxName`, `watcherPid`, `autoContinueCount`, cursors. One
writer (watcher) + many readers (status commands, tick script).
### Delivery modes
| Mode | Trigger | Loop |
|---|---|---|
| `instant` (default) | live stream | `bin/watcher.mjs` tails jsonl, pushes per-event |
| `summary` | `--summary` | `bin/tick.mjs` on `*/5 * * * *` cron, digests since last cursor |
The watcher is the source of truth for force-continue; the tick script
is a pure reporter in both modes.
### `--force` mode
Watcher reacts in real time. When a Claude turn ends with `end_turn`
but tasks aren't all `completed`, it pastes a "continue" prompt into
the tmux. When a multi-tab form appears, it asks `openclaw agent` for
each tab's answer (with jsonl context) and drives the form
automatically. Hard cap: `--max-continues` (default 5).
---
## Configuration
Set via `openclaw config set` or the plugin config block. All keys
optional.
| Key | Default | Meaning |
|---|---|---|
| `stateDir` | `~/.state/auto_claude_code` | State dir |
| `defaultCwd` | `$HOME` | Fallback `cwd` when launch has none |
| `defaultModel` | โ | `--model` default |
| `tmuxName` | `auto-claude-worker` | Shared tmux session name |
| `force` | `false` | Default for `--force` |
| `maxAutoContinues` | `5` | Force-mode circuit breaker |
| `instant` | `true` | Stream vs. digest |
| `sticky` | `true` | Auto-forward plain chat โ worker after launch |
| `every` | `*/5 * * * *` | Summary-mode cron interval |
| `nudgeAfterSec` | `600` | Stale-jsonl threshold for nudge |
| `recoverAfterSec` | `1800` | Stale-jsonl threshold for recover |
| `notify.channel` | โ | `feishu` / `telegram` / โฆ |
| `notify.to` | โ | channel-specific target (open_id, chat_id) |
Env-var overrides for the watcher:
- `AUTO_CLAUDE_CODE_STATE_DIR`
- `AUTO_CLAUDE_CODE_FORCE` (`1` / `0`)
- `AUTO_CLAUDE_CODE_MAX_CONTINUES`
- `AUTO_CLAUDE_CODE_WATCHER_POLL_MS` (default `2000`)
- `AUTO_CLAUDE_CODE_NOTIFY_CHANNEL` / `_TO` / `_ACCOUNT`
---
## Safety
`claude` starts with `--dangerously-skip-permissions`. The worker can
run **any shell command in its cwd without asking**. Scope each
`launch` to a known-safe `cwd`; don't point it at your home directory
unless you're OK with that blast radius.
`/acc stop` sends Ctrl-C to the pane (up to 3ร) before falling back to
`tmux kill-session`. The Ctrl-C path lets Claude flush its session
jsonl cleanly so you can `--resume` it later.
---
## Development
```bash
node --test test/*.test.mjs # full suite
node --check src/commands.js bin/watcher.mjs # catch ParseError
openclaw cron list # smoke-load plugin in a fresh subprocess
openclaw daemon restart # reload the gateway's cached modules
```
The gateway is a long-running systemd service. Without a restart it
keeps running the plugin code from when it was last started โ so after
every JS edit, run `openclaw daemon restart`, otherwise chat commands
exercise stale code.
### Layout
```
auto-claude-code-plugin/
โโโ index.js # subcommand router
โโโ openclaw.plugin.json # manifest + configSchema
โโโ skills/auto-claude-code/ # SKILL.md (hints for the host LLM)
โโโ bin/
โ โโโ watcher.mjs # per-launch tailer + form driver
โ โโโ tick.mjs # cron tick (summary mode)
โโโ src/
โ โโโ commands.js # /acc subcommand handlers
โ โโโ launch.js # tmux bring-up + trust-prompt bypass
โ โโโ tmux.js # tmux CLI wrapper + typed errors
โ โโโ tui.js # pane-text parsers (forms, modals)
โ โโโ claude-state.js # jsonl + tasks/ readers
โ โโโ decide.js # branch decision
โ โโโ tick/plan.js
... (truncated)
tools
Comments
Sign in to leave a comment