SubagentStop 훅: 서브에이전트 완료 시 처리

SubagentStop 훅은 서브에이전트가 응답을 마친 시점에 실행됩니다. Stop 훅과의 차이, settings.json 설정, 에이전트 타입 matcher, 그리고 판단이 필요한 검사에 쓰는 프롬프트 기반 훅까지 정리합니다.

10분 갱신

서브에이전트를 여럿 띄워 병렬로 일을 시키면 "누가 언제, 제대로 끝났는가"를 따로 잡아야 합니다. Stop은 메인 에이전트의 턴에만 울리기 때문입니다. 이 글은 서브에이전트 하나가 끝날 때 발화하는 SubagentStop과, 판단을 모델에게 맡기는 type: "prompt" 훅을 다룹니다. 마지막 글은 UserPromptSubmit입니다.

핵심 요약

  • SubagentStop은 서브에이전트가 응답을 마칠 때 발화합니다. matcher는 에이전트 타입(Explore, Plan, general-purpose, 사용자 정의 이름)과 비교됩니다.
  • 입력에 agent_id, agent_type, agent_transcript_path, last_assistant_message, stop_hook_active가 들어옵니다.
  • 결정 제어는 Stop과 같습니다. 종료 코드 2 또는 decision: "block"이면 서브에이전트가 멈추지 않고 reason을 다음 지시로 받아 계속 일합니다.
  • 서브에이전트 결과를 부모 대화에 주입하려면 SubagentStop이 아니라 Agent 도구에 건 PostToolUse를 씁니다.
  • 판단이 필요한 검사는 type: "prompt" 훅이 {"ok": true|false, "reason": ...}로 답합니다.

SubagentStop 훅은 언제 필요한가

서브에이전트는 격리된 컨텍스트에서 돌고 요약만 부모에게 돌려줍니다. 그래서 개별 완료 로그(열두 에이전트로 나눈 문서 집필의 진행 상황), 품질 게이트(리뷰어가 "문제 없음" 한 줄로 끝내려 할 때 근거 경로를 요구해 되돌리기), 정리 작업(임시 파일 삭제, 결과 적재)이 필요해집니다.

Stop 훅과 SubagentStop 훅은 무엇이 다른가

항목 Stop SubagentStop
발화 주체 메인 에이전트 Agent 도구로 띄운 서브에이전트 각각
matcher 없음 에이전트 타입
고유 입력 last_assistant_message, background_tasks, session_crons 위 항목에 더해 agent_id, agent_type, agent_transcript_path
종료 코드 2 메인이 멈추지 않고 계속 해당 서브에이전트가 멈추지 않고 계속
루프 보호 stop_hook_active, 연속 8회 상한 동일

서브에이전트 frontmatter의 Stop 훅은 자동으로 SubagentStop이 됩니다. 서브에이전트 안의 도구 호출에는 PreToolUse·PostToolUse가 그대로 발화하며 입력에 agent_id와 agent_type이 붙습니다.

기본 설정 방법

완료 로그를 남기는 최소 구성입니다.

#!/bin/bash
# ~/.claude/hooks/on-subagent-stop.sh
INPUT=$(cat)
TYPE=$(echo "$INPUT" | jq -r '.agent_type // "unknown"')
ID=$(echo "$INPUT" | jq -r '.agent_id // ""')
LAST=$(echo "$INPUT" | jq -r '.last_assistant_message // ""' | head -c 160 | tr '\n' ' ')
echo "$(date '+%F %T') [$TYPE:$ID] $LAST" >> "$HOME/.claude/subagent.log"
exit 0
{
  "hooks": {
    "SubagentStop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$HOME\"/.claude/hooks/on-subagent-stop.sh",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

전체 대화는 agent_transcript_path로 읽되, 기록이 뒤처질 수 있으니 마지막 응답은 last_assistant_message로 읽는 것이 공식 권장입니다.

matcher로 에이전트 종류를 좁힐 수 있나

있습니다. 값은 SubagentStart와 같은 에이전트 타입입니다. 기본 제공은 general-purpose, Explore, Plan, 사용자 정의 서브에이전트는 .claude/agents/*.md frontmatter의 name 필드(파일명이 아님), 플러그인 제공 에이전트는 my-plugin:reviewer 같은 범위 이름입니다. 콜론이 들어가면 정규식 경로로 가므로 ^my-plugin:reviewer$로 고정합니다.

{
  "hooks": {
    "SubagentStop": [
      {
        "matcher": "code-reviewer",
        "hooks": [
          { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/review-gate.sh" }
        ]
      }
    ]
  }
}

review-gate.sh는 last_assistant_message에 file_path:line 형태의 근거가 없으면 stderr에 "근거 경로를 포함해 다시 보고하라"를 적고 exit 2로 끝냅니다. 리뷰어는 그 문장을 다음 지시로 받아 보고를 고칩니다. "정지 규칙 = 증거"의 서브에이전트 버전입니다. 하이픈이 든 이름의 정확 일치는 v2.1.195 이상이며 그 이전은 ^code-reviewer$로 적습니다.

SubagentStop의 additionalContext는 서브에이전트 쪽으로 갑니다. 부모 대화에 주려면 PostToolUse에 "matcher": "Agent"를 걸고 거기서 돌려줍니다.

프롬프트 베이스 훅은 무엇인가

"근거 경로가 있는가"는 정규식으로 되지만 "작업을 전부 끝냈는가"는 판단이 필요합니다. 이때 type: "prompt" 훅을 씁니다. 프롬프트와 훅 입력 JSON을 빠른 Claude 모델(기본 Haiku 계열, model로 변경 가능)에 보내고, 모델은 {"ok": true} 또는 {"ok": false, "reason": "..."}로만 답합니다.

{
  "hooks": {
    "SubagentStop": [
      {
        "matcher": "general-purpose",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "서브에이전트 종료 입력: $ARGUMENTS\n맡은 작업을 전부 끝냈고 변경 파일 목록이 있으면 {\"ok\": true}, 아니면 {\"ok\": false, \"reason\": \"남은 일\"}로만 답하세요.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

$ARGUMENTS 자리에 입력 JSON이 들어가며, 없으면 프롬프트 끝에 자동으로 붙습니다. ok: false면 Stop·SubagentStop에서는 reason이 다음 지시가 되어 계속 일하고, PreToolUse에서는 도구 호출이 거부됩니다. 기본 타임아웃 30초, 비동기 불가, 모델 호출 비용이 있으니 판단이 꼭 필요한 게이트에만 씁니다. 도구까지 쓰는 type: "agent" 훅은 실험적이라 운영에는 command를 권장한다는 것이 공식 입장입니다.

정리

  • SubagentStop은 서브에이전트 하나가 끝날 때, 에이전트 타입 matcher로 골라 발화합니다.
  • agent_type, agent_id, last_assistant_message로 로그와 게이트를 만듭니다.
  • 종료 코드 2 또는 decision: "block"은 서브에이전트를 되돌리고, 부모 주입은 Agent의 PostToolUse로 합니다.
  • 판단형 검사는 prompt 훅, 규칙형 검사는 command 훅입니다.

자주 묻는 질문

Stop 훅과 SubagentStop 훅은 무엇이 다른가요?

Stop은 메인 에이전트의 턴이 끝날 때, SubagentStop은 서브에이전트 각각이 끝날 때 발화합니다. Stop은 matcher가 없고 SubagentStop은 에이전트 타입으로 거르며, 입력에 agent_id, agent_type, agent_transcript_path가 추가됩니다.

서브에이전트가 파일을 편집하면 PostToolUse 훅도 실행되나요?

네. 서브에이전트 안의 도구 호출에도 PreToolUse·PostToolUse가 그대로 발화하고, 입력에 agent_id와 agent_type이 붙어 오므로 서브에이전트의 편집만 따로 처리할 수 있습니다.

특정 종류의 서브에이전트에만 훅을 걸 수 있나요?

있습니다. matcher에 에이전트 타입(general-purpose, Explore, Plan, 사용자 정의는 frontmatter name, 플러그인은 ^플러그인:이름$)을 적고 Explore|Plan처럼 묶을 수도 있습니다.

type: prompt 훅은 언제 쓰나요?

"작업을 다 끝냈는가"처럼 규칙으로 표현하기 어려운 판단이 필요할 때입니다. 훅 입력을 빠른 모델에 보내 ok/reason으로 답을 받고, ok: false면 reason이 다음 지시가 됩니다. 정규식으로 충분한 검사는 command 훅이 낫습니다.

팀에 클로드코드를 도입하려면 실제 업무에 맞춘 설계가 필요합니다. 무료 상담 신청 →