콘텐츠로 이동

왜 ActiveCLI인가

CLI를 감싸는 일은 배포하기 전까지만 쉽습니다. 배포하고 나면 알게 됩니다. 실행 파일이 내 프로세스가 물려받은 PATH에 없다는 것, Windows는 서로 다른 네 개의 환경이라는 것, 자식을 죽여도 그 자식들은 계속 살아 있다는 것, 그리고 내 프로세스가 강제 종료되면 CLI는 살아남아 계속 과금된다는 것을 말입니다.

이 문제들은 모든 CLI에 동일하며, 이 라이브러리의 대부분이 바로 그것입니다.

문제 실제로 겪는 모습
GUI 프로세스는 최소한의 PATH만 물려받는다 사용자가 brewnvm으로 설치한 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>ea에서 잘렸고, 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로 분기되고, 상태는 인자로 계속 전달됩니다. 이것이 설계 원칙들이 겨냥하는 구체적인 반대 사례이며, 함수 모듈 방식이 추상화 실패로 이어진다는 증거입니다.

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를 흡수했습니다.