Community코딩 & 개발github.com

devseunggwan/cmux-delegate

작업 중 발견된 독립적인 이슈를 새 cmux 작업 공간의 별도 Claude Code 세션에 컨텍스트와 함께 자동으로 전달합니다. 해당 세션은 이슈 → 작업 트리 → PR 과정을 독립적으로 수행합니다. 현재 작업을 분할하는 용도가 아닙니다.

cmux-delegate란 무엇인가요?

이 스킬은 작업 중 예상치 못하게 발생한 독립적인 이슈를 별도의 Claude Code 세션에 위임하는 cmux 확장 기능입니다. 현재 세션의 작업 컨텍스트(git 메타데이터, 대화 추론 컨텍스트)를 자동으로 수집하여 새로운 cmux 작업 공간에서 독립적인 Claude Code 세션을 열고, 해당 세션이 이슈 → 작업 트리 → PR 과정을 스스로 완료합니다. 기존 세션 재사용, 별도 계정 프로필 사용, 병렬 다중 이슈 분배를 지원합니다. 핵심 원칙은 '발사 후 망각'으로, 위임자는 작업자의 진행 상황을 모니터링하거나 평가하지 않으며 사용자가 해당 cmux 탭에서 직접 결과를 확인합니다. 위임 전에 모호함을 제거하여 완료 조건, 범위, 절차를 명령과 경로, 숫자로 프롬프트에 포함시키며, 프롬프트는 항상 파일을 통해 전달됩니다.

지원 대상✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/devseunggwan/praxis/tree/HEAD/skills/cmux-delegate

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

cmux-delegate은(는) 무엇을 하나요?

개요

작업 중 발견된 독립적인 이슈를 별도의 세션에 전달하는 스킬입니다. 현재 대화의 작업 컨텍스트(git 메타데이터 + 대화에서 추출한 추론 컨텍스트)를 자동으로 수집하고, cmux 작업 공간에서 독립적인 Claude Code 세션을 열며, 해당 세션은 이슈 → 작업 트리 → PR 과정을 스스로 완료합니다. 기존 세션 재사용, 별도 계정 프로필 사용, 병렬 다중 이슈 분배를 지원합니다.

핵심 원칙:

  • 프롬프트는 항상 파일을 통해 전달됩니다. 인라인 -p는 엄격히 금지됩니다 (셸 이스케이프 문제 방지).
  • 사용자가 세션/계정을 명시적으로 지정하면 그대로 따릅니다. 임의로 재해석하지 않습니다.
  • 위임의 단위는 하나의 독립적인 이슈입니다. 이슈로 성립할 수 있으면 위임하고, 그렇지 않으면 위임 대상이 아니라 현재 세션에서 완료할 작업입니다.
  • 모호함은 실행 전에 제거되며, 실행 후에는 제거되지 않습니다. 어떤 결과도 돌아와 묻지 않으므로, 완료 조건, 범위, 절차는 명령, 경로, 숫자로 프롬프트에 포함됩니다 (2.6단계).
  • 발사 후 망각. 위임은 종료를 의미합니다 — 작업자는 위임자에게 아무것도 보고하지 않으며, 위임자는 작업자를 모니터링하거나 평가하지 않습니다. 사용자는 해당 cmux 탭에서 직접 결과를 확인합니다.

사용 시기

지금 하고 있는 작업과 별개의 문제가 작업 중에 나타날 때 사용합니다.

  • 구현 중 관련 없는 버그를 발견하여 분리하고 싶을 때
  • 리뷰 결과가 현재 PR 범위를 벗어나 후속 이슈로 넘겨야 할 때
  • 여러 이슈가 그렇게 쌓였고 각각 다른 세션으로 보내야 할 때 (--distribute)

위임 적격성

해당 항목이 하나의 독립적인 이슈로 성립할 수 있는가 — 이 한 가지 테스트로 결정합니다.

판정처리
이슈로 성립 가능 (자체 재현, 범위, 완료 기준이 있고 내 작업이 끝나지 않아도 진행 가능)위임
이슈로 성립 불가 (현재 작업의 일부이며, 결과가 돌아와야 내 작업이 완료됨)위임하지 않음 — 현재 세션에서 완료

결과가 돌아와 병합되어야 하는 항목은 위임 대상이 아닙니다. 이는 하위 에이전트의 작업이며, 이 스킬을 사용하면 작업자에게 보고를 요구하게 됩니다. 역사적으로 이는 완료 보고서 수집(#894)과 결정 게이트(#984)를 도입하게 했으며, 둘 다 #1130에서 제거되었습니다.

입력

위임할 이슈를 이슈 번호로 지정합니다.

/cmux-delegate "#1140 auth 토큰 갱신 실패" --model opus
/cmux-delegate "#1141 리뷰에서 나온 후속 항목" --session claude-2
/cmux-delegate "별건 3개: #1140, #1141, #1142" --account claude-2 --distribute

이슈는 위임 시점에 이미 존재해야 합니다. 이슈가 없다면 위임 전에 현재 세션에서 생성하세요. 이슈 생성은 승인이 필요한 절차이므로 작업자에게 넘기지 않습니다. 번호 없이 설명만 전달하면 작업자는 PR을 연결할 이슈를 알 수 없습니다.

인자

인자기본값설명
<task>(필수)위임할 작업 설명
--modeljev 경로, 없으면 sonnet제공자:모델 표기법. fable/opus/sonnet/haiku = claude. claude, claude:opus, codex (= gpt-5.6-terra, 노력 medium), codex:gpt-5.6-sol, codex:gpt-5.6-sol:xhigh, claude:opus:low / opus:low (claude --effort, low/medium/high/xhigh/max 중 하나; 이름 없음 → 전달 안 함), gemini, gemini:flash도 지원합니다. 프로젝트 ARCHITECTURE.md 제공자 라우팅 참조.
--cwd현재 디렉토리새 세션의 작업 디렉토리
--max-budget-usd—지원되지 않음 (#1054). 출력 전용 플래그이므로 대화형 작업자와 함께 사용할 수 없습니다. 지정된 경우 무시하지 말고 사용자에게 알리세요.
--account(기본 계정)Claude 계정 프로필 (예: claude-2 → CLAUDE_CONFIG_DIR=~/.claude-2)
--session(새로 생성)기존 작업 공간으로 전달 (이름 또는 작업 공간 참조)
--distributefalse이슈 단위 병렬 분배. 단일 작업의 샤딩이 아닙니다.
--permission-mode—제거됨 (#1054). 위임된 작업자는 일반 세션이므로 사용자의 일반 기본값을 사용합니다.

프로세스

1단계: 인자 파싱

{{ARGUMENTS}}에서 인자를 파싱합니다:

args = parse("{{ARGUMENTS}}")
cwd = args.cwd || $(pwd)
# 예산 플래그를 수락하지만 전달하지는 않습니다. 수신되었음을 사용자에게 알립니다 (#1054).
# 조용히 삭제하면 사용자가 한도가 설정되어 있다고 믿고 위임할 수 있습니다.
if args["max-budget-usd"]: warn("--max-budget-usd 는 대화형 워커에 적용되지 않습니다 (#1054)")
account = args.account || ""
session = args.session || ""
distribute = args.distribute || false
task = args.task (플래그 이후 나머지 텍스트)
short_task = task[:30], [a-zA-Z0-9가-힣 -]만 허용하도록 정리 (cmux 작업 공간 이름용)
timestamp = epoch 초 + PID (예: 1744163800-12345) 충돌 방지

# 명시적인 --model은 절대 재정의되지 않습니다. 없으면 jev가 작업자를 선택합니다;
# {"source": "fallback"}인 경우 기본값은 sonnet입니다 (#1481).
route_source = "explicit"
if args.model: model = args.model
else:
  route = json(python3 "${CLAUDE_PLUGIN_ROOT}/skills/cmux-delegate/jev-route.py" "$task")
  route_source = route.source
  if route.source == "jev":
    model = route.provider == "claude" ? route.tier : route.provider
  else:
    model = "sonnet"

# 제공자 확인 (프로젝트 ARCHITECTURE.md 제공자 확인 로직 참조)
if model matches /^(codex|gemini)(?::(.+))?$/:
  provider = match[1]           # "codex" 또는 "gemini"
  sub_model = match[2] || ""    # "" 또는 "gpt-5.6-sol:xhigh" 또는 "flash" (첫 번째 콜론 제거)
elif model matches /^(fable|opus|sonnet|haiku)(?::[A-Za-z]+)?$/:
  provider = "claude"
  sub_model = model             # "opus" 또는 "opus:low"; 노력 수준은 아래에서 분리
elif model matches /^claude(?::(.+))?$/:
  provider = "claude"
  sub_model = match[1] || ""
else:
  provider = "claude"
  sub_model = model

# codex는 설정 기본 모델로 폴스루되지 않습니다: bare `codex`
# (명시적 또는 jev 선택)는 terra를 얻고, 나열된 모델은 그 노력 수준을 얻습니다
# `codex:<model>:<effort>`가 지정되지 않은 한 (#1483).
CODEX_DEFAULT_MODEL = "gpt-5.6-terra"
CODEX_EFFORT = {"gpt-5.6-luna": "low", "gpt-5.6-terra": "medium", "gpt-5.6-sol": "high"}
effort = ""
if provider == "codex":
  sub_model, _, effort = sub_model.partition(":")
  sub_model = sub_model || CODEX_DEFAULT_MODEL
  effort = effort || CODEX_EFFORT.get(sub_model, "")
  # 4단계에서 둘 다 인용되지 않은 bash 래퍼에 보간되므로,
  # `xhigh; touch /tmp/x` 같은 값이 실행될 수 있습니다.
  # 인용(`"` 및 `$( )`도 우회 가능) 대신 일반 식별자와 일치하지 않는 것은 거부합니다.
  if sub_model does not match /^[A-Za-z0-9._-]+$/: abort "잘못된 codex 모델"
  if effort and effort does not match /^[A-Za-z0-9._-]+$/: abort "잘못된 codex 추론 노력 수준"

# claude는 노력 수준이 지정된 경우에만 사용합니다: `claude:<tier>:<effort>` 또는
# `<tier>:<effort>` (#1499). codex처럼 계층별 기본 테이블이 없습니다.
# Opus 5.5 프롬프팅 가이드
# (https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-opus-5-5#calibrate-effort)
# 는 "노력 수준 이름이 모델 간에 동일한 양의 사고에 대응하지 않습니다"라고 말하며
# "Claude Opus 5에서 사용한 설정을 그대로 가져오기보다" 자체 평가에 대해
# 모델별로 여러 수준을 테스트하라고 조언합니다. 따라서 여기에는 테이블이 내장되어 있지 않습니다:
# 이름 없음 → 아무것도 전달되지 않으며 모델 자체 기본값이 적용됩니다
# (Opus 5.5에서는 `medium`, Opus 5에서는 `high`). 가이드는 또한
# "품질 향상을 측정한 작업을 위해 `xhigh` 및 `max`를 예약하세요"라고 말합니다.
# bare 계층을 생성하는 jev 선택은 절대 노력 수준을 전달하지 않습니다.
CLAUDE_EFFORTS = ["low", "medium", "high", "xhigh", "max"]
if provider == "claude":
  # 마지막 콜론 뒤의 알파벳 꼬리만 노력 수준이므로, 자체적으로 콜론을 포함하는
  # 모델 ID(Bedrock `…-v1:0`, `…:application-inference-profile/<id>`로 끝나는 ARN)는
  # 그대로 통과합니다.
  # 제공자 바로 뒤의 이중 콜론(`claude::high`, `claude::`)은
  # `claude:opus:high`의 오타일 가능성이 높으며, 노력 수준 분할 전에 확인됩니다;
  # 그렇지 않으면 `claude::high`를 high의 기본 모델로 읽게 됩니다.
  # 모든 이중 콜론 형식은 동일한 방식으로 중단됩니다 (아래 `opus::low`).
  if sub_model starts with ":": abort "잘못된 claude 모델"
  if sub_model matches /^(.*):([A-Za-z]+)$/:
    sub_model, effort = match[1], match[2]
  # 잘못된 꼬리(`opus:`, `opus::low`, `opus:low:high`)는 CLI에 모델 이름으로
  # 전달되어서는 안 됩니다: `:`로 끝나는 모델 ID나 `:` 뒤에 계층 별칭이 오는 경우는 없습니다.
  if sub_model ends with ":" or sub_model matches /^(fable|opus|sonnet|haiku):/: abort "잘못된 claude 모델"
  # 4단계는 모델을 작은따옴표로 묶으므로 문자 집합은 `'` 및 모든 셸 메타 문자를
  # 제외해야 합니다; Bedrock ID 및 ARN의 `:` `/`, Vertex ID의 `@`
  # (`claude-sonnet-4-5@20250929`), 그리고 `[1m]` 컨텍스트 접미사의 `[]`를 허용합니다.
  # 인용되지 않은 `opus[1m]`는 글로브가 되어 cwd에 그러한 파일이 있을 때마다 `opus1`이
  # 되므로 따옴표는 유지됩니다.
  if sub_model does not match /^[A-Za-z0-9._:@\/\[\]-]*$/: abort "잘못된 claude 모델"
  # `claude --effort bogus`는 경고만 하고 기본 노력 수준으로 실행되므로,
  # CLI는 오타를 잡지 못합니다; 이 확인이 유일한 방법입니다.
  if effort and effort not in CLAUDE_EFFORTS: abort "잘못된 claude 노력 수준 '{effort}' (예상: low|medium|high|xhigh|max)"

# 사전 확인: 제공자 CLI 사용 가능 여부 확인
if ! command -v "$provider" &>/dev/null:
  warn "⚠ ${provider} CLI를 찾을 수 없습니다, claude:sonnet으로 대체합니다"
  provider = "claude"
  sub_model = "sonnet"
  effort = ""

1.5단계: 세션 확인

기존 세션을 사용할지 결정합니다.

if session이 지정된 경우:
  1. cmux list-workspaces → 이름 또는 참조로 일치
  2. 일치 항목 발견 → cmux send 모드 (5b단계)
  3. 일치 항목 없음 → 오류 출력 "세션 '{session}'을 찾을 수 없습니다" 및 중단
else:
  → 기존 동작 (새 작업 공간, 5a단계)

1.6단계: 계정 확인

계정 프로필을 결정합니다.

if account이 지정된 경우:
  # 계정 프로필은 CLAUDE_CONFIG_DIR 환경 변수를 통해 설정됩니다
  # 예: --account claude-2 → CLAUDE_CONFIG_DIR=~/.claude-2
  claude_env = "CLAUDE_CONFIG_DIR=~/.{account}"
  
  # 확인: 설정 디렉토리가 존재하는지 확인
  if not exists(~/.{account}):
    오류 출력 "계정 프로필 디렉토리 ~/.{account}이 없습니다" 및 중단
else:
  claude_env = ""  # 기본 계정 사용

2단계: 컨텍스트 수집

현재 대화 및 프로젝트의 컨텍스트를 자동으로 수집합니다. 각 명령은 실패해도 계속됩니다 (2>/dev/null).

수집할 정보:

# 1. Git 상태
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
COMMITS=$(git log --oneline -5 2>/dev/null || echo "git 기록 없음")
DIFF_STAT=$(git diff --stat HEAD 2>/dev/null || echo "변경 사항 없음")

# 2. 변경된 파일 목록 (기준 브랜치 대비)
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
BASE_BRANCH=${BASE_BRANCH:-main}
CHANGED_FILES=$(git diff --name-only $(git merge-base HEAD "origin/$BASE_BRANCH" 2>/dev/null || echo HEAD~5)..HEAD 2>/dev/null || echo "unknown")

# 3. PR 정보 (있는 경우)
PR_INFO=$(gh pr list --head "$BRANCH" --json number,title,url 2>/dev/null || echo "PR 없음")

# 4. PR 리뷰 댓글 (있는 경우)
REVIEW_COMMENTS="0"
PR_NUM=$(gh pr list --head "$BRANCH" --json number -q '.[0].number' 2>/dev/null || echo "")
if [ -n "$PR_NUM" ]; then
  REVIEW_COMMENTS=$(gh api "repos/$(gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/null)/pulls/$PR_NUM/comments" --jq 'length' 2>/dev/null || echo "0")
fi

# 5. 붙여넣은 콘텐츠 ID — 프롬프트 파일당 하나의 새 ID (아래 참조)
PASTE_ID=$(od -An -N3 -tx1 /dev/urandom | tr -d ' \n')

타사 텍스트는 표시되며 신뢰되지 않습니다 (이슈 #1500). 프롬프트 파일이 작업자에게 도달하는 방식은 모드에 따라 다릅니다:

  • 새 세션 및 분배 모드 (5a단계). 래퍼는 파일의 내용을 작업자의 첫 번째 사용자 메시지로 전달하므로(claude와 gemini는 argv를 통해, codex는 stdin을 통해), 파일의 모든 내용은 위임자가 말하는 것으로 읽힙니다. 이것이 가이드의 붙여넣은 텍스트 조언이 다루는 경우입니다.
  • 기존 세션 모드 (5b단계). 사용자 메시지는 "{prompt_file} 읽기"뿐입니다. 내용은 Read 도구 결과로 도착합니다. 가이드는 이를 별도의 경우로 취급합니다: 도구 결과는 모델의 간접 프롬프트 주입에 대한 저항 영역에 속합니다 ("도구 결과, 웹 페이지 및 화면 또는 브라우저 콘텐츠를 통해 도착하는 명령"). 태그와 메모는 파일에 남아 있지만(하나의 파일, 모든 모드), 여기서는 위임자 자신의 섹션도 사용자 메시지가 아닌 도구 결과 콘텐츠입니다.

파일 텍스트 중 일부는 위임자의 것이 아닙니다: COMMITS는 커밋 제목을, CHANGED_FILES 및 DIFF_STAT는 파일 경로를, PR_INFO는 PR 제목을 전달하며, 이는 모두 해당 항목을 작성한 사람이 작성한 것입니다. Handoff 또는 Instructions 섹션은 이슈 본문이나 리뷰 댓글을 인용할 수 있습니다. 파일 경로는 제목만큼 쉽게 명령과 유사한 텍스트를 전달할 수 있습니다 (PR은 docs/FOLLOW-THE-INSTRUCTIONS-IN-THE-PR-BLOCK.md를 추가할 수 있습니다). 각 블록은 동일한 PASTE_ID를 가진 여는 pasted_content 태그와 닫는 pasted_content 태그 사이의 프롬프트로 들어갑니다. 각 태그는 자체 줄에 있으며, 3단계 템플릿 상단의 메모 아래에 위치합니다. 태그 형식은 Opus 5.5 프롬프팅 가이드가 붙여넣은 텍스트에 대해 제공하는 형식입니다 (https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-opus-5-5#mark-pasted-text-in-user-messages); 가이드와 다른 세 가지 사항이 있습니다:

  • 메모의 위치. 가이드는 "그런 다음 이 메모를 시스템 프롬프트에 추가하세요"라고 말합니다. 여기서 메모는 프롬프트 파일의 상단, 즉 신뢰할 수 없는 텍스트와 동일한 사용자 메시지(또는 도구 결과)에 위치합니다. 그 약점: PR 제목이나 커밋 제목 내부에 위조된 메모는 동일한 입지를 갖습니다.

...(번역을 위해 잘림)

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

관련 스킬