들어가며
OpenAI의 Codex CLI는 터미널에서 도는 코딩 에이전트입니다. 강력하지만 기본적으로는 단일 세션·단일 에이전트로 동작합니다. 긴 작업을 여러 단계로 쪼개고, 계획과 로그를 남기고, 여러 에이전트를 병렬로 굴리는 일은 사용자가 매번 손으로 조립해야 합니다.
oh-my-codex(줄여서 OMX)는 그 조립을 상시 레이어로 만들어 Codex 위에 얹는 프로젝트입니다. 이름에서 짐작되듯 oh-my-zsh의 정신적 계승자에 가깝습니다 — Codex를 대체하지 않고, 더 강한 기본 프롬프트·일관된 워크플로·지속 상태·멀티에이전트 팀 런타임을 껍데기처럼 두릅니다. npm 패키지 oh-my-codex(리뷰 시점 버전 0.20.2, MIT 선언)로 배포되며, CHANGELOG.md가 PR 번호 #3184까지 참조하고 16개 언어 README를 갖춘, 매우 활발히 개발되는 저장소입니다.
이 글은 저장소를 클론해 코드를 직접 읽고 OMX가 실제로 무엇을 하는지, CLI가 어떻게 생겼는지, 아키텍처가 어떻게 짜여 있는지를 해부한 기록입니다.
무엇인가
한 문장으로, OMX는 OpenAI Codex CLI를 감싸 실행을 지휘하는 Node.js CLI(omx) 입니다. package.json의 자기소개도 동일합니다.
{
"name": "oh-my-codex",
"version": "0.20.2",
"description": "Multi-agent orchestration layer for OpenAI Codex CLI",
"bin": { "omx": "dist/cli/omx.js" }
}핵심은 "감싼다"는 점입니다. OMX는 모델을 직접 호출하지 않습니다. 실제 에이전트 작업은 외부 codex 바이너리가 수행하고, OMX는 그 위에서 다음을 더합니다.
- 더 강한 기본 시스템 프롬프트를 주입해 세션을 시작
$deep-interview→$ralplan→$ultragoal로 이어지는 정형 워크플로- 계획·로그·메모리·상태를 프로젝트별
.omx/디렉터리에 지속 - tmux 위에서 여러 Codex 워커를 병렬로 굴리는 팀 런타임
- Codex 라이프사이클 훅, 셸 출력 요약, 읽기전용 탐색 샌드박스 같은 보조 런타임
README도 이 경계를 분명히 합니다. "OMX는 Codex를 대체하지 않습니다. Codex가 실제 에이전트 작업을 하고, OMX는 그 주변에 더 나은 작업 레이어를 더합니다." 그래서 대상 독자도 명확합니다 — 이미 Codex CLI를 쓰고 있고 그 주변 런타임을 더 풍부하게 만들고 싶은 개발자입니다. 반대로 순수 Codex로 충분하다면 OMX는 필요 없다고 README가 직접 밝힙니다.
한 가지 오해를 먼저 정리하면, OMX는 Codex의 포크가 아닙니다. 저장소 어디에도 Codex 소스가 벤더링돼 있지 않고, 코드는 PATH에 설치된 외부 codex 실행 파일을 호출합니다.
스택과 빌드 구조
OMX는 두 언어로 된 시스템이며, TypeScript가 주력입니다.
- TypeScript / Node ≥20 —
omxCLI와 모든 오케스트레이션 로직.src/아래 약 370개의.ts파일이 번들러 없이 순수tsc로dist/에 컴파일됩니다(ES2022, NodeNext, strict). 린트·포맷은 ESLint가 아니라 Biome를 씁니다. 런타임 의존성은 놀랍도록 작습니다 —@iarna/toml,@modelcontextprotocol/sdk(MCP),zod정도입니다. - Rust(Cargo 워크스페이스, 6개 crate) — 네이티브 보조 바이너리. 비동기 런타임도, HTTP 클라이언트 크레이트도, LLM SDK도 없이
serde·fs2·libc같은 최소 의존성만 씁니다.
두 언어가 관계 맺는 방식이 특이합니다. npm 패키지가 Rust 소스를 함께 배포하고 필요할 때 빌드합니다. package.json의 files에 Cargo.toml·crates/가 포함되고, build:full 스크립트가 cargo build로 네이티브 바이너리를 만든 뒤 TS 런타임이 실행 시점에 그 바이너리를 셸아웃해 호출합니다. 크로스플랫폼 릴리스 바이너리는 dist-workspace.toml의 cargo-dist 설정으로 배포됩니다.
여섯 개 crate의 책임은 다음과 같습니다.
| Crate | 책임 |
|---|---|
omx-api | Codex 백엔드로 향하는 로컬 HTTP 프록시 사이드카(127.0.0.1:14510) |
omx-sparkshell | 긴 셸/tmux 출력을 값싼 모델로 요약하는 요약기 |
omx-mux | tmux 위에서 입력 주입·출력 캡처를 추상화한 멀티플렉서 계약 |
omx-runtime-core | 멀티에이전트 조정용 지속 상태 기계(권한 리스·디스패치 큐·메일박스·스냅샷) |
omx-runtime | omx-runtime-core 위의 얇은 CLI |
omx-explore | POSIX sh 허용목록으로 codex를 돌리는 읽기전용 탐색 샌드박스 |
디렉터리와 모듈 구조
저장소는 "코드"와 "프롬프트 데이터"가 반반입니다. 특히 아래 세 디렉터리는 OMX의 지능이 모델 호출 코드가 아니라 마크다운에 담겨 있음을 보여줍니다.
skills/(46개) — 각 디렉터리가SKILL.md하나. Codex 세션 안에서$이름트리거로 부르는 워크플로/행동 정의입니다. front-matter의name/description과<Purpose>·<Use_When>·<Strict_Loop_Contract>같은 XML풍 섹션으로 구성됩니다.autopilot,ralplan,ultragoal,team,deep-interview,code-review,tdd등이 있습니다.prompts/(37개.md) — 전문 서브에이전트의 역할(시스템 프롬프트) 정의.executor.md,architect.md,critic.md,security-reviewer.md등이<identity>/<goal>/<constraints>형식으로 적혀 있습니다.missions/— 도구 자신을 검증하는 self-test 태스크 명세.
코드 쪽 src/의 주요 디렉터리는 cli/(46개 명령 모듈 + 238KB짜리 index.ts 디스패처), team/(멀티에이전트 팀 레이어), hooks/(Codex 라이프사이클 훅 + AGENTS.md 오버레이), mcp/(6개 MCP 서버), config/, catalog/, capabilities/ 등입니다.
특이한 부속물도 있습니다. geobench/는 성능 벤치마크가 아니라 GEO(Generative Engine Optimization) 가시성 프로필로, LLM이 이 프로젝트를 얼마나 언급·인용하는지를 Claude Code·Aider·Cursor 등과 비교해 측정합니다. omx-capabilities.lock.json은 배포된 프롬프트·스킬·툴의 SHA-256 다이제스트를 57KB에 담아 드리프트를 잡는 잠금 파일입니다. 저장소는 심지어 자기 자신의 .omx 상태를 .gjc/에 커밋해 dogfooding합니다.
작동 원리
진입점부터 Codex 실행까지
CLI 진입점 src/cli/omx.ts는 29줄짜리 얇은 shim입니다. 컴파일된 dist/cli/index.js를 동적 import해 main()을 부르는 게 전부입니다.
rememberOmxLaunchContext();
const distEntry = join(root, 'dist', 'cli', 'index.js');
if (existsSync(distEntry)) {
const { main } = await import(pathToFileURL(distEntry).href);
await main(process.argv.slice(2));
if (process.argv[2] !== 'mcp-serve') {
process.exit(process.exitCode ?? 0);
}
} else {
console.error('oh-my-codex: run "npm run build" first');
process.exit(1);
}main()은 src/cli/index.ts의 거대한 switch(command)로, setup·doctor·exec·team·mission·ralph·ralplan·ultragoal·sparkshell·api·mcp-serve 등 수십 개 명령을 각 모듈로 라우팅합니다. 인자 없이 omx --madmax --xhigh처럼 플래그만 주면 기본값인 launch로 떨어집니다.
가장 읽기 쉬운 경로는 omx exec입니다. 이 경로는 선택적으로 git worktree를 만들고, 격리된 CODEX_HOME을 준비한 뒤, Codex argv를 조립해 블로킹으로 실행합니다. 실제 모델 작업을 하는 엔진이 외부 codex 프로세스임이 여기서 드러납니다.
function runCodexBlocking(
cwd: string,
launchArgs: string[],
codexEnv: NodeJS.ProcessEnv,
): void {
const { result } = spawnPlatformCommandSync("codex", launchArgs, {
cwd,
stdio: "inherit",
env: codexEnv,
encoding: "utf-8",
});
// …codex 실행 파일이 PATH에 없으면 여기서 실패
}이 경로에는 LLM SDK가 없습니다. OMX는 모델 요청을 스스로 보내지 않고, 인자를 조립해 codex에 넘길 뿐입니다. --madmax를 Codex의 --dangerously-bypass-approvals-and-sandbox로, --high/--xhigh를 -c model_reasoning_effort="high|xhigh"로 번역하는 것도 이 조립 단계에서 이뤄집니다.
Codex를 "더 강하게" 만드는 프롬프트 주입
그렇다면 껍데기가 어떻게 Codex를 강화할까요. 핵심은 Codex의 기본 시스템 프롬프트를 OMX가 생성한 파일로 갈아끼우는 것입니다.
export function injectModelInstructionsBypassArgs(
cwd: string,
args: string[],
env: NodeJS.ProcessEnv = process.env,
defaultFilePath?: string,
): string[] {
if (!shouldBypassDefaultSystemPrompt(env)) return [...args];
if (hasModelInstructionsOverride(args)) return [...args];
return [
...args,
CONFIG_FLAG,
buildModelInstructionsOverride(cwd, env, defaultFilePath),
];
}CONFIG_FLAG는 Codex의 -c이고, 오버라이드는 model_instructions_file을 OMX가 세션마다 만든 파일로 가리킵니다. OMX_BYPASS_DEFAULT_SYSTEM_PROMPT=0으로 끌 수 있습니다.
세션별 맥락은 AGENTS.md 런타임 오버레이(src/hooks/agents-overlay.ts)가 더 풍부하게 채웁니다. Codex 실행 직전에 코드베이스 지도, 활성 모드 상태(ralph 반복·autopilot 단계 등), 우선순위 노트패드, 프로젝트 메모리 요약을 <!-- OMX:RUNTIME:START --> / END 마커 사이에 주입하고, 세션이 끝나면 그 구간만 도로 벗겨냅니다. 마커 기반이라 적용/제거가 멱등적입니다.
OMX가 직접 모델을 부르는 유일한 길 — Rust 프록시
메인 경로에서는 모델을 안 부른다고 했지만, 보조 작업(긴 셸 출력 요약 등)에는 값싼 모델이 필요합니다. 이때 OMX는 자기 API 키를 요구하지 않고 Codex의 OAuth 토큰을 재사용하는 방식을 택했습니다. omx-api 사이드카가 CODEX_HOME/auth.json에서 인증을 읽어 Bearer 토큰과 계정 ID를 붙여 Codex 백엔드로 포워딩하는, std::net::TcpListener 위에 손으로 구현한 HTTP 프록시입니다.
omx-sparkshell은 이 로컬 프록시를 통해 값싼 "spark" 모델로 출력을 요약합니다.
pub const DEFAULT_API_BASE_URL: &str = "http://127.0.0.1:14510";
pub const DEFAULT_SPARK_MODEL: &str = "gpt-5.6-luna";
pub const DEFAULT_STANDARD_MODEL: &str = "gpt-5.6-terra";여기 보이는 gpt-5.6-luna/terra, 그리고 프론티어 티어 gpt-5.6-sol은 현재 실재하는 OpenAI 모델명이 아니라 .omx-config.json이나 환경변수로 덮어쓰도록 둔 전방 기본값입니다. 즉 OMX의 자체 모델 사용조차 Codex 인증에 얹혀 갑니다.
한편 omx ask는 또 다른 방식으로, claude·gemini 같은 다른 에이전트 CLI를 셸아웃해 조언을 받습니다. OMX는 직접적인 API 클라이언트라기보다 외부 에이전트 CLI들의 오케스트레이터에 가깝습니다.
멀티에이전트 팀 런타임
omx team은 이 프로젝트에서 가장 야심찬 부분입니다. 태스크를 저장소 인지형으로 분해(repo-aware-decomposition.ts)하고, 서브태스크를 역할(prompts/*.md)로 라우팅한 뒤, 각 워커를 자기만의 tmux 페인에서 도는 Codex 세션으로 띄웁니다. 워커들 사이의 조정 — 단일 쓰기자 권한 리스, 디스패치 큐, 메일박스, 스냅샷/리플레이 — 은 crates/omx-runtime-core가 구현한 지속 상태 기계이고, tmux 페인에 텍스트를 넣고 읽는 일은 crates/omx-mux가 담당합니다. 결과적으로 메시지 버스가 tmux인 지속 액터 시스템인 셈입니다.
스킬·플러그인 로딩
스킬이 Codex에 노출되는 경로는 둘입니다. 하나는 레거시 omx setup으로, 역할 프롬프트와 스킬 미러를 Codex 홈(~/.codex/ 또는 프로젝트 .codex/)에 설치하고 관리형 AGENTS.md 가이드를 써서 Codex가 $skill 트리거를 인식하게 합니다. 다른 하나는 plugins/oh-my-codex/의 Codex 플러그인으로, .codex-plugin/plugin.json이 스킬·MCP 서버·훅을 선언하고, hooks/hooks.json이 SessionStart·PreToolUse·Stop 등 모든 라이프사이클 이벤트에 훅을 겁니다. 이 플러그인 훅 shim은 방어적으로 설계돼, 현재 세션이 OMX가 띄운 세션인지 확인한 뒤에만 실제 핸들러(omx codex-native-hook)로 위임하고, 아니면 아무 일도 하지 않아 비OMX Codex 세션의 훅 계약을 건드리지 않습니다.
설치와 사용
전제는 인증된 codex가 PATH에 있어야 한다는 것입니다. 설치는 npm 전역 설치입니다.
codex --version # Codex CLI가 먼저 설치·인증돼 있어야 함
npm install -g oh-my-codex이후 설정과 스모크 테스트를 거칩니다.
omx setup --scope project --merge-agents # 또는 --scope user
omx doctor # 설치 형태·훅·전제 검증
codex login status
omx exec --skip-git-repo-check -C . "Reply with exactly OMX-EXEC-OK"권장 실행은 git 프로젝트에서 이름 붙인 worktree로 띄우는 것입니다.
omx --worktree=feat/task --madmax --xhighmacOS/Linux에서 tmux가 있으면 리더가 detached tmux에 뜨고 HUD 페인이 생깁니다. 세션 안에서는 $deep-interview "...", $ralplan "...", $ultragoal "..." 같은 $ 트리거로 워크플로를 몰고 갑니다. 짧은 프롬프트 배치를 순차 실행하려면 omx mission, 병렬 실행이 필요하면 omx team을 씁니다.
유저 관점: 어디서 작업하고 무엇과 엮이나
정리하면 유저는 omx라는 별도 CLI로 세션을 시작하지만, 실제로 프롬프트를 치는 곳은 omx가 아니라 omx가 대신 띄워 준 Codex 세션입니다. omx --madmax --xhigh를 실행하면 omx가 worktree·HUD·주입된 시스템 프롬프트를 갖춘 Codex를 tmux 안에 띄우고, 그다음부터 $deep-interview·$ralplan 같은 트리거도 그 Codex 세션 안에서 칩니다. shell에서 직접 치는 것은 omx mission·omx team·omx exec 같은 배치·병렬·비대화형 명령입니다. 즉 omx는 대화 상대가 아니라 런처이자 세션 관리자입니다.
"플러그인이냐 별도 CLI냐"로 보면 omx는 별도 CLI가 본체입니다. 저장소에 Codex 플러그인 레이아웃(plugins/oh-my-codex/)도 있지만, 앞서 봤듯 그 플러그인 훅조차 설치된 omx CLI를 다시 호출하므로 런타임에는 omx가 필요합니다.
Claude Code와는 직접 통합되지 않습니다. 유일한 접점은 자문입니다 — omx ask <claude|gemini> "..."로 Claude(또는 Gemini) CLI를 조언자로 부를 수 있습니다(ASK_PROVIDERS = ['claude', 'gemini'], src/cli/ask.ts). 정리하면 Codex가 본체이고 omx는 그 위의 워크플로 레이어이며, Claude Code는 곁다리 자문입니다.
한계와 주의점
코드와 문서에서 드러난 사항만 정리합니다.
- 강한 외부 의존. 인증된
codex가 없으면 실행 자체가 실패합니다([omx] failed to launch codex: executable not found in PATH).omx ask는claude/geminiCLI를, 권장 팀/HUD 경로는tmux(또는 Windows의psmux)를 추가로 요구합니다. - Windows·Codex App은 명시적으로 2등 시민. README가 "네이티브 Windows와 Codex App은 기본 경험이 아니며 깨지거나 일관되지 않게 동작할 수 있다"고 못 박습니다.
omx-explore샌드박스는 POSIXsh허용목록에 의존해 Windows에서 아예 동작하지 않습니다. - 보안 표면.
--madmax는 승인·샌드박스 가드레일을 제거하므로 신뢰하는 저장소에서만, 가급적 worktree 격리와 함께 쓰라고 반복 경고합니다.omx-api프록시는 OAuth 토큰과 계정 ID를 다루는 로컬 자격증명 포워딩 서버이며(로프백 바인딩·시크릿 리댁션은 있지만), 그 성격을 인지하고 쓸 필요가 있습니다. - 패키징 관찰. MIT가
package.json·Cargo.toml·플러그인 메타에 선언돼 있지만 클론에는 별도LICENSE파일이 보이지 않았습니다. - 복잡도 집중.
src/cli/index.ts238KB,setup.ts194KB,doctor.ts114KB 등 몇몇 파일에 로직이 크게 쏠려 있어 감사가 쉽지 않습니다.
결론
OMX의 아키텍처를 한 줄로 요약하면 "인자를 조립하고, 시스템 프롬프트를 주입하고, codex를 띄운다"입니다. 멀티에이전트 오케스트레이션 레이어를 표방하는 도구치고는 놀랍도록 얇은 실행 코어이고, 진짜 지능은 46개 skills/와 37개 prompts/ 마크다운에 프롬프트 엔지니어링으로 담겨 있습니다.
세 가지가 특히 인상적입니다. 첫째, 자기 API 키 대신 Codex의 OAuth 토큰을 재사용하는 손수 짠 Rust HTTP 프록시로 값싼 보조 모델을 붙인 점. 둘째, tmux를 메시지 버스 삼아 권한 리스·디스패치 큐·메일박스를 갖춘 Rust 상태 기계로 멀티에이전트를 조정하는 점. 셋째, GEO 가시성 벤치마크와 57KB SHA-256 능력 잠금 파일까지 갖춘, 보통의 CLI를 훨씬 넘어서는 운영·증거 규율입니다. Codex를 이미 쓰고 있고 그 위에 정형 워크플로와 병렬 실행 레이어가 필요한 사람에게 OMX는 "포크 없이 껍데기로 강화한다"는 접근의 잘 다듬어진 사례입니다.
이 글에서 다루지 못한 부분
분량상 다음 구성요소는 개요만 언급했습니다. autoresearch/autoresearch-goal(검증 게이트가 붙은 연구 미션), omx wiki(마크다운·검색 우선 프로젝트 지식), omx hud(모니터링 페인), 6개 MCP 서버(state/memory/code-intel/trace/wiki/hermes), omx-capabilities.lock.json 드리프트 검증의 실패 코드 체계, 그리고 omx setup의 AGENTS 병합 정책 세부입니다. 각각은 저장소의 docs/와 해당 src/ 모듈에 더 깊은 명세가 있습니다.