T0mSIlver/starbridge/tree/main/mod
starbridge-mod
将所有者对 Starbridge 的回答提交到发起提问的 Claude Code 会话中。一个 Claude Code 模组:无法加载模组的构建会跳过它,而 starbridge 插件继续工作。
关于这个 mod
适用于 Claude Code 的 Starbridge 模组
当所有者回答一个决策时,此模组将回答提交到发起提问的 Claude Code
会话中,正如 starbridge wait 打印的行所示:
Answer to d_Xk3… (Merge #12 now?): Merge
它适用于终端、桌面应用的代码标签页和远程控制。
安装
从 Starbridge 市场安装它,位于 starbridge 插件旁边
(plugin/README.md):
claude plugin install starbridge-mod@starbridge --scope user
拒绝模组的 Claude Code 构建或组织会跳过此插件并
保留技能;此时回答会等待,直到某个会话运行 starbridge answers。
要在单个会话中试用检出:claude --plugin-dir mod。
工作原理
此模组不持有任何密钥。每个交互式会话选择两条路径之一,并在代理启动或停止时切换。
通过代理
当机器的代理(starbridge agent,PROTOCOL.md,“本地代理 API”)
在其 unix 套接字上应答时,每个会话通过 $.http.fetch 与其通信:
- 每个会话 id 调用一次
POST /v1/sessions/<id>/hello,携带会话的 工作目录。 - 背靠背调用
GET /v1/sessions/<id>/events?wait=25,在来自模组的调用 的 30 秒限制内。代理仅将会话所提问的决策的回答交给该会话。 - 模组提交每个事件的行,然后通过
POST /v1/sessions/<id>/ack确认;代理会再次交付未确认的事件。 它会跳过未知的事件类型。 - 会话结束时调用
POST /v1/sessions/<id>/bye。
套接字为 $STARBRIDGE_AGENT_SOCKET,否则对于默认配置目录为
$XDG_RUNTIME_DIR/starbridge/agent.sock,
否则为配置目录中的 agent.sock,与 CLI 的计算方式一致;在 Windows 上
(OS=Windows_NT),则是配置目录中的端口文件 agent.port,其中
列出了回环端口以及每次调用携带的令牌(PROTOCOL.md)。426(代理使用
另一个 API 修订版)或无法连接的调用会将会话发送到 CLI 路径。
通过 CLI
在没有 agent 时,或设置 STARBRIDGE_NO_AGENT=1 时,mod 会运行 CLI,并每 30 秒检查一次
agent。
- 每个交互式会话都会运行 mod,但每台机器只有一个会话进行轮询:
持有
~/.config/starbridge/mod-poller.json中租约的会话。它连续运行starbridge answers --session <id> --wait 25,这使来自 mod 的调用 保持在 30 秒限制以下。 - 其他会话监视
state.json。当它发生变化时,它们运行starbridge answers --session <id>,该命令只读取本地状态。 answers只返回该会话提出的、且没有任何wait打印过的决策答案。mod 提交每一行,然后用starbridge answers --session <id> --ack <ack>确认它;CLI 会把 未确认的行再次交出。- 不轮询的会话也会在
state.json未发生变化时每 30 秒运行一次answers, 以防有变化未被发现。 - 当一个会话结束时,它会放弃租约,另一个会话开始 轮询。
在两条路径上,/clear 或 /resume 之后,mod 都会使用新的会话
id;在 /clear 期间到达的行会以未确认状态等待旧
会话。出错后,mod 会等待 2 秒,之后每再出错一次等待时间翻倍,最多一分钟,
并在状态行中显示错误,直到一次
调用成功。
Pi
pi/starbridge.ts 是与 Pi 扩展相同的应答循环。它随仓库的 Pi 包以及 starbridge 技能一起发布。starbridge setup 会将它安装到
它所运行的 CLI 的发布标签,即 v 加 starbridge --version 所打印的内容:
pi install git:github.com/T0mSIlver/starbridge@v<version>
在交互式或 RPC Pi 会话中,它将每个答案作为用户消息提交,
这会在 Pi 空闲时启动一个回合,并在它忙碌时在当前回合之后运行。它还会将插件的规则(plugin/hooks/rule.md)添加到 Pi 的系统
提示中。pi -p 不会获得循环,因此那里的 starbridge ask 会告诉 agent
去 starbridge wait。
安装 pi-permission-system
后,pi/permissions.ts 会在其授权器链中注册一个 starbridge 链接,
所有者通过在其
config.json 中设置 "authorizerChain": ["starbridge"] 来启用它(starbridge config permissions on 会提议添加它)。当规则说 ask 时,该链接会像 Claude Code 的钩子一样运行 starbridge hook permission --agent pi,并返回设备的允许(仅限本次
调用:该链从不允许链接为会话允许)或拒绝及
其消息。与此同时,Pi 显示“在此处回答”,这会从设备那里取回提示,
并打开 pi-permission-system 自己的对话框。当 starbridge config permissions 关闭、
机器未配对或服务器不回答时,以及 570 秒后,该链接会立即
遵从该对话框。
opencode
opencode/starbridge.ts 是同一个应答循环,作为 opencode 外挂实现。
starbridge setup 会把它连同从 hooks/ 导入的文件和规则一起复制到
~/.config/opencode/starbridge/,并让
~/.config/opencode/plugins/starbridge.ts 指向它。
opencode 不给命令会话 id,所以外挂的 shell.env 钩子会为它们设置
STARBRIDGE_OPENCODE_SESSION 和该会话的标题,并启动该会话的循环。每个应答
通过 promptAsync 送入:空闲会话会开始一轮,忙碌会话会在下一步接收它。
opencode run 没有循环,所以 starbridge ask 会让那里的 agent 执行
starbridge wait。规则通过 experimental.chat.system.transform 写入
系统提示。
每个 permission.asked 事件都会运行 starbridge hook permission --agent opencode,当 starbridge config permissions 关闭时它会立即退出。
与此同时 opencode 的对话框保持显示:设备的应答通过 opencode 的回复路由发送,
键盘上的应答会停止 CLI,从而在设备上确认该提示。
设备能看到 Allow 批准的内容:bash 的命令;edit 的路径和 diff,这是其
edit、write 和 apply_patch 工具所询问的,还包括移动会把文件带到的位置以及
补丁会删除哪些文件;来自其 shell 的 external_directory 询问的目录和命令。
其他权限会显示其模式和元数据。MCP 工具调用只显示工具名称,因为 opencode 的
权限事件不携带其任何参数。超过 8,000 个字符时,CLI 会截断最长的字符串并说明
这一点。
每个 question.asked 事件(opencode 的 question 工具的一次调用)都会运行
starbridge hook question --agent opencode,它会将每个问题连同其标签作为选项
发布,并在所有答案齐备后打印这些答案。外挂通过
POST /question/{id}/reply 发送它们。键盘上的应答或 Esc
(question.replied 或 question.rejected)会停止 CLI,从而在设备上确认这些
问题。
开发
pnpm --filter @starbridge/mod test
e2e/run.ts 通过 tmux 中的真实 Claude Code 会话驱动每个用例:空闲、回合中、/clear 和 /resume、热重载、服务器中断,以及同时两个会话。它针对随机端口(--local)上的服务器应用运行,或针对带有测试设备(e2e/device.ts)和本机 CLI 配置的真实服务器运行。它需要 claude、tmux 和 starbridge 在 PATH 上,并为每个用例打印一行计时。使用 --agent 时,它会在整个运行过程中运行 starbridge agent,最后一行检查每个会话是否通过它应答。
测试针对 CLI、真实代理和真实服务器运行两条路径,@starbridge/server/test-support 会在随机端口上启动它们。hooks/agent.ts 是代理路径,hooks/poller.ts 是 CLI 路径,hooks/switch.ts 选择其中一个,hooks/register.ts 将它们连接到引擎。CI 对所有文件进行类型检查,但 register.ts 除外。要对 register.ts 进行类型检查,请加载一次 mod,以便引擎将其类型写入 .claude-plugin/types/,然后运行 tsc -p tsconfig.engine.json。
安装
请先查看作者 README 确认 marketplace 和插件名称;命令可能随仓库结构改变。
claude plugin marketplace add T0mSIlver/starbridge claude plugin install starbridge-mod
原文 / README
Starbridge mod for Claude Code
When the owner answers a decision, this mod submits the answer into the Claude
Code session that asked, as the line starbridge wait prints:
Answer to d_Xk3… (Merge #12 now?): Merge
It works in the terminal, the desktop app's Code tab and Remote Control.
Install
Install it from the Starbridge marketplace, beside the starbridge plugin
(plugin/README.md):
claude plugin install starbridge-mod@starbridge --scope user
A Claude Code build or organization that refuses mods skips this plugin and
keeps the skill; answers then wait until a session runs starbridge answers.
To try a checkout in one session: claude --plugin-dir mod.
How it works
The mod holds no keys. Each interactive session picks one of two paths, and switches when the agent starts or stops.
Through the agent
When the machine's agent (starbridge agent, PROTOCOL.md, "Local agent API")
answers on its unix socket, each session talks to it with $.http.fetch:
POST /v1/sessions/<id>/helloonce per session id, with the session's working directory.GET /v1/sessions/<id>/events?wait=25back to back, under the 30 s limit on calls from a mod. The agent hands the session only the answers to the decisions it asked.- The mod submits each event's line, then confirms it with
POST /v1/sessions/<id>/ack; the agent hands an unconfirmed event over again. It skips event types it does not know. POST /v1/sessions/<id>/byewhen the session ends.
The socket is $STARBRIDGE_AGENT_SOCKET, else
$XDG_RUNTIME_DIR/starbridge/agent.sock for the default config directory,
else agent.sock in the config directory, as the CLI works it out; on Windows
(OS=Windows_NT), the port file agent.port in the config directory, which
names the loopback port and the token every call carries (PROTOCOL.md). A 426 (the
agent speaks another API revision) or a call that cannot connect sends the
session to the CLI path.
Through the CLI
With no agent, or with STARBRIDGE_NO_AGENT=1, the mod runs the CLI and checks
for the agent every 30 s.
- Every interactive session runs the mod, but only one per machine polls: the
session holding the lease in
~/.config/starbridge/mod-poller.json. It runsstarbridge answers --session <id> --wait 25back to back, which stays under the 30 s limit on calls from a mod. - The other sessions watch
state.json. When it changes, they runstarbridge answers --session <id>, which reads local state only. answersreturns only answers to decisions that session asked and that nowaithas printed. The mod submits each line, then confirms it withstarbridge answers --session <id> --ack <ack>; the CLI hands an unconfirmed line over again.- A session that does not poll also runs
answersevery 30 s whenstate.jsonhas not changed, in case a change went unseen. - When a session ends, it gives up the lease and another session starts polling.
On both paths, after a /clear or a /resume the mod uses the new session
id; a line that arrives during a /clear waits, unconfirmed, for the old
session. After an error the mod waits 2 s, then twice as long after each
further error, up to a minute, and shows the error in the status line until a
call succeeds.
Pi
pi/starbridge.ts is the same answer loop as a Pi extension. It ships in the
repository's Pi package with the starbridge skill. starbridge setup installs it at the
release tag of the CLI it runs, v and what starbridge --version prints:
pi install git:github.com/T0mSIlver/starbridge@v<version>
In an interactive or RPC Pi session it submits each answer as a user message,
which starts a turn when Pi is idle and runs after the current one when it is
busy. It also adds the plugin's rule (plugin/hooks/rule.md) to Pi's system
prompt. pi -p gets no loop, so starbridge ask tells the agent there to
starbridge wait.
With pi-permission-system
installed, pi/permissions.ts registers a starbridge link in its authorizer
chain, which the owner turns on with "authorizerChain": ["starbridge"] in its
config.json (starbridge config permissions on offers to add it). When a rule says ask, the link runs starbridge hook permission --agent pi, as Claude Code's hook does, and returns the devices' allow (this
call only: the chain never lets a link allow for the session) or deny with
their message. Meanwhile Pi shows "Answer here", which takes the prompt back
from the devices and opens pi-permission-system's own dialog. The link defers
to that dialog at once while starbridge config permissions is off, the
machine is not paired or the server does not answer, and after 570 s.
opencode
opencode/starbridge.ts is the same answer loop as an opencode plugin.
starbridge setup copies it, with the files it imports from hooks/ and the
rule, into ~/.config/opencode/starbridge/, and points
~/.config/opencode/plugins/starbridge.ts at it.
opencode gives commands no session id, so the plugin's shell.env hook sets
STARBRIDGE_OPENCODE_SESSION and the session's title for them, and starts that
session's loop. Each answer goes in with promptAsync: an idle session starts
a turn, a busy one takes it at its next step. opencode run gets no loop, so
starbridge ask tells the agent there to starbridge wait. The rule goes into
the system prompt through experimental.chat.system.transform.
Each permission.asked event runs starbridge hook permission --agent opencode, which exits at once while starbridge config permissions is off.
opencode's dialog stays up meanwhile: the devices' answer is sent through
opencode's reply route, and an answer at the keyboard stops the CLI, which
settles the prompt on the devices.
The devices see what an Allow approves: the command for bash; the paths and
the diff for edit, which its edit, write and apply_patch tools ask, with
where a move takes a file and which files a patch deletes; the directories
and the command for an external_directory ask from its shell. Other
permissions show their patterns and metadata. An MCP tool call shows only the
tool's name, since opencode's permission event carries none of its arguments.
Past 8,000 characters the CLI cuts the longest string and says so.
Each question.asked event (a call of opencode's question tool) runs
starbridge hook question --agent opencode, which posts each question with its
labels as options and prints the answers once all are in. The plugin sends them
through POST /question/{id}/reply. An answer or Esc at the keyboard
(question.replied or question.rejected) stops the CLI, which settles the
questions on the devices.
Develop
pnpm --filter @starbridge/mod test
e2e/run.ts drives real Claude Code sessions in tmux through each case:
idle, mid-turn, /clear and /resume, a hot reload, a server outage, and two
sessions at once. It runs against the server app on a
random port (--local), or against a real server with a test device
(e2e/device.ts) and this machine's CLI config. It needs claude, tmux and
starbridge on PATH, and prints one row of timings per case. With
--agent it runs starbridge agent for the whole run, and a last row checks
that every session answered through it.
The tests run both paths against the CLI, a real agent and the real server,
which @starbridge/server/test-support starts on a random port.
hooks/agent.ts is the agent path, hooks/poller.ts the CLI path,
hooks/switch.ts picks one, and hooks/register.ts connects them to the
engine. CI type-checks all but register.ts. To type-check register.ts,
load the mod once so the engine writes its types to .claude-plugin/types/,
then run tsc -p tsconfig.engine.json.
