eighteyes/universal-backchannel/tree/main/mod/ubc
ubc
어떤 셸에서든 지정한 Claude Code 세션으로 메시지를 보내고, 그 세션이 메시지로 턴을 시작하게 하는 Claude Code 플러그인입니다.
이 mod 소개
universal-backchannel
"Slip a note under the door"
ubc는 Claude Code로 들어가는 백채널입니다. 어떤 셸이든 메시지를 남기면 해당 세션이 그 메시지로 턴을 시작합니다.
ubc "the build is green, go ahead and tag it"
셸에서 ubc를 실행하면 그 셸이 속한 세션을 찾습니다. 같은 herdr 탭, 같은 프로젝트 폴더, 또는 직접 지정한 이름을 사용합니다. 추측을 건너뛰려면 --agent <uuid>를 전달하세요. 둘 이상의 세션이 일치하면 ubc가 목록을 출력하고 3으로 종료하므로 임의로 고르지 않습니다. /ubc name api로 세션 이름을 정할 수 있습니다. 메시지는 읽기 전에 받은 편지함에서 옮겨 한 번만 전달합니다. 턴 중간에 도착한 메시지는 대기열에 들어갔다가 턴이 끝나면 하나의 프롬프트로 도착합니다.
설치
git clone https://github.com/eighteyes/universal-backchannel
cd universal-backchannel
ln -s "$PWD/bin/ubc" ~/.local/bin/ubc # the CLI
claude --plugin-dir "$PWD/mod/ubc" # a session with the mod loaded
모든 세션에서 모드를 로드하려면 ~/.claude/settings.json의 env 블록에 mod/ubc 경로를 CLAUDE_CODE_PLUGIN_DIRS로 추가하세요.
필요한 것은 bash, jq(--agent 없이 세션을 찾을 때만 사용), hooks-module 모드를 로드하는 Claude Code 빌드입니다.
모드는 아무것도 그리지 않습니다. 받은 편지함을 폴링하고 턴을 시작합니다. /ubc는 세션 id와 바로 붙여 넣을 수 있는 전송 줄을 출력합니다. 상태는 ~/.ubc(inbox, read, sessions)에 저장되며 UBC_DIR로 트리를 옮길 수 있습니다.
설치
먼저 작성자의 README에서 marketplace와 플러그인 이름을 확인하세요. 저장소 구조에 따라 명령어가 달라질 수 있습니다.
claude plugin marketplace add eighteyes/universal-backchannel claude plugin install ubc
원문 / README
universal-backchannel
"Slip a note under the door"
Your Claude Code sessions are islands. A build script, a cron job, another agent or you in a spare shell can't reach a session without typing into its window. To reach one from outside we need an address and a mailbox: anything writes, the session reads.
ubc is a backchannel into Claude Code. Any shell drops a message, and the session it's meant for starts a turn with it.
ubc "the build is green, go ahead and tag it"
The ubc plugin sent a message:
ubc message from my-repo:
the build is green, go ahead and tag it
Features
no uuid needed
Run ubc from a shell and it finds the session that shell belongs to: same herdr tab, same project folder, or a name you gave it. Pass --agent <uuid> to skip the guessing.
never guesses
When more than one session matches, ubc lists them and exits 3. Pick one with --agent or --to.
names
/ubc name api in a session, then ubc --to api "deploy done" from anywhere.
delivered once
The mod claims each message by moving it out of the inbox before it reads it. Two polls or two sessions can't deliver the same message twice.
queues behind a busy turn
A message that lands mid-turn waits for that turn to finish. Several waiting messages arrive as one prompt.
pipes
make test 2>&1 | tail -20 | ubc sends the output. --from ci signs it.
no pane
The mod draws nothing. It polls an inbox and starts turns. /ubc prints the session's id and a ready-to-paste send line.
It's a folder of text files and a 1.5 second poll, but it's replaced a lot of copy-pasting between my windows.
Installation
git clone https://github.com/eighteyes/universal-backchannel
cd universal-backchannel
ln -s "$PWD/bin/ubc" ~/.local/bin/ubc # the CLI
claude --plugin-dir "$PWD/mod/ubc" # a session with the mod loaded
To load the mod in every session, add the mod/ubc path to CLAUDE_CODE_PLUGIN_DIRS in the env block of ~/.claude/settings.json.
Needs: bash, jq (only for finding a session without --agent), and a Claude Code build that loads hooks-module mods.
Incomplete Implementations
Codex, Desktop, others: nothing yet. The mod needs Claude Code's $.prompt.submit to start a turn.
Hot reload: a mod folder under ~/.claude/dev-mods/ that's a symlink loads once and then ignores edits. Copy it in, or use --plugin-dir.
Implementation
CLI
ubc "hi there from ubc" # to the session this shell belongs to
ubc --to api "deploy done" # to a session named with /ubc name api
ubc --agent <uuid> "hi" # by id
echo "build failed" | ubc # message from stdin
ubc --from ci "deploy done" # sign it (default: this folder's name)
ubc --who # which session a send would reach
ubc --list # live sessions
Finding the session
With no --agent, the first rule that matches wins:
rule matches
--to <name> a live session named with /ubc name <name>
$CLAUDE_CODE_SESSION_ID set in shells Claude itself starts (Bash tool, hooks)
$HERDR_TAB_ID a live session in the same herdr tab
$HERDR_WORKSPACE_ID a live session in the same herdr workspace
$PWD a live session whose cwd is this folder, above it or below it
Several matches narrow to those whose cwd nests with $PWD, then to an exact cwd. A session is live for 150s after its last heartbeat (UBC_LIVE_SECS).
Mod
event does
session.start registers /ubc; polls the inbox every 1.5s; heartbeats every 60s
command.run /ubc prints id, send line and waiting count; /ubc name <x> names it, /ubc name - drops it
/ubc can't deliver: Claude Code doesn't allow a prompt submit from a command hook, so delivery always comes from the poll.
Layout
~/.ubc/inbox/<uuid>/*.msg waiting; the CLI writes a dotfile and renames it
~/.ubc/read/<uuid>/*.msg delivered; never pruned
~/.ubc/sessions/<uuid>.json heartbeat: cwd, herdr ids, name, time
A message is from: <name>, a blank line, then the body. UBC_DIR moves the tree; UBC_FROM sets the default sender.
Trust
Nothing is authenticated. Anything that can write to ~/.ubc can prompt your sessions, the same as anything that can type into your terminal.
Tests
claude plugin test mod/ubc
Contributions
Are welcome. I wanted agents and scripts to tap each other on the shoulder without a server, a socket or a pane, and a mailbox on disk turned out to be enough.