tmux + Claude Agent로 기획부터 PR까지 원커맨드 자동화하기
관련 링크
참고한 글
📚 설치 · 사용법 · tmux 설정 등 실전 가이드는 공식 문서에서 정리해두었다.
이 글은 "ship을 왜 이렇게 설계했는지", "내부에서 어떻게 움직이는지" 에 집중한다. 그대로 따라 해보고 싶다면 아래 문서부터 읽는 걸 권한다.
- 🧭 개요 — ship-overview
- 🛠 튜토리얼 (설치 + 사용법) — ship-tutorial
- 💻 리포지토리 — project-hh-com/tmux
왜 만들었나
개발 과정에서 사람의 개입을 최대한 줄이고 싶었다. AI가 코드를 잘 짜는 건 이제 당연한 얘기인데, 정작 "AI에게 일을 시키고 결과를 받는 과정" 자체는 여전히 사람 손이 많이 간다.
- 브랜치를 따고
- 요구사항을 정리해서 에이전트에게 넘기고
- 결과물에 대고 타입체크 / 린트 / 테스트 / 빌드를 돌리고
- 실패하면 로그를 다시 에이전트에게 붙여 넣고
- 커밋, 푸시, PR 생성까지 사람이 하나씩 클릭
결국 AI는 "코드 생성기"로만 쓰이고, 그 앞뒤 흐름은 사람이 매번 수동으로 연결해주는 구조였다. 특히 에이전트를 여러 개 동시에 돌리고 싶을 때 터미널을 여러개 열고 각 터미널에 프롬프트를 붙여 넣는 작업은 "내가 왜 AI의 조수 역할을 하고 있지?" 싶을 정도로 번거로웠다.
그리고 또 하나 — 에이전트가 내놓은 결과를 사람이 다시 검수하는 시간이 생각보다 컸다. 중간에 에러가 남아있지는 않은지, 요구사항과 다르게 개발하지는 않았는지 사람이 일일이 확인하는 경우가 많았다. 그래서 사람이 입력하는 건 "무엇을 만들지" 한 줄뿐이고, 브랜치 생성부터 PR까지 — 검증 단계까지 에이전트가 스스로 돌리도록 — 전부 한 커맨드로 묶어버렸다. 그게 ship이다.
ship이 내부에서 하는 일
ship "..." 한 줄을 실행하면 내부적으로 다음 단계가 순차로 돈다. (자세한 사용법은 위 튜토리얼 링크 참고.)
- 🌿
feat/{slug}-{timestamp}브랜치 생성 (main/master/prd/develop에서만 자동 분기, 그 외엔 현재 브랜치 사용) - 🤖 architect 에이전트가 요청을 분석해 필요한 하위 에이전트 목록을
agents.txt로 뽑는다 - 🗣️ 설계 단계에서 red → blue → judge 3자 토론으로 계획의 허점을 검증
- ⚡ tmux 창을 쪼개 Wave A (구현) → Wave B (검증) 두 단계로 에이전트를 순차 실행
- 🗣️ 구현 후에도 red/blue/judge 토론을 한 번 더 — judge가
REWORK판정하면 Wave를 재실행 - 💾 Wave 완료마다 중간 커밋 + 푸시
- 🔍 CI 게이트 4단계:
tsc --noEmit→eslint --max-warnings 0→npm test→npm run build - 🔄 게이트 실패 시 자동 수정 에이전트가 로그를 읽고 수정, 최대 3라운드 재시도
- 📬
release-manager에이전트가 PR 생성 (게이트 실패면 Draft,Claude Agent라벨 부착)
Wave A / Wave B는 이 프로젝트에서 쓰는 자체 용어다. tmux나 Claude의 표준 용어가 아니라 "구현 단계(A)와 검증 단계(B)를 나눈 내부 단위"라는 의미로 쓴다.
핵심 아이디어: tmux 창 분할 + 파일 플래그로 동기화
멀티 에이전트를 안정적으로 돌리는 데 가장 큰 걸림돌은 충돌과 동기화다. 두 에이전트가 같은 파일을 동시에 고치면 서로의 작업을 덮어쓰고, "언제 다음 단계로 넘어갈지"를 판단할 방법도 필요했다.
구현에서 쓴 두 가지 장치:
1. tmux split-window로 창을 쪼개 에이전트마다 독립 창을 준다
tmux가 이미 붙어있으면 같은 window 안에서 split-window -h로 오른쪽에 새 컬럼을 만들고, 같은 Wave의 나머지 에이전트는 그 컬럼 안에서 split-window -v로 세로 분할한다. 마지막에 select-layout main-vertical로 정리하면 왼쪽에 메인 창, 오른쪽에 Wave별로 세로 배치된 에이전트 창이 떨어진다. tmux가 없으면 new-session -d로 백그라운드 세션을 띄우고 tmux attach로 들여다보는 식으로 폴백한다.
2. 완료 신호는 파일 플래그(<agent>.done)로 주고받는다
각 에이전트 프롬프트 끝에 touch ${FLAG_DIR}/<name>.done 한 줄을 박아둔다. 메인 스크립트는 5초 간격으로 .done 파일 개수를 세다가, Wave 안의 전원이 끝나면 다음 단계로 넘어간다. 최대 대기 10분, 시간 초과면 넘어간다.
┌─────────────────────────────────────────────────┐
│ main pane │ Wave A (구현) │ Wave B (검증) │
│ │ ┌───────────┐ │ ┌───────────┐ │
│ ship │ │ backend │ │ │ reviewer │ │
│ (스크립트) │ ├───────────┤ │ ├───────────┤ │
│ │ │ frontend │ │ │ test │ │
│ │ ├───────────┤ │ ├───────────┤ │
│ │ │ debugger │ │ │ security │ │
│ │ └───────────┘ │ └───────────┘ │
└─────────────────────────────────────────────────┘
│ │ │
│ split-window -h split-window -h
│ (새 컬럼) (그 오른쪽 컬럼)
│
└─ 각 컬럼 내부는 split-window -v 로 세로 분할
select-layout main-vertical 로 균등 정리
Wave A가 끝나 전원이 .done을 남기면 메인 스크립트가 중간 커밋을 찍고, 같은 방식으로 오른쪽에 Wave B 컬럼을 새로 만든 뒤 검증 에이전트들을 띄운다.
💡
ship스크립트는 심볼릭 링크로 설치해 두면git pull한 번으로 즉시 최신 버전이 반영된다. 자세한 내용 → 심볼릭 링크로 단일 소스 관리하기
red / blue / judge 토론
에이전트가 만든 결과물을 사람이 다시 읽는 시간을 줄이려고 끼워 넣은 장치. ship에서 두 번 돈다.
- 설계 직후: architect가 쓴
plan.md를 대상으로 - Wave B(검증) 직후: 구현된 코드나 테스트 로그를 대상으로
한 라운드 안에서 세 에이전트가 차례로 돈다.
- red — 허점·위험요소·누락된 엣지케이스만 지적 (해결책 제시 금지)
- blue — red의 지적을 ACCEPT / MITIGATE / REJECT로 판정하고, ACCEPT·MITIGATE 항목은 실제 파일을 수정
- judge — 라운드 결과를
CONSENSUS/CONTINUE/REWORK중 하나로 판정
CONSENSUS면 다음 단계로, CONTINUE면 한 라운드 더(최대 2라운드), REWORK면 Wave 에이전트 전체를 처음부터 다시 돌린다. 판정은 judge가 작성하는 파일 첫 줄의 VERDICT: 값을 grep으로 읽어서 결정한다.
배우면서 깨달은 것들
- 에이전트는 많을수록 좋은 게 아니다. 작업이 작으면 1명이 훨씬 빠르고 디버깅도 쉽다. architect의 진짜 일은 최소한의 에이전트를 고르는 것.
- Wave 경계 = 체크포인트. 중간 커밋을 Wave 단위로 남기니까 실패해도 어디서 꼬였는지 바로 보인다.
- CI 게이트가 없으면 에이전트는 자기 말만 믿는다. "다 했다"고 말하는 에이전트도
tsc --noEmit에서 한 방에 사실이 드러난다. 실패 로그 파일 경로만 다시 에이전트에게 넘기면 자기 실수를 스스로 찾아 고친다. - 프롬프트에 로그 내용을 임베드하지 말고 파일 경로만 넘긴다. ship은 에이전트에게 "
${FLAG_DIR}/tsc.log를 Read 툴로 읽어라"고만 지시한다. 그래야 프롬프트 크기가 커지지 않고 에이전트가 필요한 부분만 골라 읽는다.
앞으로 ship을 어떻게 더 develop할 것인가
ship을 설계할 때 가장 신경 쓴 건 확장성이다. 지금은 "기획 → 구현 → 검증 → PR" 흐름 하나뿐이지만, 성격이 다른 작업도 같은 뼈대 위에 얹을 수 있어야 했다. 그래서 커맨드 · Wave · 에이전트를 분리된 층위로 만들어뒀다 — 새 커맨드를 추가할 때 tmux 분할/CI 게이트/PR 자동화 로직을 다시 짤 필요 없이, 어떤 에이전트를 어떤 Wave에 태울지만 정의하면 된다.
이 구조 위에서 앞으로 붙여볼 방향들:
fix커맨드 — 버그 수정 특화. Wave A에 debugger + test만 태워서 원인 분석 → 수정 → 회귀 테스트 작성까지 가볍게 돌린다.review커맨드 — PR 리뷰 자동화. reviewer + security가 레이어 규칙 · 타입 안전성 · 보안 취약점을 검토하고gh pr comment로 코멘트까지 단다.- 새 역할 에이전트 —
perf(번들 크기/렌더링),a11y(접근성) 같은 축을 Wave B에 붙여 검수 범위를 넓힌다. ship 스크립트의 case 분기에 역할 프롬프트만 추가하면 된다. - 스택 중립화 — 지금은 CI 게이트가
tsc/eslint/npm test/npm run build로 하드코딩되어 있다. 프로젝트 루트의.ship.json같은 설정 파일로 분리해서 React Native · Python · Go 프로젝트에서도 같은 워크플로우가 돌도록 만들 계획. - 실패 루프 학습 — CI 게이트 실패 로그와 최종 수정 커밋을 쌓아두고, 반복적으로 틀리는 패턴을 architect 프롬프트에 피드백으로 주입. 같은 실수를 두 번째부터는 안 하게.
- 비용 가시화 — 현재는 에이전트가 몇 번 돌았는지 로그에만 남는다. Wave별/토론별 토큰 사용량을 집계해서 "이 작업은 에이전트 N명으로도 충분했다"는 회고 데이터를 PR 설명에 자동으로 붙이는 것.
- 실행할수록 똑똑해지는 지식 베이스 — 지금은 로그가
agent.log,red_*.md,judge_*.md같은 식으로 Wave 단위 파일에 흩뿌려져 있다. 이걸 더 촘촘하고 체계적으로 구조화해서(작업 유형 · 투입 에이전트 · 실패/성공 · red가 찌른 허점 · judge의 판정 근거 · 최종 수정 diff) 장기 저장소에 쌓고 싶다. 실행 횟수가 늘수록 "이 프로젝트에서 자주 놓치는 엣지케이스", "이 스택에서 반복되는 타입 오류", "이 팀의 코드 컨벤션" 같은 지식이 자동으로 축적되고, 다음 번 architect가 플랜을 짤 때 그 맥락을 미리 참고하게 만들고 싶다. ship을 쓰면 쓸수록 똑똑해지는 구조.