커스텀 에이전트,
JSON 한 줄이면.

어떤 도구든 Gnista에 나타날 수 있어요. 작업 중, 완료, 응답 대기 중으로요. Gnista 새 버전을 기다릴 필요도, Gnista 말고 따로 설치할 것도 없어요. 무슨 일이 생길 때마다 도구가 JSON 한 줄을 Gnista의 헬퍼에 넘기면 돼요.

업데이트

해 보기

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>은 에이전트의 이름이에요. 영문 소문자, 숫자, -, _로 32자까지, 첫 글자는 영문자나 숫자예요. 기본 제공 에이전트의 이름(claude, codex 등)은 쓸 수 없어요. 이벤트는 JSON 대신 여기서 넘겨도 돼요. 이벤트마다 다른 명령을 실행하는 도구를 위한 거예요.

JSON

호출 한 번에 JSON 객체 하나를, stdin으로:

키뜻
session필수. 한 세션 동안 바뀌지 않는 아무 ID(텍스트나 숫자).
event필수. 여기나 명령줄에서. 아래 이벤트 중 하나.
cwd프로젝트 폴더. 줄에 표시돼요. 없으면 헬퍼 자신의 폴더.
message에이전트가 하고 있는 일, 묻는 것, 마지막으로 한 말.
tool사용 중인 도구. tool과 tool-done에서.
title세션 제목. 폴더 대신 표시돼요.
model모델. 예: gpt-5. Gnista가 아는 모델은 아이콘에 작은 배지로 보이고, 나머지는 직접 설명해 주면 돼요.
transcript에이전트의 트랜스크립트 파일, details용(버전 2). Claude 방식 훅은 이것을 transcript_path로 보내요.
pid에이전트의 프로세스. 프로세스가 끝나면 세션도 끝나요. 없으면 헬퍼를 실행한 프로세스.

이벤트

대소문자와 구분 기호는 따지지 않아요. needs-you, NeedsYou, needs_you는 모두 같아요.

이벤트Gnista에서
start세션이 나타나고, 작업 중이에요.
prompt작업 중: 누군가 무언가를 물었어요.
tool작업 중. ‘명령 실행 중’, ‘편집 중: api.ts’ 또는 message를 보여 줘요.
tool-done계속 작업 중. 단, 도구가 doneAfterTools에 있으면 완료예요.
needs-you응답 대기 중: 노치가 message와 함께 알려 줘요.
done턴이 끝났어요.
end세션이 끝났어요.

작업 중이던 세션이 조용해지면 몇 분 뒤 유휴 상태가 돼요(설정 ▸ 일반 ▸ 유휴 전환).

에이전트 파일

선택 사항이에요. ~/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줄과 설정에 표시되는 에이전트 이름, 40자까지. 없으면 명령의 이름.
icon같은 폴더의 SVG나 PNG 파일, 1MB까지. 없으면 Gnista의 불꽃.
fields자기만의 JSON을 보내는 도구용: 위 JSON 표의 각 값을 어디서 찾을지. 경로는 $로 시작하고, 객체의 키는 .key, 목록의 항목은 [0]이에요. 지정하지 않은 필드는 원래 키에서 읽어요.
events도구의 이벤트 이름과, 각각이 Gnista의 어떤 이벤트인지. "ignore"는 조용히 버려요. 이름은 같은 너그러운 방식으로 맞춰 보고, Gnista의 이름도 그대로 통해요.
host에이전트가 돌아가는 앱의 번들 ID. Gnista가 프로세스로 알 수 없을 때 클릭하면 여기로 가요.
jump이동 규칙. 아래를 보세요.
doneAfterTools턴이 끝났다고 말하지 않는 에이전트용: 이 도구 중 하나를 쓴 뒤(tool-done 이벤트) 턴이 끝난 걸로 봐요.

1MB가 넘거나 어디든 잘못된 곳이 있는 파일(모르는 키, 경로가 아닌 경로, Gnista에 없는 이벤트)은 통째로 무시돼요. 설정 ▸ 에이전트 ▸ 커스텀 에이전트에 이유와 함께 표시되고, 헬퍼도 stderr에 알려 줘요. 그동안에도 위의 기본 형식은 그대로 작동해요.

Gnista가 연결하게 하기

"version": 2로 하면 파일이 에이전트를 어떻게 연결할지도 Gnista에 알려 줄 수 있어요. 기본 제공 에이전트를 연결할 때처럼요. 에이전트 자신의 설정에 어떤 훅을 넣을지, 훅이 보내는 내용을 어떻게 읽을지, 권한 요청에 노치에서 답할 수 있는지, 트랜스크립트가 어디 있는지를 정하면 줄에 내 프롬프트와 에이전트의 답, 비용이 표시돼요. 버전 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"
}
키하는 일
version2.
speaker문장에서 부르는 이름: “Ralph wants to run…”. 40자까지. 없으면 name.
colour에이전트의 색, "#RRGGBB" 형식.
adapter"fields"(기본값): fields와 events로 도구의 JSON에서 Gnista의 값을 찾아요. 버전 1과 같아요. "claude-style": 도구가 Claude Code의 훅 페이로드를 보내요. 그러면 events는 도구의 이벤트 이름을 Claude의 이름에 대응시켜요.
toolNamesclaude-style일 때: Claude의 도구에 대한 내 도구의 이름, 예: {"run_shell": "Bash"}. 줄과 요청이 똑같이 읽혀요.
hooks연결할 때 Gnista가 설치하는 것: 아래 표.
approvals권한 요청이 노치에서 기다려요: "plain"(허용 또는 거부), "answers"(질문에 대한 답과 함께) 또는 "claude"(답, ‘항상’ 규칙, 모드와 함께). claude-style이고 PermissionRequest용 claude-table 훅이 있을 때만 가능해요.
detailsGnista가 읽을 수 있는 트랜스크립트를 쓴다면 그 형식: "claude", "codex", "gemini" 또는 "antigravity". 그러면 줄에 프롬프트와 답, 그리고 antigravity를 제외하고 비용이 표시돼요.

그러면 설정 ▸ 에이전트에 내 에이전트가 다른 에이전트와 함께 나와요. 거기서 연결하면 지정한 파일에 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에이전트의 설정 파일, 홈 폴더 기준이고 에이전트 전용 폴더 안에 있어야 해요: .ralph/settings.json. 그 폴더가 있으면 에이전트가 이 Mac에 있다는 뜻이에요.
eventsGnista가 훅으로 거는 에이전트의 이벤트 이름: 글자, 숫자, _.
timeout초 단위, 1에서 600까지. 훅에 타임아웃이 있는 에이전트용.
foundBy에이전트만 쓰는 파일, 홈 폴더 기준. 폴더만으로는 설치됐는지 알 수 없을 때(다른 앱도 만들어서) 써요.

이동 규칙

세션을 클릭하면 그 터미널 탭이나 윈도우로 이동해요. 들어갈 방법이 없는 앱이라면, 이동 규칙이 대신 열 URL을 알려 줘요. 정확한 탭과 윈도우를 먼저 시도한 다음, 앱을 앞으로 가져오기만 하기 전에 쓰여요.

{ "url": "myeditor://focus?folder={cwd}&pane={tmux_pane}" }

에이전트 파일의 "jump"로 쓰거나, 한 앱의 모든 세션에 쓰려면 ~/Library/Application Support/Gnista/JumpRules/<bundle id>.json(예: com.example.Editor.json)에 두세요. 에이전트 자신의 규칙이 먼저예요.

플레이스홀더채워지는 값
{session}세션 ID.
{cwd}프로젝트 폴더.
{tty}터미널. 예: /dev/ttys004.
{pid}에이전트의 프로세스 ID.
{tmux_pane}tmux 페인. 예: %3.

모든 값은 통째로 퍼센트 인코딩돼서(/는 %2F가 돼요) URL의 구조를 바꿀 수 없어요. 플레이스홀더 중 하나라도 값이 없으면 그 규칙은 쓰이지 않아요. 파일을 열거나 코드를 실행하는 URL(file:, javascript:, data: 등)은 거부돼요.

설정에서

설정 ▸ 에이전트 ▸ 커스텀 에이전트에는 파일이 있거나 상태를 알린 적 있는 에이전트가 모두 나오고, Gnista가 마지막으로 소식을 받은 때도 보여요.