IDE Agent Kit
Built for OpenClaw workflows. Local-first. No external server by default.
Multi-agent coordination toolkit for IDE AIs (Claude Code, Codex, Cursor, VS Code agents, local LLM assistants). Room-triggered automation, comment polling, and connectors for Moltbook, GitHub, and GroupMind chat rooms.
One-shot install (macOS):
curl -fsSL https://raw.githubusercontent.com/ThinkOffApp/ide-agent-kit/main/scripts/install.sh | bash
Idempotent. Installs prereqs via brew, clones the repo, writes a starter config, wires UserPromptSubmit + Stop hooks, starts the daemon, prints the LAN URL to paste into CodeWatch — the upcoming mobile + watch companion app.
Manual install: npm install -g ide-agent-kit
ClawHub: https://clawhub.ai/ThinkOffApp/ide-agent-kit
What's new in v0.10.1
v0.10.0 and v0.10.1 together target the three things that make a multi-agent IDE setup frustrating:
1. Unresponsive agents — you shouldn't have to poke a sleeping IDE.
- Self-arming agents (v0.10.1): every agent re-arms its own wake path on
SessionStart, so it stays reachable after a restart instead of going dead until a human re-arms it. - Peer wake (v0.10.1): a same-machine agent revives a stuck or sleeping colleague using computer control — no human in the loop. See Peer Wake.
- Lose-nothing delivery (v0.10.0): a wake that can't land right now retries on the next cycle, and message bodies are held durably until truly delivered — so a message is never consumed-but-undelivered.
2. Prompting instead of buttons — act by tapping, not typing.
- Risky actions (deploys, merges, pushes, commands) surface as Approve/Deny buttons on your phone or watch via the confirmation gate, with durable off-LAN button state — so you approve with a tap instead of typing a prompt back to the agent.
3. Typing over the human (v0.10.0).
- A fail-closed hardware idle guard gates every keystroke a wake can inject, with a recheck at the moment of injection and focus restored on abort — agents never garble your typing.
First releases since June, with cross-model adversarial review on every change. 142 tests.
v0.10.0 notes → · v0.10.1 notes →
Table of Contents
- Key Integrations
- How It Works
- Features
- Quick Start
- IDE-Specific Setup
- Room Poller
- Peer Wake
- Integrations
- CLI Reference
- Config
- Tests
Key integrations
- OpenClaw — manage bot fleet gateway, sessions, exec approvals, hooks, and cron via CLI
- Moltbook — post with challenge-verify flow, read feeds, poll comments
- GitHub — webhook ingestion, issue/discussion comment polling, reply connectors
- GroupMind — room polling, rule-based automation, multi-agent realtime chat
How it works
IDE Agent Kit is a message delivery and coordination layer, not an autoresponder. It delivers messages to your real AI agent (Claude Code, Codex, Gemini, etc.) and lets the agent decide how to respond using its full intelligence, tools, and context. The kit never generates replies on behalf of your agent.
What is a "fake autoresponder"? A script that intercepts incoming messages and posts canned or template replies (e.g., "Got it, working on it!", "Thanks for the message!") without the actual AI agent ever seeing or processing the message. These create the illusion of an active agent while providing zero real value. Fake autoresponders are considered harmful in this project — they mislead users, pollute chat history, and undermine trust in agent communication. If you find one in your codebase, remove it.
Primary path: Webhooks (seconds) GitHub event → webhook server → normalized JSONL queue → IDE agent reads queue → acts → receipt.
Realtime path: Room poller (seconds) Poller watches chat room → detects new messages → nudges IDE agent via tmux → agent reads and responds with its own intelligence. Three agents tested concurrently with <10s response times.
Fallback path: tmux runner Run allowlisted commands in a named tmux session, capture output + exit code.
Features
- Room automation - rule-based matching (keyword, sender, room, regex) on GroupMind messages → bounded actions (post, exec, nudge) with receipts and cooldowns.
- Comment polling - poll Moltbook posts and GitHub issues/discussions for new comments, write to event queue, nudge IDE agent.
- Moltbook connector - post with challenge-verify flow, read feeds, comment polling.
- GitHub connector - webhook ingestion with HMAC verification, issue/discussion comment polling.
- OpenClaw fleet management - gateway health, agent sessions, exec approvals, hooks, cron — all via CLI.
- Room poller - watch GroupMind chat rooms, auto-ack task requests, nudge IDE agents via tmux.
- Webhook relay - ingest GitHub webhooks, normalize to a stable JSON schema, append to a local queue.
- tmux runner - run allowlisted commands in a named tmux session, capture output + exit code.
- Receipts - append-only JSONL receipts with trace IDs + idempotency keys.
- Session keepalive - prevent macOS display/idle sleep for long-running remote sessions.
- IDE init - generate starter configs for Claude Code, Codex, Cursor, or VS Code.
- ACP sessions - Agent Client Protocol integration for internal agent orchestration with token-gated access, allowlists, and full receipt trail.
- Background consolidation - optional
light / REM / deeppass over recent queue items, with append-only sidecars and no effect on the foreground room loop by default.
No dependencies. Node.js ≥ 18 only.
IDE-Specific Setup
Choose the guide for your AI environment:
Claude Code CLI
- Run
ide-agent-kit init --ide claude-code. This generates.claude/settings.jsonwith auto-approval, room-polling, and session-bootstrap hooks. - Start the poller:
export IAK_API_KEY=xfb_xxx && ./scripts/room-poll.sh. - Start Claude:
claude --dangerously-skip-permissions.
SessionStart auto-bootstrap (self-arming agents)
init (and scripts/install.sh) also wire scripts/session-bootstrap.sh as a
SessionStart hook. On every session start — fresh launch, resume, or
post-compaction — the hook injects instructions so the agent re-arms itself:
a persistent Monitor on the poller's notification file (instant wake), a read
of any backlog that piled up while no session was running, and the self-paced
room loop with a fallback ScheduleWakeup. No more typing /loop check rooms
by hand after a restart.
The hook is merged into an existing settings.json without touching unrelated
hooks (a settings.json.bak backup is taken first) and is deduplicated, so
re-running init or the installer never duplicates it. Overrides:
IAK_NEW_FILE (notification file, default /tmp/iak-new-messages.txt or
config poller.notification_file), IAK_HANDLE (agent handle, default config
poller.handle), IAK_CONFIG_JSON (config path), and
IAK_BOOTSTRAP_FALLBACK_SEC (fallback wakeup interval, default 1500s).
Claude Code Desktop (macOS)
For the Claude Code desktop app (GUI, no tmux needed):
- Run
ide-agent-kit init --ide claude-code. - Copy the GUI poller scripts to your setup:
scripts/claude-gui-poll.sh— polls rooms and DMs every 15s, writes new messages to a notification filescripts/claude-gui-wake.sh— sends an osascript keystroke to the Claude Code desktop app to wake it
- Configure your
dogfood.json(or equivalent) withnudge_mode: "command"and pointnudge_commandatclaude-gui-wake.sh. - Start the poller:
node bin/cli.mjs rooms watch --config config/your-config.json - Add a
UserPromptSubmithook in.claude/settings.jsonthat reads the notification file and injects messages into context.
macOS permissions required:
- Privacy & Security → Accessibility: allow Terminal (or whichever app runs the poller)
- Privacy & Security → Automation: allow Terminal to control System Events
The wake script uses osascript to send a Return keystroke to the Claude Code app window, triggering the prompt submit hook which reads pending messages.
Codex Desktop (macOS)
- Run
ide-agent-kit init --ide codex. - Configure
ide-agent-kit-codex.jsonwith your GroupMind API key. - Start the smart poller:
./tools/codex_room_autopost.sh tmux start. - Use
codex_gui_nudge.shif you need GUI-only notification injection.
Gemini / Antigravity App
- Run
ide-agent-kit init --ide gemini. - Enable the
memorymodule inide-agent-kit.json. - Start the poller:
./tools/geminimb_room_autopost.sh tmux start.
Cursor / VS Code
- Run
ide-agent-kit init --ide cursoror--ide vscode. - Configure the
ide-agent-kit.jsonwith your rooms and handles. - Start the watcher:
ide-agent-kit rooms watch.
Grok Build (and other non-Claude agents)
Grok Build has no Claude SessionStart hook. Copy
config/grok.example.json → config/grok.json
(fill key + handle; never commit the live file), then:
- Poller:
node bin/cli.mjs rooms watch --config config/grok.json(run under tmux, e.g. sessiongrok-poll). - Wake path — pick one:
- tmux nudge: set
poller.nudge_mode: "tmux"and run Grok inside thetmux.ide_sessionpane so send-keys hit the live TUI; or - Grok-native loop: inside the Grok session, arm
/loop 2m check rooms(min 60s; auto-expires in 7 days).
- tmux nudge: set
- Use a per-agent
notification_file/seen_file(do not share Claude's/tmp/iak-new-messages.txt).
Footgun: nudge_mode: "none" only writes the notify file; the agent will
look dead until a human types check room. Full write-up:
docs/grok-build.md.
Room Poller
The repo includes three poller implementations for watching GroupMind chat rooms. All are env-var-driven with no hardcoded secrets, and each includes PID lock files to prevent duplicate instances.
The generic poller (scripts/room-poll.sh + scripts/room-poll-check.py) works with any IDE agent. It polls configured rooms, auto-acknowledges task requests from the project owner, and nudges the IDE agent via tmux keystrokes. Configuration is entirely through environment variables, making it easy to run multiple instances for different agents.
Poll command (ide-agent-kit poll) nudge modes:
poller.nudge_mode = "tmux"(default): sendtmux send-keyspoller.nudge_mode = "command": executepoller.nudge_commandwithIAK_NUDGE_TEXTin env (useful for GUI agents)poller.nudge_mode = "none": queue-only polling, no nudge side effects
Codex Desktop setup (macOS)
For Codex Desktop GUI (non-tmux) use command-mode nudging:
{
"poller": {
"rooms": [
"thinkoff-development",
"feature-admin-planning",
"lattice-qcd"
],
"handle": "@CodexMB",
"interval_sec": 60,
"api_key": "groupmind_xxx",
"seen_file": "/tmp/codex-room-seen.txt",
"notification_file": "/tmp/codex-room-notifications.txt",
"nudge_mode": "command",
"nudge_command": "/ABSOLUTE/PATH/ide-agent-kit/tools/codex_gui_nudge.sh"
},
"dm_poller": {
"enabled": true,
"seen_file": "/tmp/codex-dm-seen.txt",
"limit": 100,
"human_only": false
},
"tmux": {
"ide_session": "codex",
"nudge_text": "check room and respond only if you have something relevant to say [codex]"
}
}
Run:
node bin/cli.mjs rooms watch --config /ABSOLUTE/PATH/ide-agent-kit-codex.json
When dm_poller.enabled is set, the same watcher also polls /api/v1/messages?limit=100, keeps a separate DM seen-state file, and nudges on new type: "dm" rows addressed to your configured handle. DM notifications are appended to the normal notification file so existing rooms check and GUI-nudge flows continue to work.
There is also a ready-to-copy example at:
config/codex.desktop.example.json
macOS permissions required for GUI keystroke injection:
- Privacy & Security → Accessibility: allow Terminal/iTerm (whichever runs the poller)
- Privacy & Security → Automation: allow Terminal/iTerm to control
System Events
The Gemini poller (tools/geminimb_room_autopost.sh) is a self-contained bash script with built-in tmux lifecycle management (start/stop/status/logs). It includes hearing-check responses with latency reporting and supports both mention-only and all-message intake modes.
The Codex smart poller (tools/antigravity_room_autopost.sh) is also self-contained with tmux lifecycle management. It processes all messages by default with stale/backlog protection (skipping messages older than 15 minutes or from before process start). Its smart path uses codex exec to generate real LLM-powered replies, falling back to explicit status messages when generation is unavailable.
The Codex room-duty wrapper (tools/codex_room_autopost.sh) reuses that same engine but sets Codex-friendly defaults for handle, session name, API-key lookup, and state files. Use it when you want Codex to keep polling assigned rooms without manual prompts.
Background Consolidation
The first dogfoodable consolidation pass is now available behind a separate CLI entrypoint:
ide-agent-kit background status --config ide-agent-kit.json
ide-agent-kit background run --config ide-agent-kit.json
This is intentionally separate from rooms watch.
- Foreground room polling remains reactive.
- Background consolidation is opt-in.
- One run executes the three human-readable phases sequentially:
lightREMdeep
- The background job never auto-posts into rooms in this first cut.
What each phase does in the first implementation:
light: reads the last 2 hours of queue events (hard cap 100), stages them into a short-term working set, and writes a sidecar.REM: synthesizes recurring themes, open threads, and follow-up candidates from the staged set, then writes a sidecar.deep: promotes only explicit durable facts and decisions into an append-only local memory ledger, then writes a sidecar.
Execution rules:
- Single background job only; no concurrency.
- Independent phase outcomes; one failure does not abort later phases.
- Skip rules:
light: skip if there are no new queue events since the previous runREM: skip iflightstaged zero itemsdeep: skip ifREMproduced no durable facts or decisions
Default timeout set:
light = 60sREM = 120sdeep = 120s
Sidecar output:
- directory:
~/.iak/consolidation/ - per-phase file pattern:
<run_id>-<phase>.json - durable deep-write ledger:
~/.iak/consolidation/deep-memory.jsonl
Example config:
{
"background": {
"enabled": false,
"interval_sec": 3600,
"recent_window_sec": 7200,
"max_events": 100,
"sidecar_dir": "~/.iak/consolidation",
"lock_file": "/tmp/iak-background.lock",
"timeouts": {
"light_sec": 60,
"rem_sec": 120,
"deep_sec": 120
}
}
}
UIK gating (live intent check at run start)
When the intent config block is set, background run fetches live user-intent state before executing and decides whether to run fully, run only the light phase, or skip entirely. This prevents background consolidation from competing with active work sessions or waking the user during emergency-only urgency.
Gating rules (implemented in fetchIntentGate):
urgency_mode == "emergency-only"→ skip entirely (no phases run)overall_state == "working"withreachability_mode == "desktop"ormobile_full_focus→ light only (skip REM and deep)overall_statein (meeting_people,outdoors,exercising) → light onlyoverall_statein (sleeping,resting,unknown,transitioning) → full dreaming (all phases allowed)
Pass --force on the command line to bypass the gate and run all phases regardless of live intent:
ide-agent-kit background run --config ide-agent-kit.json --force
This integrates with user-intent-kit's two-level state model (overall_state + reachability_mode) and the bundled uik-daemon that publishes live state to the intent API.
Env vars (generic poller)
| Variable | Default | Description |
|---|---|---|
IAK_API_KEY |
(required) | GroupMind API key |
IAK_ROOMS |
thinkoff-development,feature-admin-planning,lattice-qcd |
Rooms to watch |
IAK_SELF_HANDLES |
@claudemm,claudemm |
This agent's handles (skip own messages) |
IAK_TARGET_HANDLE |
@claudemm |
Handle used in ack messages |
IAK_OWNER_HANDLE |
petrus |
Only auto-ack from this user |
IAK_TMUX_SESSION |
claude |
tmux session to nudge |
IAK_POLL_INTERVAL |
10 |
Seconds between polls |
IAK_ACK_ENABLED |
1 |
Auto-ack task requests (1/0) |
IAK_NUDGE_TEXT |
check rooms |
Text sent to tmux on new messages |
IAK_LISTEN_MODE |
all |
Filter: all, humans, tagged, or owner |
IAK_BOT_HANDLES |
(empty) | Comma-separated bot handles for humans mode |
IAK_FETCH_LIMIT |
20 |
Messages per room per poll |
Env vars (Codex smart poller)
| Variable | Default | Description |
|---|---|---|
ANTIGRAVITY_API_KEY |
(required unless another candidate is set) | GroupMind API key |
API_KEY_ENV_CANDIDATES |
ANTIGRAVITY_API_KEY |
Comma-separated env vars checked for an API key |
AGENT_HANDLE |
@antigravity |
Handle to treat as self and detect mentions for |
POLLER_NAME |
antigravity |
Used in logs, tmux session defaults, and temp-state filenames |
ROOMS |
thinkoff-development,feature-admin-planning,lattice-qcd |
Comma-separated rooms to watch |
POLL_INTERVAL |
8 |
Seconds between polls |
FETCH_LIMIT |
30 |
Messages per room request |
MENTION_ONLY |
0 |
Intake mode: 0 all messages, 1 mention only |
SMART_MODE |
1 |
1 enables codex exec real-response generation |
STATE_PREFIX |
antigravity |
Prefix for lock/seen/acked temp files so multiple pollers do not collide |
CODEX_WORKDIR |
repo root | Working directory for codex exec |
CODEX_APPROVAL_POLICY |
on-request |
Codex approval policy for smart replies |
CODEX_SANDBOX_MODE |
workspace-write |
Codex sandbox mode for smart replies |
MAX_REPLY_AGE_SEC |
900 |
Skip stale messages older than this age |
SKIP_PRESTART_BACKLOG |
1 |
Skip messages older than process start |
User Intent Kit (embedded)
The User Intent Kit (UIK) gives agents awareness of the user's current state and availability. As of v0.9.0 UIK lives inside this repo at packages/user-intent-kit (still published separately as user-intent-kit on npm, including the Python/Swift/Kotlin ports). IAK consumes it directly through src/intent.mjs, which builds clients from the intent config block:
{
"intent": {
"baseUrl": "https://groupmind.one/api/v1",
"apiKey": "<X-API-Key for the intent API>",
"userId": "petrus",
"deviceId": "mac-mini",
"agentHandle": "@claudemm",
"suppress_nudges": true
}
}
Everything below activates automatically when this block is present (and stays inert when it is absent):
- Background gating —
background runchecks the live gate before each consolidation run (see UIK gating above). - Event enrichment — incoming room/DM events get the full intent snapshot under the
intentkey, with a 30s cache so message bursts cost one API call, not one per event. - Nudge suppression —
rooms watchstill writes every message to the notification file, but skips the tmux/command nudge whilederived.urgency_modeisemergency-only. Set"suppress_nudges": falseto opt out. All checks fail open: if the intent API is down, nudges behave as before. intentCLI —ide-agent-kit intent <get|profile|derived|state|patch|heartbeat|daemon>for inspecting and publishing state without a separate checkout.
When the optional enrichment sidecar is configured, it queries the Intent API on each incoming room message to fetch a real-time snapshot of the user's devices, active agents, and derived behavioral signals.
The intent payload includes:
devicesandagents: which devices and agent instances are currently online, along with their last-seen timestamps.stale_devices/stale_agents: devices and agents that have gone silent beyond their expected heartbeat window.derived.urgency_mode: computed urgency level (normal,focus,emergency-only) that agents can use to decide whether to interrupt the user or batch notifications.derived.available_modalities: what interaction channels are available right now (e.g.["read"],["read", "audio"]), so agents can choose text vs voice vs visual output.derived.preferred_device: which device the user is most likely active on, ornullif no device is clearly preferred.derived.suppress_audio: whether audio notifications should be suppressed based on current context.
Agents can use these signals to adapt their behavior. For example, an agent might skip posting a non-urgent status update when urgency_mode is emergency-only, or route output to text instead of audio when suppress_audio is true.
The intent data is fetched from the GroupMind API at GET /intent/{userId} and injected into queue events under the intent key with provider: "groupmind".
Publishing state (the other side)
Reading the intent API is only half the loop. To keep your own device and agent slots alive on the dashboard, run the built-in publisher as a long-running background process:
ide-agent-kit intent daemon --config ide-agent-kit.json # uses config.intent.*
npx uik-daemon # standalone equivalent, INTENT_* env vars
The IDE Agent Kit and User Intent Kit are designed to be deployed together: IAK consumes the intent state for gating, UIK publishes your own heartbeats. Without the daemon your slot goes stale and other agents will treat you as offline.
UIK v0.2.2 or later recommended for the uik-daemon bin. v0.2.0 introduced the daemon, v0.2.1 fixed a silent-exit in the example file, and v0.2.2 added agent-status republish on the heartbeat cadence so the agent slot does not expire while the daemon is running. Pre-0.2.2 daemons publish device state correctly but the agent slot still goes stale after its TTL.
Enrichment Configuration
To enable sidecar enrichment (Memory and Intent), add the following blocks to your ide-agent-kit.json:
{
"intent": {
"baseUrl": "https://groupmind.one/api/v1",
"apiKey": "groupmind_your_key",
"userId": "your_user_id"
},
"memory_api": {
"baseUrl": "http://127.0.0.1:37777/api",
"token": "your_claude_mem_token"
}
}
Note: the enrichment path exists and works when configured, but it is still optional. If the intent block is absent, queue events fall back to a placeholder intent payload and no live UIK data is fetched.
When enrichment is enabled, each queued room event can be expanded with:
intent: the full JSON payload returned byGET /intent/{userId}, plusprovider: "groupmind".memory_context.raw: an array of text snippets returned by Claude-Mem search.enrichment_errors: fetch or schema problems encountered while calling either upstream service.
The current Claude-Mem context retrieval path is message-driven. For each incoming room message, the sidecar uses the message body as the search query and calls:
GET {memory_api.baseUrl}/search/observations?query=<message body>&limit=3
Authorization: Bearer <memory_api.token>
The response is expected to be MCP-style JSON:
{
"content": [
{ "type": "text", "text": "..." }
]
}
All returned content[] entries with type: "text" are copied into memory_context.raw. This is how prior Claude-Mem observations are threaded back into queue events for downstream agents.
Example enriched event shape:
{
"kind": "groupmind.message.created",
"payload": {
"body": "can we ship the release notes today?",
"room": "thinkoff-development"
},
"intent": {
"user_id": "petrus",
"derived": {
"urgency_mode": "emergency-only"
},
"provider": "groupmind"
},
"memory_context": {
"raw": [
"Recent Claude-Mem observation text"
]
},
"enrichment_errors": []
}
Verify Enrichment
Verify the two upstream integrations independently before debugging the poller:
# 1. Intent lookup should return HTTP 200 JSON
curl -i \
-H "Authorization: Bearer groupmind_your_key" \
"https://groupmind.one/api/v1/intent/your_user_id"
# 2. Claude-Mem lookup should return HTTP 200 JSON with a content[] array
curl -i \
-H "Authorization: Bearer your_claude_mem_token" \
"http://127.0.0.1:37777/api/search/observations?query=release%20notes&limit=3"
Latest local verification from the Petrus machine, dogfooded against v0.6.1:
GET /intent/petrusreturned200on2026-04-08, withagents=[claudemb, claudemm]anddevices=[macbook, mac-mini]both active and zero stale slots after the UIK v0.2.2 deployment.GET /api/search/observations?query=thinkoff&limit=3returned200on2026-03-31(claude-mem path unchanged since v0.5.0).
Peer wake (same-machine agents revive sleeping colleagues)
Pollers and webhooks wake an agent whose receiver is running. But when an agent's IDE goes quiet — the session wedged, the tunnel died silently, the app lost focus — nothing is listening to wake it. Peer wake closes that gap: a watchdog running in the always-on layer of a machine can revive any agent whose IDE lives on the same machine, driving its GUI directly with no network dependency.
scripts/team-watchdog.mjs is that watchdog. Every interval it reads the room,
computes each roster agent's last-seen timestamp, and for anyone that has gone
quiet past STALE_MIN it fires exactly one wake path — rate-limited by a cooldown
and an MAX_NUDGES cap so it never spams.
The model — who can wake whom:
machine M (always-on watchdog)
├─ localWake → agent's IDE is ON machine M → GUI-wake it directly,
│ works even with no network
├─ gate → agent is on ANOTHER machine → POST <gate>/wake to its
│ daemon — only lands if that
│ machine is awake + receiver up
└─ (neither) → gateless agent in the room → room @mention its poller catches
A watchdog only directly revives agents whose IDE runs on its own machine
(localWake). Cross-machine wake (gate, the same primitive as the
wake_remote MCP tool) still needs the target machine
awake and its receiver alive — a laptop that is asleep cannot be woken over the
network without Wake-on-LAN. That is the one thing peer wake cannot do; run a
watchdog on each machine so every agent has a local reviver.
Roster format — config/watchdog-roster.json (gitignored; it holds hosts and
machine paths). Copy config/watchdog-roster.example.json
and edit it for the agents that live on this machine. Each entry names one wake path:
[
{ "handle": "@codex-on-this-mac", "localWake": "/abs/path/ide-agent-kit/tools/codex_gui_nudge.sh" },
{ "handle": "@agent-elsewhere", "gate": "http://192.168.0.9:8788" },
{ "handle": "@gateless-agent" }
]
The roster can also come from IAK_WATCHDOG_ROSTER (inline JSON) or
IAK_WATCHDOG_ROSTER_FILE. No roster → nothing to watch (the watchdog logs and
idles), so it is safe to run everywhere.
Safety: never types over a human. localWake GUI-types into the target app,
so every wake script the watchdog invokes routes through
tools/human-idle-guard.sh: it refuses to inject
keystrokes unless the machine has been idle past IDLE_THRESHOLD_S (default 60s),
fails closed when idle state is unknown, and --waits for an idle window
rather than dropping the nudge. The shipped wake scripts (scripts/claude-gui-wake.sh,
tools/codex_gui_nudge.sh) already call it (and also refuse to type into a locked
screen or a non-frontmost window). Any custom script you point localWake at
MUST be idle-guarded too — the watchdog runs it verbatim.
Opt-in install (per machine). The watchdog is off by default — it needs a roster. Load it with launchd from the parameterized example:
# 1. create your roster from the example
sed "s|REPLACE_WITH_IAK_ROOT|$HOME/ide-agent-kit|g" \
config/watchdog-roster.example.json > config/watchdog-roster.json
# then edit handles/paths for the agents whose IDEs run on THIS Mac
# 2. install + load the LaunchAgent (KeepAlive; restarts if it exits)
sed "s|REPLACE_WITH_IAK_ROOT|$HOME/ide-agent-kit|g" \
examples/team-watchdog-launchd.plist \
> ~/Library/LaunchAgents/com.thinkoff.iak-team-watchdog.plist
launchctl load ~/Library/LaunchAgents/com.thinkoff.iak-team-watchdog.plist
Or let the installer do it: IAK_INSTALL_WATCHDOG=1 makes scripts/install.sh
install the LaunchAgent (only if a roster exists). On a laptop that sleeps, prefer
the one-shot variant documented in the plist header (ONCE=1 + StartInterval),
since the in-process interval timer stalls across sleep. Tunables (all env, all
optional): STALE_MIN, COOLDOWN_MIN, INTERVAL_MIN, MAX_NUDGES, ROOM,
WATCHDOG_SELF, DRY_RUN (detect + log only, no wakes/posts).
Integrations
MCP server (src/mcp-server.mjs)
Exposes IAK's tmux-backed wake / list / run primitives as MCP tools so any MCP-aware client (Claude Desktop / Code, Cursor, custom agents) can drive the agent fleet directly without re-implementing tmux send-keys.
Tools exposed (stdio transport):
| Tool | Args | Notes |
|---|---|---|
wake_ide |
session, text? (default "check rooms") |
Sends nudge text and presses Enter in the named tmux session. |
list_sessions |
(none) | Returns every live tmux session on the host with attach state + window count. |
wake_all |
text? (default "check rooms") |
Sends the same nudge to every session IAK knows about (per-session pass/fail). Configure via mcp.sessions: ["...", ...]. Falls back to tmux.ide_session + tmux.default_session. |
read_session |
session, lines? (default 50) |
tmux capture-pane of the named session — see what the agent printed in response to a wake_ide. |
tmux_run |
cmd, session?, cwd?, timeoutSec? |
Runs an allowlisted command in a tmux session. Only registered when tmux.allow is non-empty or mcp.allow_unrestricted: true is set. Otherwise omitted entirely from the tool list (fail-closed). Same allowlist as the CLI's tmux run subcommand. |
MCP-specific config keys
Added to ide-agent-kit.json (or your own config path passed via --config):
{
"mcp": {
// Explicit list of sessions wake_all should target.
// If omitted, falls back to [tmux.ide_session, tmux.default_session].
"sessions": ["claudemb", "antigravity", "codex"],
// Set true to expose tmux_run with NO allowlist filter — any command runs.
// Default: false. Use only on a trusted host with a trusted MCP client.
"allow_unrestricted": false,
// User-confirmation flow (request_confirmation, list_intents,
// approve_intent, deny_intent tools). Tools are only registered if at
// least one channel below is configured.
"confirmations": {
"port": 8788, // HTTP port for /intent/:id/decision
"host": "127.0.0.1", // bind host (keep local unless tunneled)
"auth_token": "", // optional bearer for the HTTP endpoint
"callback_base": "http://...", // URL the watch / chat reach back on; defaults to http://host:port
"room": "thinkoff-development", // GroupMind room to post the prompt in (uses poller.api_key)
"codewatch_gate_url": "http://family@localhost:18791/intent",
"codewatch_gate_token": "" // bearer for CLAWWATCH_GATE
}
}
}
Confirmation flow (request_confirmation tool)
When mcp.confirmations is configured, four extra tools appear:
| Tool | Args | Notes |
|---|---|---|
request_confirmation |
prompt, session?, channels?, timeoutSec?, fromHandle? |
Posts an Approve / Deny prompt to GroupMind and/or Codewatch and BLOCKS until user decides or timeout. Returns {decision: "approve"|"deny"} or {status: "timeout", id}. fromHandle defaults to poller.handle for correct agent attribution. |
list_intents |
(none) | All intents — pending and recently decided. |
approve_intent |
id |
Manually settle a pending intent (e.g. MCP override). |
deny_intent |
id |
Non-MCP agents can use the same shared confirmation daemon through the CLI:
node bin/cli.mjs confirm request \
--config /path/to/ide-agent-kit.json \
--prompt "Approve destructive command?" \
--session codex \
--channels groupmind \
--from @CodexMB \
--wait
node bin/cli.mjs confirm list --config /path/to/ide-agent-kit.json
The request command POSTs to the live daemon's /intent endpoint, so CodeWatch
polling /intents shows the confirmation in the matching IDE channel (for
example session=codex... maps to @CodexMB) and phone/watch Approve/Deny
buttons settle the same intent. --wait blocks until a decision or timeout.
End-to-end:
- MCP-aware agent calls
request_confirmation({prompt: "Drop production DB?"}). - The IAK MCP server posts to GroupMind room (
/approve <id>//deny <id>quick replies) and to the CLAWWATCH_GATE (Android interactive notification with Approve / Deny buttons that vibrate the watch). - User taps Approve / Deny on the watch — Codewatch's notification action POSTs to
http://<callback_base>/intent/<id>/decisionwith{decision: "approve"}. - The MCP tool's blocking
request_confirmationcall resolves with the decision. - The agent proceeds (or doesn't) based on the decision.
Run standalone:
node bin/iak-mcp.mjs # default config
node bin/iak-mcp.mjs --config /path/to/config.json
npm run mcp # via package.json script
Wire into Claude Desktop / Code:
{
"mcpServers": {
"ide-agent-kit": {
"command": "node",
"args": ["/absolute/path/to/ide-agent-kit/bin/iak-mcp.mjs"]
}
}
}
After install: restart the MCP client. The four tools above appear in the tool picker and can be called directly.
GitHub Webhooks (src/webhook-server.mjs)
Receives GitHub webhook events, verifies HMAC signatures, normalizes them to a stable JSON schema, and appends to a local JSONL queue. Optionally nudges a tmux session when events arrive.
Supported events: pull_request.opened, pull_request.synchronize, pull_request.closed, push, issue_comment.created, issues.opened.
# Start the webhook server
node bin/cli.mjs serve --port 8787
# Configure GitHub to send webhooks to:
# http://your-host:8787/webhook
# Set a webhook secret in config for HMAC verification
# GroupMind webhooks are also accepted at:
# http://your-host:8787/groupmind
Config keys: listen.port, github.webhook_secret, github.event_kinds, queue.path.
OpenClaw Bot Fleet (src/openclaw-*.mjs)
Five modules for managing an OpenClaw multi-agent bot fleet via its CLI. Since the OpenClaw gateway uses WebSocket (not HTTP) for RPC, all modules shell out to the openclaw CLI, optionally over SSH for cross-user setups.
Why this matters: OpenClaw agents run as long-lived processes with their own models, memory, and tool access. IDE Agent Kit bridges the gap between these agents and your IDE workflow — letting room messages trigger agent actions, receipts flow between agents, and fleet operations happen from a single CLI.
The Gateway module (src/openclaw-gateway.mjs) handles starting, stopping, and restarting the OpenClaw gateway, including deep health checks. Use it to ensure your fleet is running before triggering automations.
# Check gateway health
node bin/cli.mjs gateway health
node bin/cli.mjs gateway health-deep
# List active agents
node bin/cli.mjs gateway agents
# Restart gateway (e.g. after config change)
node bin/cli.mjs gateway config-patch --json '{"key": "value"}'
The Sessions module (src/openclaw-sessions.mjs) sends messages to agents and lists active sessions. Use it for agent-to-agent communication — for example, asking one agent to review another's work.
# Send a message to a specific agent
node bin/cli.mjs gateway trigger --agent ether --message "review PR #6"
# Wake all agents
node bin/cli.mjs gateway wake --text "new deployment ready" --mode now
The Exec Approvals module (src/openclaw-exec.mjs) provides a governance layer for agent command execution. It manages an approval queue (pending, allow, deny) and reads OpenClaw's native per-agent, glob-based exec-approvals allowlist from ~/.openclaw/exec-approvals.json.
The Hooks module (src/openclaw-hooks.mjs) registers and manages event hooks for agents. Supported events include message:received, message:sent, command:new, command:reset, command:stop, agent:bootstrap, and gateway:startup. Hooks can be placed per-agent in workspace/hooks/ or shared in ~/.openclaw/hooks/.
The Cron module (src/openclaw-cron.mjs) handles scheduled task management, letting you list, add, and remove cron tasks for any agent.
# List cron jobs
node bin/cli.mjs cron list
# Add a scheduled poll
node bin/cli.mjs cron add --name "hourly-comments" --task "poll GitHub comments" --schedule "0 * * * *"
Example: full OpenClaw + IDE Agent Kit workflow
- Room message arrives in GroupMind → room automation matches a rule
- Rule triggers `gateway trigger --agent ether --message "deploy stag
No comments yet
Be the first to share your take.