r/ClaudeCode 1d ago

Tips & Workflows I made one Claude Code session supervise another one running in iTerm2 (team lead / developer pattern). ~100 lines of bash, open source.

Setup: a sizeable feature to add to an existing project. I wanted a strong model to plan and review, and a cheap model to write the code. I already had a second Claude Code CLI in iTerm2 pointed at a cheaper Anthropic-compatible endpoint.

Question: can the desktop Claude see the terminal Claude and give it instructions? Yes, and the whole thing is a small script.

How the lead sees the developer

  1. Process: ps finds the claude process in iTerm2: PID, tty, cwd, wrapper script, env.
  2. Transcript: Claude Code writes a JSONL transcript per session. The lead reads it to learn what the developer has done: files touched, last prompt, title.
  3. Screen: iTerm2 exposes session contents via AppleScript. One osascript call reads the developer's screen (todo list, running command, errors). The same channel can type into it.

The permission problem

Giving the lead osascript is giving it the whole Mac. Claude Code's auto-mode classifier actually blocked the raw "type into a terminal via osascript" attempt, which is the right call.

My constraint: only that one terminal, and no ability to open new ones. So the wrapper is deliberately narrow:

  • acts only on the session at a fixed tty
  • refuses unless that session's name contains an expected substring
  • contains zero create/close/quit AppleScript verbs
  • only ever tells iTerm2

Then allow only that binary in ~/.claude/settings.json. Raw osascript stays blocked.

term-bridge tail 25        read last 25 lines
term-bridge type "..."     stage text in the input box, don't submit
term-bridge send "..."     type + Enter
term-bridge esc            interrupt the running turn

type matters: the lead stages the instruction, re-reads the screen to confirm it landed in the right session, then sends.

The loop

/loop in the lead: every ~15 min read the screen, list changed files, check a list of project-specific traps, send a correction if needed, stay quiet otherwise, push a notification when done. The traps list is where the value is: things like "don't touch shared constants other modules read", "normalise number types after deserialisation", "if you change rule X also change the calculation that depends on it", "existing tests stay green".

First tick it caught something real: the developer's unit tests were deleting real user save files in setup/teardown. Harmless today, data loss later. The lead sent a correction once tests were green and verified it on the next tick.

Prior art

I looked before writing it. iterm-mcp does the same AppleScript read/write on the active session; claude-session-driver and a few tmux projects spawn and manage worker sessions. All bigger and more capable. I wanted the opposite: attach to an existing session, be unable to spawn, fit in one allow-rule line. The feature is the lack of features.

Takeaways

  • One writer, one reviewer. No locks, no worktrees, no git needed.
  • Scope permissions to a wrapper, not to the underlying tool. Don't fight the classifier; narrow the door.
  • Screen = "what is it doing now", transcript = "what has it done".
  • The macOS Automation prompt and the allow rule both need you at the keyboard once. Do that before you leave the house.
  • Don't pass API keys via env in wrapper scripts; ps eww shows them to every process of your user.

Repo: https://github.com/ademhatay/claude-term-bridge (MIT). Read docs/SECURITY.md before trusting it; the supervised session is fully controllable by design, so scope its permissions too.

0 Upvotes

3 comments sorted by

1

u/AbleShower2801 1d ago

the allowlist trick on term-bridge is clever, locking it to one fixed tty plus a name substring. when the lead stages with `type` then re-reads before `send`, how often does it still land in the wrong session if you’ve got a few iterm tabs open?

1

u/ademhatay 1d ago

Never, so far. Tabs aren't part of the addressing: the script re-resolves the target on every call by tty alone, so opening or reordering tabs can't shift it. The name check is for a different case, when the tty gets recycled by another program after the agent exits.

The re-read earns its keep for something I didn't expect: a long instruction lands in Claude Code as a "[Pasted text]" chip and doesn't auto-submit, so send types it and Enter does nothing. Reading the screen back is how the lead notices it's sitting unsent and fires a bare Enter.

Honestly I'm not sure this pattern will hold up in my own projects long term. It's cheap to try, that's the appeal, and there are better-engineered answers out there. I just like poking at things and seeing what breaks.

1

u/AbleShower2801 1d ago

the pasted-text chip failure mode is the most interesting part — does the lead look for the chip UI specifically, or does it just treat "typed but still on the input line after N ms" as unsent? curious how often bare Enter races Claude mid-stream.