왜 ActiveCLI인가
CLI를 감싸는 일은 배포하기 전까지만 쉽습니다. 배포하고 나면 알게 됩니다. 실행 파일이 내 프로세스가 물려받은 PATH에 없다는 것, Windows는 서로 다른 네 개의 환경이라는 것, 자식을 죽여도 그 자식들은 계속 살아 있다는 것, 그리고 내 프로세스가 강제 종료되면 CLI는 살아남아 계속 과금된다는 것을 말입니다.
이 문제들은 모든 CLI에 동일하며, 이 라이브러리의 대부분이 바로 그것입니다.
함정들
섹션 제목: “함정들”| 문제 | 실제로 겪는 모습 |
|---|---|
| GUI 프로세스는 최소한의 PATH만 물려받는다 | 사용자가 brew나 nvm으로 설치한 CLI가 보이지 않는다. spawn이 ENOENT로 실패한다 |
where claude가 엉뚱한 파일을 지목한다 |
npm이 claude.cmd 옆에 확장자 없는 래퍼를 함께 설치하는데, cmd.exe는 후자를 실행한다 |
| WSL UNC 경로는 cwd가 될 수 없다 | \\wsl.localhost\...는 Windows에서 실패하고, 배포판 안에는 존재하지도 않는다 |
win32에서 shell: true는 공백에서 쪼갠다 |
C:\Program Files\ 아래의 런처가 반으로 찢어지고, 인자는 주입 가능해진다 |
cmd.exe는 따옴표 안에서도 %VAR%를 전개한다 |
CLI가 보기도 전에 인자가 조용히 다시 쓰인다 |
cmd.exe는 &에서 명령을 끝낸다 |
JSON 설정이 잘린 채로 도착한다. CI가 첫 실행에서 이것을 잡았다 |
| 유능한 터미널은 출력을 ANSI 이스케이프로 장식한다 | \x1b[1 q가 프로토콜과 같은 스트림에 실려 와 JSON 한 줄의 파싱이 실패한다 |
| 자식을 죽이는 것은 트리를 죽이는 것이 아니다 | 서브에이전트 셸과 백그라운드 작업이 계속 돌아간다 |
| git-bash 워커는 트리에서 떨어져 나간다 | taskkill /T가 그것들을 보지 못하고, 고아로 살아남는다 |
| 강제 SIGKILL은 정리 코드를 하나도 실행하지 않는다 | CLI가 호스트보다 오래 살아남고, 이후의 어떤 호스트에도 보이지 않는다 |
macOS에서는 결코 드러나지 않았을, CI가 잡아낸 것
섹션 제목: “macOS에서는 결코 드러나지 않았을, CI가 잡아낸 것”Windows CI 첫 실행에서 macOS 머신으로는 드러날 수 없었던 결함 하나가 드러났습니다.
cmd.exe를 통과하는 인자에서 a&b|c<d>e가 a에서 잘렸고, cmd는 'b' is not recognized as an internal or external command를 뱉었습니다. Node의 CommandLineToArgvW 인용이 &를 literal로 만든다는 통념은 틀렸으며, 코드의 원본 주석에도 그렇게 적혀 있었습니다.
mcp add-json이 넘기는 JSON은 정확히 이 문자들로 가득합니다. Windows 사용자의 MCP 설정이 디스크에 저장되는 길에 조용히 잘려 나갈 뻔했습니다.
해법은 & | < >만 캐럿으로 이스케이프하고 그 외에는 건드리지 않는 것입니다. 괄호까지 이스케이프한 첫 시도는 write("hi") 같은 평범한 인자를 망가뜨렸습니다. 캐럿은 인용되지 않은 문맥에서만 의미를 가지며, 괄호는 블록 문법 안에서만 특별하기 때문입니다.
조사 결과
섹션 제목: “조사 결과”이 라이브러리는 두 조건을 만족하는 프로젝트를 찾는 조사(2026-08-11) 이후에 작성되었습니다. 조건은 ①공식 SDK가 아니라 CLI를 감쌀 것, ②그 결과물이 애플리케이션이 아니라 SDK로 소비 가능할 것입니다.
두 조건을 모두 만족하는 프로젝트는 전부 작습니다.
| 저장소 | 스타 | 상태 |
|---|---|---|
| jayminwest/overstory | 1,327 | 아카이브됨 (2026-05-28) |
| Enderfga/claw-orchestrator | 545 | 살아 있는 최대 후보 |
| RobertTLange/headless-cli | 37 | 8종 CLI 지원. 두 조건에 가장 부합 |
| six-ddc/codex-dynamic-workflows | 16 | — |
스타가 많은 프로젝트는 전부 둘 중 한 조건에서 탈락합니다. pi(87,262)는 범용 툴킷이고, CLI-Anything(46,892)은 다른 축을 다루며, claude_codex_bridge(3,394)는 TUI이고, myclaude(2,743)는 Go 바이너리입니다.
CLI를 감싸 사람에게 건네면 수천 개의 스타를 받습니다. CLI를 감싸 라이브러리 소비자에게 건네는 것은, 지금까지는 예외 없이 작습니다.
아무도 AbstractAdapter를 세우지 못했습니다
섹션 제목: “아무도 AbstractAdapter를 세우지 못했습니다”최대 경쟁 JetBrains 플러그인인 zhukunpenglinyutong/jetbrains-cc-gui(스타 5,315)는 ai-bridge/channels/ 아래에 6개의 CLI 채널 — claude, codex, grok, kimi, opencode, pi — 을 붙였지만, 그것들 사이에 공통 계약이 없습니다.
| 채널 | 그 채널의 send가 받는 파라미터 |
|---|---|
| Claude | sessionId, disableThinking, agentPrompt |
| Codex | threadId, baseUrl, apiKey, serviceTier |
같은 동사인데 파라미터가 다르고, 명령 목록도 다릅니다 — Claude 7종에 Codex 3종입니다. 호출자는 페이로드를 만들기 위해 지금 상대하는 프로바이더가 누구인지 알아야 합니다. 이것은 통합이 아니라 한 지붕 아래의 분리 수납입니다.
원인은 구조에 있습니다. 코드는 모듈에 흩뿌려진 함수들 — export async function handleClaudeCommand(command, args, stdinData) — 로 작성되어 거대한 switch로 분기되고, 상태는 인자로 계속 전달됩니다. 이것이 설계 원칙들이 겨냥하는 구체적인 반대 사례이며, 함수 모듈 방식이 추상화 실패로 이어진다는 증거입니다.
ACP와의 관계
섹션 제목: “ACP와의 관계”Zed의 Agent Client Protocol은 2025-12부터 JetBrains IDE에 내장되었고 25종 이상의 에이전트를 지원합니다. ActiveCLI는 두 가지 이유로 ACP와 경쟁하지 않습니다.
- ACP는 사람이 에디터 앞에 앉아 승인한다를 전제합니다. 프로그램이 사람 없이 CLI를 구동하는 상황에서는 그 전제가 성립하지 않습니다.
- ACP는 프로세스 실행 문제 — 실행 파일 탐색, Windows 인용, WSL 경로 — 를 표준화하지 않습니다. 채택자들은 그 문제를 그대로 안고 갑니다. Zed #56176은 WSL에서 ACP 서버가 실행되지 않는다고 보고하고, Kiro 문서는 “절대 경로를 하드코딩하라”를 공식 해법으로 제시하며, JetBrains AI Assistant는 WSL을 아예 지원하지 않습니다.
따라서 ACP는 경쟁자가 아니라 어댑터 후보입니다. headless-cli가 바로 그런 방식으로 ACP를 흡수했습니다.