히스토리
- @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