Claude Code, Codex, OpenCode가 내 답을 기다리는지 아는 법
Claude Code는 hook 세 개로 지금 내 답을 기다리고 있다고 알려 줘요. AskUserQuestion에 거는 PreToolUse, permission_prompt 타입의 Notification, 그리고 마지막 답장이 물음표로 끝나는 Stop이에요. idle_prompt는 어떤 답장이든 끝난 뒤 아무도 입력하지 않으면 오니까 무시해야 해요. Foxl Code가 원격 터미널에 쓰는 CLI별 정리(Claude Code, Codex, OpenCode, Hermes), 각 CLI가 보고하지 못하는 것, 에이전트에 부담을 적게 주고 실수로 프롬프트에 끼어들지 않는 hook 작성법을 담았어요.

이 페이지에서
Claude Code의 hook은 정해 둔 이벤트가 일어날 때 내가 설정한 명령을 실행해 주는 기능이에요. 그중 세 개가 지금 내 답을 기다리고 있다고 알려 줘요. 질문 창을 열 때는 AskUserQuestion 도구에 건 PreToolUse hook이 실행돼요. 권한 창이 몇 초 동안 열려 있으면 permission_prompt 타입의 Notification hook이 불려요. 답장이 끝날 때마다 실행되는 Stop hook은 답장 내용을 last_assistant_message에 담아 오니, 답장이 질문으로 끝났는지 볼 수 있어요. 이름만 보면 딱 맞아 보이는 idle_prompt는 무시해야 해요. 질문이 아니어도, 어떤 답장이든 끝난 뒤 1분쯤 아무도 입력하지 않으면 오거든요.
Foxl Code는 코딩 에이전트를 원격 터미널에서 돌려 주는 서비스인데, 원격 Claude Code 터미널이 사용자의 답을 기다린다고 표시할 때 바로 이 세 hook을 써요. v0.9.3-beta.5부터는 같은 보고 명령이 Codex, OpenCode, Hermes의 신호도 각자의 hook과 plugin으로 받아요. 아래에는 CLI별 정리와 CLI마다 있는 함정, 그리고 에이전트에 부담을 적게 주고 실수로 모델의 컨텍스트에 글이 섞여 들어가지 않는 hook 작성법을 담았어요. 실제 클라우드 터미널이 질문을 보고한 적은 아직 없어요. 무엇이 확인되지 않았는지는 마지막에 적었어요.
터미널 속 에이전트가 기다리는 걸 왜 알 수 없나요?
원격 터미널에서 에이전트를 돌리면 터미널을 여러 개 열어 두고 하나도 안 보고 있을 수 있어요. 2026-10-21에 원격 Claude Code 터미널 하나가 예, 아니요로 답할 질문을 하고 입력 창에서 기다렸는데, Foxl의 API는 답을 기다리는 task가 하나도 없다고 했어요. Foxl Code의 사이드바도, 사용자를 기다리는 task를 모아 두는 확인 필요(Needs you) 목록도, 휴대폰 앱도 조용했어요. 이번 변경 전에는 Foxl Code에 그 순간을 알아챌 장치가 없었어요.
화면을 읽으면 될 것 같지만 쉽게 깨져요. 밖에서 보면 생각 중, 끝남, 묻는 중은 CLI가 그리는 글자 몇 개 차이이고, 릴리스마다 다시 그려질 수 있어요. 저희가 쓰는 CLI 버전을 올렸더니 터미널이 입력을 받기 전에 기다리던 첫 화면이 바뀌어서, 원격 Codex와 OpenCode 터미널이 열흘 동안 아무 오류 없이 타이핑을 받지 않은 적도 있어요. hook은 CLI가 방금 무슨 일이 있었는지 문서화된 payload로 직접 알려 주는 방법이에요. 그래서 저희는 hook을 써요.
Claude Code의 어떤 hook이 "답을 기다려요"라는 뜻인가요?
저희 클라우드 환경은 Claude Code 2.1.296을 고정 버전으로 써요. 아래에서 "패키지에서 읽음"이라고 적은 내용은 실행 중인 터미널에서 본 게 아니라, 이 버전의 npm 패키지 코드에서 읽은 거예요.
| hook | 언제 오나요 | 저희가 다루는 방식 |
|---|---|---|
PreToolUse, matcher AskUserQuestion | 질문 창이 곧 열려요. tool_input에 질문과 선택지가 있어요 | 선택지가 있는 질문 |
Notification, matcher permission_prompt | 권한 창이 키 입력 없이 6초쯤 열려 있었어요(패키지에서 읽음) | 권한 요청 |
Stop | 답장이 끝났어요. last_assistant_message가 그 내용이에요 | 내용이 ?로 끝나면 질문, 아니면 턴의 끝 |
UserPromptSubmit, PostToolUse, PostToolUseFailure, SessionEnd | 사람이 입력했거나, 허락한 도구가 실행됐거나 실패했거나, CLI가 끝났어요 | 답했음 |
Notification, 타입 idle_prompt | 답장이 끝나고 60초쯤 지났는데 아무것도 입력하지 않았어요(문서 원문: "Claude finished responding about 60 seconds ago and you haven't typed since") | 무시 |
이 글에서는 묻는 중인 이벤트가 오면 질문이 열린다고, 답했음인 이벤트가 오면 질문이 닫힌다고 할게요.
대부분이 실수하는 곳은 idle_prompt 줄이에요. 메시지만 보면 딱 원하던 신호 같지만, 어떤 답장이든 끝난 뒤 아무도 입력하지 않으면 와요. 이걸 질문으로 다루면 거의 모든 턴 뒤에 알림이 생겨요. Foxl이라면 확인 필요 목록에 항목이 하나씩 생기고 휴대폰도 울려요. 그래서 Notification hook은 permission_prompt matcher로만 등록하고, 스크립트에서 타입을 한 번 더 확인해요. Stop 답장이 물음표로 끝나는지 판단할 때는 끝에 붙은 공백, Markdown 강조 기호, backtick, 닫는 괄호, 따옴표를 먼저 떼어 내요. 한중일 입력기가 치는 전각 ?도 물음표로 쳐요.
Claude Code의 최신 문서에는 전용 PermissionRequest 이벤트와 더 많은 알림 타입도 있어요. 2.1.296에 이 새 이벤트가 있는지는 확인하지 않았어요. permission_prompt는 최신 목록에도 그대로 있고, 6초를 기다리는 덕분에 화면 앞에서 바로 답한 창은 걸러져요. hook은 Claude Code의 사용자 수준 설정 파일에 넣으세요. 같은 이벤트에 이미 걸어 둔 hook이 있으면 그 뒤에 덧붙이면, 원래 있던 hook도 그대로 실행돼요.
{
"hooks": {
"PreToolUse": [{ "matcher": "AskUserQuestion",
"hooks": [{ "type": "command", "command": "report-prompt", "timeout": 10 }] }],
"Notification": [{ "matcher": "permission_prompt",
"hooks": [{ "type": "command", "command": "report-prompt", "timeout": 10 }] }],
"Stop": [{ "hooks": [{ "type": "command", "command": "report-prompt", "timeout": 10 }] }]
}
}hook은 모두 작은 스크립트 하나를 불러요. 위의 report-prompt, 이 글에서 보고 명령이라고 부르는 스크립트이고, task의 자격 증명을 환경 변수에서 읽어요. 질문이 닫혔다는 이벤트 (UserPromptSubmit 등, 표의 "답했음")도 같은 방식으로 등록하되, 아래에서 설명할 셸 조건문을 통과할 때만 실행돼요.
에이전트에 부담을 적게 주는 hook은 어떻게 쓰나요?
보고용 hook은 에이전트의 루프 안에서 돌아서, hook의 실수는 곧 에이전트의 문제가 돼요. 그래서 규칙을 다섯 개 정했어요. 모두 문서에서 읽었거나, 2.1.296 패키지 코드를 직접 읽어서 정했어요.
- stdout에는 아무것도 쓰지 마세요. 대부분의 이벤트에서 Claude Code는 hook의 stdout을 로그로만 남겨요. 하지만
UserPromptSubmit을 비롯한 몇몇 이벤트에서는 hook이 stdout에 쓴 글을 컨텍스트에 넣어서 Claude가 읽고 따를 수 있게 해요. 보고 스크립트가 상태 메시지 한 줄만 찍어도 프롬프트에 섞여 들어가요. - 언제나 exit 0으로 끝내세요. exit 2는 막을 수 있는 이벤트에서 동작을 막고, 0과 2가 아닌 다른 종료 코드는 오류 알림을 띄워요. 저희 스크립트는 네트워크가 실패해도 stderr에 한 줄만 남기고 끝나요.
- 동기 hook으로 돌리고, timeout을 짧게 주세요.
"async": true로 돌릴 수도 있지만, 고정 버전은 async hook이 끝날 때마다 터미널에 한 줄을 찍어요. 도구를 부를 때마다 hook이 돌면 이게 소음이 돼요. 저희 hook은 동기 hook이고, Claude Code 쪽 timeout은 10초로 줬어요. 스크립트 자체는 요청에 3초만 쓰고 조금 뒤 스스로 끝나요. 그래서 Claude Code에서 드는 비용은 답장이 끝날 때마다 Node.js를 한 번 띄우고 요청을 한 번 보내는 것이고, Foxl 서버에 닿지 않을 때도 길어야 3초쯤이에요. - 자주 오는 이벤트는 셸에서 거르세요.
PostToolUse는 도구를 부를 때마다 와요. 보고 명령은 질문이 열렸다고 보고할 때 표시 파일을 하나 만들어요. 닫힘 hook은 이 파일이 있을 때만 Node.js를 띄우고, 없으면 stdin을 비워서 CLI가 깨진 파이프를 보지 않게 해요.
열린 질문이 없을 때는 도구를 부를 때마다if [ -e /tmp/foxl-agent-prompt/open ]; then exec report-prompt; else cat >/dev/null; fitest -e한 번이면 끝나요. - 자격 증명은 명령줄에 넣지 마세요. hook은 CLI의 환경 변수를 물려받으니 token은 환경 변수에서 읽어요. 그러면
ps에 나오지 않아요.
Codex, OpenCode, Hermes는 어떻게 알려 주나요?
CLI마다 확장 방법이 달라요. 아래 내용은 모두 저희가 고정한 버전을 기준으로 읽었어요. Codex에는 Claude Code의 hook 형식을 그대로 받는 hook 엔진이 있어요. OpenCode와 Hermes(Nous Research의 에이전트 CLI)는 터미널 UI에서 셸 hook을 돌리지 않고 plugin을 불러와요. 저희 plugin은 이벤트를 같은 형식으로 바꿔서 같은 보고 명령에 넘겨요. AWS의 코딩 CLI인 Kiro에는 받을 만한 이벤트가 없어요.
| CLI (고정 버전) | 묻는 중 | 답했음 | 보고하지 못하는 것 |
|---|---|---|---|
Codex 0.162.1, 시스템 수준 hooks.json | PermissionRequest, last_assistant_message가 ?로 끝나는 Stop | UserPromptSubmit, PostToolUse, Interrupt, SessionEnd | Plan 모드의 질문 창 |
| OpenCode 1.19.0, 서버 plugin | question.asked, permission.asked, 답장이 ?로 끝난 idle session.status | question.replied나 rejected, permission.replied, busy session.status | - |
| Hermes 0.19.0, Python plugin | 사람에게 되묻는 도구 clarify의 pre_tool_call, pre_approval_request, ?로 끝나는 post_llm_call | clarify의 post_tool_call, post_approval_response, pre_llm_call, 중단된 on_session_end | - |
| Kiro 2.29.1 | 없음 | - | 전부. 질문이나 승인 이벤트가 없고 Stop에 답장 내용이 없어요 |
Codex. Codex는 사용자 설정에 넣은 hook을 실행하기 전에 검토를 거쳐요. 새로 넣었거나 바뀐 hook은 검토 대상으로 표시되고, 신뢰하기 전까지는 건너뛰어요. 시스템 수준 설정에 넣는 managed hook은 정책으로 관리되기 때문에 이 검토 없이 신뢰돼요. 그래서 저희 hook은 managed hook으로 넣어 두고, 보고 명령은 Foxl Code가 질문을 보고하라고 지정한 터미널에만 설치해요.
두 가지를 더 맞췄는데, 하나는 직접 돌려서 확인했고 하나는 소스에서 읽었어요. 0.162.1에서 돌려 보니 비대화형 모드인 codex exec도 managed hook을 실행하고, 도구를 부를 때마다 hook 관련 줄을 두 개씩 출력에 찍었어요. 그래서 Foxl Code가 codex exec로 시작하는 실행에서는 hook을 꺼요. 또 0.162.1 소스를 보면 Codex의 터미널 UI는 300ms보다 오래 걸리는 hook을 화면에 표시해요. 그래서 보고 명령은 보고할지 판단하고 표시 파일만 만든 뒤, 실제 요청은 분리(detach)한 자식 프로세스에 넘겨요.
Codex의 Plan 모드 질문 창은 일부러 보고하지 않아요. 이 창의 PreToolUse hook은 Codex가 질문을 허락할지 정하기 전에 실행되고, Plan 모드가 아니면 Codex가 질문을 거절해요. 0.162.1에서 재 보니 hook이 질문과 함께 실행된 뒤 Codex가 "request_user_input is unavailable in Default mode"라고 답했어요. Foxl Code의 터미널은 Default 모드로 시작하니까, 이 hook을 그대로 보고하면 실제로는 아무에게도 보인 적 없는 질문 때문에 휴대폰이 울려요. 대신 Plan 모드인 Codex 터미널의 질문 창은 확인 필요 목록에 들어오지 않아요.
OpenCode. 저희 plugin은 OpenCode의 이벤트 버스를 구독해요. 질문 이벤트는 OpenCode plugin 문서의 이벤트 목록에는 없어서, v1.19.0의 schema 소스에서 찾아 읽었어요. idle 상태가 되면 턴이 끝난 거고, 마지막 답장이 ?로 끝나면 질문으로 쳐요. 질문 창이나 권한 창이 열려 있는 동안에는 상태 변화를 무시해요. 그래서 창 앞뒤로 에이전트가 busy나 idle이 돼도 질문이 닫히지 않아요. subagent 세션에서 온 이벤트도 건너뛰어요.
Hermes. on_session_end는 이름과 달리 0.19.0에서는 턴이 끝날 때마다, 답장 바로 뒤에 실행돼요. 이걸 닫힘으로 처리했더니, 방금 그 턴에서 연 질문이 곧바로 닫혀 버렸어요. 그렇게 연결했을 때 질문 12개 가운데 7개가 열리자마자 닫혔고, 1개는 질문 보고가 닫힘 보고보다 늦게 서버에 도착해서 닫을 방법이 없는 채로 열려 남았어요. 이제는 중단된 세션 종료만 닫힘으로 치고, 보고는 Hermes에서 일어난 순서대로 하나씩 보내요.
Kiro는 이 기능을 쓸 수 없어요. Kiro의 hook에는 질문이나 승인 이벤트가 없고, stop payload에는 답장 내용이 없어요. 그래서 hook으로는 질문을 열 수도, 질문과 답을 구분할 수도 없어요. 남은 신호는 공개되지 않은 형식의 내부 세션 파일뿐인데, 저희는 그 파일을 해석하지 않기로 했어요.
Foxl이 알게 되면 무슨 일이 일어나나요?
보고는 모두 Foxl Code 서버의 엔드포인트 하나로 가고, 그 task 전용 자격 증명으로 인증돼요. 서버는 보고에 따라, 에이전트의 다른 질문이 모이는 확인 필요 목록에 항목을 열거나 닫아요. Tasks 홈에서는 그 task에 "답변을 기다리는 중"이, Work 화면에서는 "답변 대기 중"이 떠요(아래 그림은 영어 UI라 "Waiting for your answer"예요).

답장 끝의 질문은 확인 필요 목록이나 휴대폰에서 답할 수 있어요. Foxl은 답을 그 task의 터미널에 한 줄로 입력하고, 1초 뒤 Enter를 따로 보내요. Enter까지 한 덩어리로 보내면 붙여넣기로 받아들여져 제출되지 않거든요. 질문 창이나 권한 창은 터미널 UI가 기대하는 키 입력을 짐작해서 답하지 않아요. 대신 확인 필요 카드가 선택지를 보여 주고, 터미널에서 답하라고 안내하고, 터미널을 여는 버튼을 줘요. 터미널에서 답하면 같은 hook으로 카드가 닫혀요.
휴대폰 알림은 아무도 모른 채 오래 방치될 수 있는 것에만 보내요. Foxl이 보고를 받은 뒤 15초가 지나도 질문 창이나 권한 창이 열려 있으면 휴대폰에 알림을 보내요. 터미널을 보고 있던 사람이라면 몇 초 안에 답했을 테니까요. Claude Code 권한 창은 창이 열리고 6초쯤 지나야 보고가 오니까, 알림은 창이 열린 지 21초쯤 뒤에 가요. 답장 끝의 질문은 확인 필요 목록에는 보이지만 알림은 보내지 않아요. 답장이 질문으로 끝나는 일이 워낙 많거든요. 2026-10-21의 질문도 이런 경우였어요. 그래서 그 질문이 지금 다시 나온다면 확인 필요와 task에는 뜨겠지만, 휴대폰은 울리지 않을 거예요.

아직 확인하지 못한 것은 무엇인가요?
테스트에서는 실제 보고 명령을 CLI마다 고정 버전의 payload로 돌렸고, plugin은 Node.js와 Python에 고정 버전의 이벤트 형식을 넣어 돌렸어요. 서버 엔드포인트는 로컬에 띄운 Cloudflare 런타임에서 돌렸어요. 아직 확인하지 못한 건 세 가지예요.
- 실제 클라우드 터미널에서 온 질문. 2026-10-23 기준으로 실서비스 데이터베이스에 그런 질문이 하나도 없어요. 실제 터미널로 처음부터 끝까지 확인하는 일이 남아 있어요.
- Claude Code가 이 hook을 실제로 실행하는 것. 6초 지연과 async hook이 찍는 줄을 포함한 Claude Code 세부 사항은 2.1.296 패키지에서 읽었고, 실행 중인 터미널에서 본 게 아니에요. 고정 버전을 이 hook으로 돌려 보는 테스트가 있지만 아직 돌리지 않았어요.
- 다른 CLI의 터미널. Codex, OpenCode, Hermes의 몇몇 세부 사항은 소스에서 읽었어요. OpenCode가 질문 이벤트를 딱 그 이름으로 보낸다는 것도 그중 하나예요. 실제로 돌린 건
codex exec뿐이에요.
터미널에서 에이전트를 직접 돌린다면 위의 Claude Code hook 표만 가져가도 돼요. hook 세 개를 알림을 받을 수 있는 곳에 연결하고, idle_prompt는 무시하고, 아무것도 출력하지 말고, exit 0으로 끝내고, 알림을 보내기 전에 몇 초 기다리세요. Foxl Code에서 타이핑이 원격 에이전트에 어떻게 닿는지는 모든 키 입력이 엉뚱한 머신으로 가던 때의 이야기(영어)에 있어요.
참고 자료
- Claude Code: Hooks 레퍼런스Documentation
- Codex: HooksDocumentation
- OpenCode: PluginsDocumentation
- Hermes Agent (Nous Research)Reference
- Foxl v0.9.3-beta.5 릴리스Release