试一试
在终端里,Gnista 运行着的时候:
G=/Applications/Gnista.app/Contents/Helpers/gnista
echo '{"session":"demo","event":"needs-you","message":"Ralph wants to deploy","cwd":"'"$PWD"'"}' | "$G" hook custom ralph一个来自“Ralph”的会话出现了,正在等你。点它,就回到这个终端。然后:
echo '{"session":"demo","event":"done","message":"Deployed"}' | "$G" hook custom ralph
echo '{"session":"demo","event":"end"}' | "$G" hook custom ralph辅助程序总是以 0 退出,从不等待,所以拖不住你的工具。你发的内容有问题时,它会在 stderr 上说明,什么也不发送。
命令
gnista hook custom <name> [event]<name> 是你的 Agent 的名字:最多 32 个小写字母、数字、- 和 _,以字母或数字开头。内置 Agent 的名字(claude、codex 等)已被占用。事件也可以写在这里而不写在 JSON 里,适合每个事件运行不同命令的工具。
JSON
每次调用一个 JSON 对象,从 stdin 传入:
| 键 | 含义 |
|---|---|
session | 必填。在一个会话里保持不变的任意 ID(文本或数字)。 |
event | 必填,写在这里或命令行上。下面的事件之一。 |
cwd | 项目文件夹。会显示在行里;不填则用辅助程序自己所在的文件夹。 |
message | Agent 正在做的、在问的,或最后说的话。 |
tool | 正在用的工具,用于 tool 和 tool-done。 |
title | 会话标题,显示在文件夹的位置。 |
model | 模型,例如 gpt-5。Gnista 认识的模型会在图标上显示一个小角标;其他模型可以自己描述。 |
transcript | Agent 的记录文件,用于 details(版本 2)。Claude 风格的钩子会把它作为 transcript_path 发来。 |
pid | Agent 的进程。进程退出,会话就结束。不填则用运行辅助程序的那个进程。 |
事件
匹配时不区分大小写,也不管分隔符:needs-you、NeedsYou 和 needs_you 是同一个。
| 事件 | 在 Gnista 里 |
|---|---|
start | 会话出现,工作中。 |
prompt | 工作中:有人问了它问题。 |
tool | 工作中,显示“正在运行命令”“正在编辑 api.ts”或 message。 |
tool-done | 仍是工作中,除非这个工具在 doneAfterTools 里。 |
needs-you | 需要你:刘海会告诉你,并显示 message。 |
done | 这一轮结束了。 |
end | 会话结束了。 |
工作中的会话安静下来,几分钟后就变为空闲(设置 ▸ 通用 ▸ 视为空闲)。
Agent 文件
可选。放在 ~/Library/Application Support/Gnista/Agents/<name>.json。每个键都是可选的:
{
"name": "Ralph",
"icon": "ralph.svg",
"fields": {
"session": "$.conversation_id",
"event": "$.hook_event_name",
"cwd": "$.workspace_roots[0]",
"tool": "$.tool.name"
},
"events": {
"beforeSubmitPrompt": "prompt",
"afterToolUse": "tool-done",
"stop": "done",
"heartbeat": "ignore"
},
"host": "com.example.Ralph",
"jump": { "url": "ralph://open?session={session}" },
"doneAfterTools": ["attempt_completion"]
}| 键 | 作用 |
|---|---|
name | 行和设置里显示的 Agent 名字,最多 40 个字符。不填则用命令里的名字。 |
icon | 同一文件夹里的 SVG 或 PNG 文件,最大 1 MB。不填则用 Gnista 自己的火花。 |
fields | 给发送自己格式 JSON 的工具用:上面 JSON 表格里每个值在哪里找。路径以 $ 开头,.key 取对象的键,[0] 取列表的项。没映射的字段照常从同名的键读取。 |
events | 你的工具的事件名,各自对应 Gnista 的哪个事件。"ignore" 会悄悄丢掉一个事件。名字按同样宽松的方式匹配;Gnista 自己的事件名照样有效。 |
host | Agent 所在应用的 bundle ID:Gnista 从进程看不出来时,点击会带你去那里。 |
jump | 一条跳转规则:见下文。 |
doneAfterTools | 给从不说一轮已结束的 Agent 用:用过其中任何一个工具之后(一个 tool-done 事件),这一轮就算结束。 |
超过 1 MB 的文件,或者有任何错误的文件(未知的键、不是路径的路径、Gnista 没有的事件),会被整个忽略。设置 ▸ 智能体 ▸ 自定义智能体会列出它并说明原因,辅助程序也会在 stderr 上说。在此期间,上面的简单格式照样能用。
让 Gnista 来接入
写上 "version": 2,文件还能告诉 Gnista 怎样接入你的 Agent,就像接入内置 Agent 那样:要在 Agent 自己的配置里放哪些钩子、怎样读懂它们发来的内容、它的权限请求能不能在刘海里回答,以及它的记录文件在哪里,这样它的行里就会显示你的提问、它的回复和花费。版本 1 的键含义不变。
{
"version": 2,
"name": "Ralph",
"colour": "#2EB872",
"adapter": "claude-style",
"hooks": {
"format": "claude-table",
"file": ".ralph/settings.json",
"events": ["UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop", "SessionEnd"],
"timeout": 5
},
"approvals": "plain",
"details": "claude"
}| 键 | 作用 |
|---|---|
version | 2。 |
speaker | 句子里怎样称呼它:“Ralph 想运行……”。最多 40 个字符。不填则用 name。 |
colour | 它的颜色,写成 "#RRGGBB"。 |
adapter | "fields"(默认):像版本 1 那样,用 fields 和 events 在你的工具的 JSON 里找到 Gnista 要的值。"claude-style":你的工具发送 Claude Code 的钩子负载;这时 events 把它自己的事件名对应到 Claude 的事件名。 |
toolNames | 用于 claude-style:你的工具对 Claude 各工具的叫法,例如 {"run_shell": "Bash"},这样行和请求读起来都一样。 |
hooks | 你接入它时 Gnista 会安装什么:见下表。 |
approvals | 它的权限请求在刘海里等你:"plain"(允许或拒绝)、"answers"(并回答它的问题)或 "claude"(带回答、“始终”规则和模式)。只适用于 claude-style,并且要有针对 PermissionRequest 的 claude-table 钩子。 |
details | 它的记录文件的格式(前提是它写了 Gnista 能读的记录):"claude"、"codex"、"gemini" 或 "antigravity"。这样行里就会显示它的提问、它的回复,以及花费(antigravity 除外)。 |
设置 ▸ 智能体会把你的 Agent 和其他 Agent 列在一起:在那里接入它,就会把 Gnista 的钩子加进你指定的文件;断开则会把它们移除。Gnista 只改动自己的条目,并且会先备份该文件。钩子调用的是 gnista hook custom <name>,并带上事件。
| 在“hooks”里 | 作用 |
|---|---|
format | "claude-table":Claude Code 的 {"hooks": {"<event>": [{"hooks": [{"type": "command", …}]}]}},事件在负载里。"flat":{"hooks": {"<event>": [{"command": …}]}},事件在命令行上。"toml":带 event 和 command 的 [[hooks]] 块,文件以 .toml 结尾。 |
file | Agent 的配置文件,相对于你的主文件夹,并且位于 Agent 自己的文件夹里:.ralph/settings.json。这个文件夹存在,就说明该 Agent 装在这台 Mac 上。 |
events | Gnista 要挂钩的 Agent 事件名:字母、数字和 _。 |
timeout | 秒数,1 到 600,给钩子需要超时设置的 Agent 用。 |
foundBy | 只有 Agent 自己才会写的文件,相对于你的主文件夹,用于仅凭它的文件夹还不能确定它已安装的情况(其他应用也会创建该文件夹)。 |
跳转规则
点一个会话,Gnista 会带你去它的终端标签页或窗口。对于它没法进入的应用,跳转规则给它一个 URL 去打开:先试过精确的标签页和窗口,再打开这个 URL,最后才只是把应用调到前台。
{ "url": "myeditor://focus?folder={cwd}&pane={tmux_pane}" }在 Agent 文件里写成 "jump";或者要对某个应用里的所有会话生效,放在 ~/Library/Application Support/Gnista/JumpRules/<bundle id>.json(例如 com.example.Editor.json)。Agent 自己的规则优先。
| 占位符 | 填入 |
|---|---|
{session} | 会话 ID。 |
{cwd} | 项目文件夹。 |
{tty} | 终端,例如 /dev/ttys004。 |
{pid} | Agent 的进程 ID。 |
{tmux_pane} | tmux 窗格,例如 %3。 |
每个值都整体做百分号编码(/ 变成 %2F),所以改变不了 URL 的结构。占位符没有全部取到值的规则不会被使用。会打开文件或运行代码的 URL(file:、javascript:、data: 之类)一律拒绝。
在设置里
设置 ▸ 智能体 ▸ 自定义智能体列出每个有文件或汇报过的 Agent,以及 Gnista 最后一次收到它消息的时间。