이 노트에 대하여
프록시 레이어조차 도구를 가져야 한다는 당연한 사실을 실감하며 만들어진 문서다. 방대한 Python 래퍼를 훨씬 작은 Clojure 코드로 재구성하며, Emacs와 gptel에서 도구 사용 가능한 경로를 연다. 얇지만 결정적인 기반층을 손에 넣는 과정이다.
히스토리
- @pi-claude — ccow → proxycli 리네이밍, 전체 타임라인 정리
- @junghan — 네임스페이스 ccow→proxycli 리팩터, 리포 생성/푸시
- 생성 — claude-code-openai-wrapper를 Clojure로 재작성
proxycli: Python 4,828줄 → Clojure 441줄
프로젝트 배경
claude-code-openai-wrapper (Python, 4,828줄)는 Claude Code CLI를 OpenAI API로 래핑하는 프로젝트. Doom Emacs + gptel에서 Claude Code의 도구(Read, Write, Edit, Bash)를 OpenAI API처럼 호출할 수 있게 해준다.
원 저자가 더 이상 관리하지 않아 포크 후 gptel 호환 작업을 진행하던 중, 핵심 기능이 441줄 Clojure로 대체 가능함을 발견하여 proxycli 로 새 리포를 만들었다.
- 원본: aaronlippold/claude-code-openai-wrapper
- 포크: junghan0611/claude-code-openai-wrapper (ko branch)
- 신규: junghan0611/proxycli
왜 proxycli가 필요한가
Emacs에서 AI를 호출하는 방법은 여럿이지만, 도구가 필요할 때 쓸 수 있는 건 제한적이다:
| 방법 | 도구 사용 | 파일 접근 | 비고 |
|---|---|---|---|
| OpenRouter API | ❌ | ❌ | API 과금, 멀티 모델 |
| CLIProxyAPI (채팅 전용) | ❌ | ❌ | 빠름, 도구 없음 |
| Claude Code TUI | ✅ | ✅ | 터미널 전환 필요 |
| proxycli | ✅ | ✅ | gptel에서 바로 호출 |
맨날 TUI를 열 수는 없다 — 어디서든 그냥 호출하고 싶을 때가 있다. proxycli는 gptel에서 도구 + 스킬 을 바로 사용 가능하게 한다.
왜 Clojure로 재작성했나
- Python 환경 관리 부담 — poetry, venv, SDK 업데이트 추적
- 과도한 코드 — gptel이 쓰는 건 2개 엔드포인트뿐인데 4,828줄
- 배포 복잡성 — Python 런타임 필요 vs GraalVM 단일 바이너리
- 언어 선호 — Clojure + GraalVM native-image 검증된 경험 (dictcli, geworfen)
핵심 발견: SDK가 하는 일의 본질
Python SDK(claude-agent-sdk)가 내부적으로 하는 일:
claude --output-format stream-json --verbose \
--max-turns 10 \
--allowedTools Read,Write,Edit,Bash,Grep,Glob,WebSearch,WebFetch \
--permission-mode bypassPermissions \
--print "사용자 프롬프트"서브프로세스 하나 실행하고 stdout의 JSON 스트림을 line-by-line 파싱. 이게 전부. SDK 없이 직접 호출하면 된다.
코드 규모 비교
| 항목 | Python | Clojure |
|---|---|---|
| 소스 파일 | 12개 | 3개 |
| 소스 줄 수 | 4,828 | 441 |
| 테스트 | 6,879줄 | ~100줄 |
| 외부 의존성 | 10개+ | 3개 |
| 배포 형태 | Python env | 39MB 단일 바이너리 |
| 기동 시간 | ~3초 | 72ms (native) |
비율: 9.1% (90.9% 감소)
제거된 불필요 코드
mcp_client.py(367줄) — MCP 서버 관리 (gptel 안 씀)tool_manager.py(404줄) — 도구 설정 API (gptel 안 씀)session_manager.py(214줄) — 세션 관리 (gptel은 stateless)parameter_validator.py(263줄) — 파라미터 호환성 리포트auth.py(286줄) — 다중 인증 방식 (CLI 인증만 필요)rate_limiter.py(95줄) — API 제한 (로컬용이라 불필요)- HTML 랜딩페이지 (593줄) — 브라우저 UI (gptel 안 씀)
- Anthropic Messages API (
/v1/messages) — gptel 안 씀
proxycli 구조
proxycli/
├── src/proxycli/
│ ├── core.clj # 진입점 (35줄)
│ ├── claude.clj # CLI 실행 + stream-json 파싱 (181줄)
│ └── server.clj # Ring HTTP + SSE 스트리밍 (225줄)
├── deps.edn # 의존성 3개
├── build.clj # uberjar 설정
├── flake.nix # NixOS + GraalVM
└── run.sh # 통합 CLI작업 타임라인 (2026-03-18)
| 시간 | 작업 |
|---|---|
| 13:47 | claude-agent-sdk 0.1.36→0.1.48 업그레이드 |
| 14:00 | Python 코드베이스 분석: gptel이 쓰는 건 2개 엔드포인트뿐 |
| 14:10 | Clojure 재작성 시작 (clj-wrapper/ 서브디렉토리) |
| 14:20 | 첫 테스트 통과 — 8 tests, 25 assertions |
| 14:24 | 커밋 & 푸시: 375줄로 Python 4,828줄 대체 |
| 16:42 | gptel 실전 테스트 — 서버 띄우고 Emacs에서 직접 호출 |
| 16:50 | stdin 닫기 버그 수정 — CLI가 —print 모드로 동작하도록 |
| 16:56 | Independent mode + 도구 실행 + CWD 지원 검증 |
| 17:12 | KST 시간 주입 + 스킬 로딩 (MCP만 차단) |
| 17:20 | 선택적 스킬 주입 (PROXYCLI_SKILLS 환경변수) |
| 17:25 | GraalVM native-image 빌드: 39MB, 72ms 기동 |
| 17:28 | native binary 타입 힌트 수정 (OutputStreamWriter) |
| 17:34 | native binary SSE 스트리밍 검증 완료 |
| 17:57 | 새 리포 junghan0611/proxycli 생성 |
| (야간) | 네임스페이스 ccow→proxycli 리팩터, 첫 커밋 |
토큰 효율성
| 모드 | 시스템 프롬프트 토큰 |
|---|---|
| 클린 (스킬 없음) | 13,546 |
| 스킬 3개 주입 | ~16,000 |
| Python 기본 (전부 로드) | 20,417 |
클린 모드로 Opus 호출도 부담 없는 컨텍스트.
Clojure 커뮤니티 기여 가능성
이 작업은 Clojure 커뮤니티에 유용한 레퍼런스:
- ProcessBuilder + stream-json 파싱 패턴 — 외부 CLI 래핑
- Ring SSE 스트리밍 — PipedOutputStream으로 비동기 SSE
- GraalVM native-image + NixOS FHS — 크로스플랫폼 바이너리 빌드
- Python→Clojure 마이그레이션 사례 — 91% 코드 감소 실증
- 타입 힌트 — GraalVM native에서
^java.io.Writer필수
비전: 범용 CLI 프록시
현재는 Claude Code CLI 전용이지만, 핵심 구조는 범용이다. Claude 전용 부분은 build-command 함수 20줄뿐. 나머지 420줄은 “CLI → OpenAI API” 범용 패턴.
향후 EDN 설정 파일로 다양한 CLI를 프록시할 수 있는 구조로 확장 가능:
{:name "claude" :cli "claude" :args ["--output-format" "stream-json" "--print"]}
{:name "ollama" :cli "ollama" :args ["run" "--format" "json"]}관련 링크
- GitHub: proxycli
- GitHub: claude-code-openai-wrapper (포크, ko branch)
- GitHub: 원본 Python wrapper
- Claude Agent SDK (Python)
- gptel — Emacs LLM client
Comments