magi — Chat으로 요청하는 것만으로, 설계·구현·리뷰·PR·릴리스까지 여러 에이전트가 연결해주는 CLI
magi demo (desktop)
claude나 codex 같은 여러 에이전트 CLI를 사용해서, 요청한 작업을 설계부터 릴리스까지 진행하게 해주는 도구 magi를 Rust로 만들었습니다. 사람이 할 일은 기본적으로 Chat으로 요청하는 것과, 질문에 답하는 것, 그리고 마지막 머지(merge) 승인하는 것입니다.
왜 만들었는가
에이전트에게 구현을 맡길 수 있게 되었어도, 그 전후 과정은 의외로 사람이 연결하고 있습니다.
- 요청하기 전에 무엇을 만들지 모호한 상태에서 던지면, 결과물의 좋고 나쁨을 판단할 수 없습니다.
- 첫 설계 방침이 하나만 있으면, 그것이 잘못되었을 때 리뷰에서 고치기 어렵습니다. 리뷰 관점도 한 명의 에이전트로는 편향될 수 있습니다.
- PR을 만들고, CI를 보고, 지적 사항에 대응하고, 릴리스 버전(version)을 올리는 등 PR 이후 작업은 결국 스스로 감시해야 합니다.
- 자리를 비운 사이에 에이전트가 판단에 어려움을 느껴 멈춰 있어도, PC 앞에 있지 않으면 알아차리기 어렵습니다.
이러한 과정들을 '요청하면, 필요한 때만 호출되는' 형태로 만들고 싶어서 magi를 만들었습니다. subscription 계약의 CLI(claude / codex / opencode / agy / omp, 또는 임의의 명령어)를 그대로 구동하는 방식이므로, 기존 플랜 범위 내에서 작동하며 API 키가 필요 없습니다.
magi의 주요 특징
이후 설명에서는 magi.toml의 설정 이름이 나옵니다. magi.toml은 TOML로 작성된 설정 파일이며, [graph] (구현·리뷰·머지까지의 흐름), [verify] (검증에 사용하는 명령어), 그리고 [merge]와 [release] (머지 후와 릴리스 처리) 등의 섹션으로 나뉘어 있습니다. 기사에서는 [graph] implementers처럼 '섹션 이름 + 키 이름'으로 작성합니다. 실제로 만지는 것은 magi init이 생성하는 magi.toml입니다.
1. 요청은 Chat만으로
magi web에서 실행되는 모바일용 UI에 Chat 기능이 있습니다. 리포지토리를 읽거나 명령어를 실행할 수 있는 에이전트와 대화하며 만들고 싶은 것을 상담합니다. 하고 싶은 형태가 정해지면 '태스크로 등록해줘'라고 말하면, 에이전트가 대화 속에서 magi task add --solo (단독 구현)를 실행해 줍니다. 대화마다 2~3명의 implementers로 경쟁시키는 설정으로 전환할 수도 있습니다.
- 대화는 Chat 상태 그대로 이어지므로, 나중에 다른 작업을 요청할 수 있습니다.
- Chat은 기본적으로 쓰기 금지(read-only)로 작동합니다 (대화 도중 리포지토리가 바뀌면 이후 비교가 불가능하기 때문입니다).
등록된 태스크는 magi serve / magi web의 루프가 하나씩 꺼내서 실행합니다. magi web의 루프는 모바일 UI에서 바로 시작/정지할 수 있습니다. 터미널에서는 `magi run
에는 3이라고 쓰여 있으므로, 그대로 사용하면 3명이 경쟁합니다. 인원수는 magi run --implementers N (단축형 -c) 또는 [graph] implementers로 변경할 수 있습니다. Chat에서는 대화별 선택을 통해 Solo / 2 / 3 중 하나를 선택할 수 있습니다.
4. reviewers 및 fixer의 리뷰 루프
구현에 성공했다면, 다음은 유한 횟수의 리뷰 루프입니다.
- 각 reviewer에게는 검토 대상 커밋에 고정된 전용 detached worktree가 제공됩니다. 검토하는 동안 작업 트리가 변경되지 않으며, fixer와 충돌하지 않습니다. -
[verify] e2e에 작성된 명령어가 worktree에서 실행되고, 그 출력이 fixer에게 전달됩니다. - fixer가 막는(blocking) 지적 사항을 수정합니다. 다만, 확인할 수 있는 근거가 있다면, 지적 사항을 근거를 들어 거부할 수도 있습니다. - 지적 사항이 사라지고 검증도 통과하는지, [graph] review_rounds에서 정한 횟수를 소진할 때까지 반복됩니다.
finding의 ID(예: R2-1-3)는 에이전트가 아닌 magi가 부여합니다. fixer의 대응 보고를 이 ID로 대조할 수 있으므로, reviewer의 정확도를 집계하여 확인할 수 있습니다. 또한, 타임아웃 등으로 reviewer가 답변하지 않은 라운드는 '지적 사항 없음'으로 간주되지 않습니다.
5. PR・gate・e2e・릴리스까지 진행하기
`merge =
이것은 에이전트가 실행하는 커맨드이며, 답변이 올 때까지 멈춥니다. 질문은 스마트폰 UI에 선택지 버튼 형태로 표시되며, 누른 답변이 그대로 에이전트의 표준 출력으로 반환됩니다. 터미널에서는 magi answer로 같은 답변을 반환할 수 있습니다. 질문에는 HTML로 작성된 확인 화면(Panel)도 첨부될 수 있어, diff나 표를 보면서 결정할 수 있습니다.
magi demo (phone)
구현한 변경 사항의 PR 병합(merge) 역시 동일한 메커니즘입니다. [graph] land_approval은 기본값으로 on이며, 병합 전에 '무엇이 들어갈지'를 나열하는 질문이 옵니다. 침묵은 보류로 처리되어 아무것도 답변하지 않으면 병합되지 않습니다. 위에 적었듯이, actions 모드의 version bump PR만 CI가 통과하면 자동으로 병합됩니다. 무인으로 병합시키고 싶다면, land와 land_approval 둘 다 의도적으로 전환합니다. 기본적으로 구현한 변경 사항의 마지막 단계는 사람이 결정합니다.
설치 및 사용법
설치
퀵 스타트
magi init # 첫 번째 magi.toml을 작성합니다
magi doctor # CLI, 리포지토리, 사용할 에이전트를 확인합니다
magi web # 스마트폰용 UI를 실행합니다
magi web을 열면 화면의 'Start the loop'를 눌러 루프를 작동시킵니다. 이후에는 Chat으로 태스크를 요청하기만 하면 됩니다. 화면을 켜지 않고 큐(queue)만 돌리고 싶을 때는 magi serve를 사용합니다. 같은 큐의 루프는 magi web과 magi serve 둘 중 어느 한쪽에서 작동시킬 수 있습니다.
[verify] gate와 [verify] e2e는 해당 리포지토리에서 실제로 확인하고 싶은 커맨드에 맞춰 magi.toml에 작성합니다.
어디서 실행할 것인가
magi web과 magi serve는 --repo를 붙이지 않으면, 실행된 디렉토리를 '리포지토리를 지정하지 않은 태스크의 대상'으로 삼습니다. Chat의 첫 대화도 이 리포지토리에서 시작됩니다. 하나의 리포지토리만 사용할 경우, 해당 리포지토리의 루트에서 실행하고 루트의 magi.toml을 사용하면 충분합니다.
여러 리포지토리를 사용할 경우, 리포지토리가 아닌 디렉토리(저는 홈)에서 실행하여 글로벌 설정 파일에 [repos] roots를 작성하는 방식이 편리합니다.
[repos]
roots = ['{{ home() }}/src'] # <root>/<host>/<owner>/<repo> 형태로 나열된 체크아웃을 찾습니다
~는 전개되지 않으므로, 설정 파일 템플릿에서 사용할 {{ home() }}로 홈을 가리킵니다. Windows 경로에도 대응할 수 있도록 따옴표는 한 쌍(TOML의 리터럴 문자열)으로 합니다.
Chat에서는 'owner/repo에 ~를 추가해 달라'고 요청하면, 에이전트가 magi task add --repo owner/repo로 태스크를 내보냅니다. repo 이름만으로도 고유하게 결정되면 통합니다. 이름은 roots 아래의 체크아웃에서 해결되며, 목록은 magi repos에서 볼 수 있습니다. gate 등의 검증 커맨드는 각 리포지토리의 magi.toml에 작성된 것이 사용됩니다.
설정 파일 위치
설정은 3군데에 나누어 작성할 수 있습니다. 아래로 갈수록 우선순위가 높습니다.
<config_dir>/magi/config.toml # 머신 공통 (글로벌)
<repo>/.magi/config.toml
<repo>/magi.toml # 리포지토리별
<config_dir>는 OS 표준 설정 디렉토리이며, macOS의 경우 ~/Library/Application Support이고, Linux의 경우 ~/.config입니다. 환경 변수 MAGI_CONFIG_DIR로 위치를 변경할 수 있으며, 빈 문자열로 설정하면 머신 공통 계층은 읽히지 않습니다.
나누어 놓은 이유는 사용할 에이전트 목록(어떤 CLI를, 어떤 모델로 사용할지. [[agents]]에 작성합니다)과, implementers/judges/reviewers
등과 같은 역할 할당은 머신별 사정에 따른 것이고, gate는 리포지토리별 사정이기 때문입니다. 전자는 <config_dir>/magi/config.toml에 단 한 번만 작성하고, 각 리포지토리의 magi.toml에는 해당 리포지토리에 고유한 [verify] gate 등만 쓸 수 있습니다.
글로벌 설정 파일은 필수적이지 않습니다. magi init이 생성하는 magi.toml에는 PATH에서 감지된 CLI가 [[agents]]로 들어가 있으므로, 그것만으로도 작동합니다. 설정이 아무것도 없는 경우에도, PATH 상에 있는 에이전트 CLI를 그대로 사용합니다.
magi web의 Settings 화면에서 에이전트 할당이나 인원수를 저장하면 이 글로벌 설정 파일이 생성됩니다.
implementers 같은 배열은 여러 곳에 쓰면 오류가 발생합니다. 이는 병합(merge) 과정에서 합쳐져서 요청하지 않은 구성이 되는 것을 방지하기 위함입니다. 머신 측 또는 리포지토리 측, 둘 중 한 곳에만 작성합니다. 실제로 어떻게 해결되었는지는 magi doctor로 확인할 수 있습니다.
설정 파일 예시
제 설정에서 발췌하여 보여드립니다. 먼저 글로벌 설정입니다. 사용할 에이전트, 역할 할당, 실행 흐름, 병합 방식을 여기에 두고 있습니다.
# <config_dir>/magi/config.toml
[[agents]]
id = "opus"
...
다음은 리포지토리별 설정입니다. magi 자체의 리포지토리 magi.toml에서 발췌했습니다 (magi는 magi로 개발하고 있습니다). 사용할 에이전트나 역할 할당은 쓰지 않고, 이 리포지토리에 고유한 것만 씁니다.
# <repo>/magi.toml
[verify]
e2e = ["cargo test --locked --all-targets"]
...
[prompts]는 이 리포지토리에서 작동하는 에이전트에게 전달되는 프로젝트 고유의 전제 조건입니다. all은 모두에게, review는 reviewer에게만 전달됩니다.
맺음말
저에게 가장 큰 변화는 '요청'과 '승인' 사이를 제가 직접 메우지 않아도 되었다는 것입니다. 판단이 필요한 부분에만 스마트폰으로 알림이 오고, 나머지는 진행되고 있습니다.
이번에는 Chat・설계 통합・리뷰 루프・릴리스까지의 흐름을 중심으로 소개했습니다. judges 평가 메커니즘 등 여기서 다루지 않은 부분은 README에 정리되어 있습니다.
토론