프로바이더란
호출자는 프로바이더(Claude)를 이름으로 부를 뿐, 어댑터를 직접 다루지 않습니다. 이 구분은 의도적입니다. 어댑터는 협력 객체와 상태를 가진 동작하는 객체인 반면, 프로바이더는 CLI의 정체성 — “claude와 대화한다”, “codex로 전환한다”라고 말할 때 가리키는 그것입니다.
command.provider = Codex; // 기계를 넘겨주는 것이 아니라 값 대입프로바이더는 command가 실제로 어댑터를 필요로 하는 시점, 즉 send() 시점에 어댑터를 만듭니다. 그 전이 아닙니다.
정체성과 기계
섹션 제목: “정체성과 기계”두 단어는 실제로 서로 다른 것을 가리키며, 이 분리가 위의 대입문을 정직하게 만듭니다.
| 프로바이더 | 어댑터 | |
|---|---|---|
| 무엇인가 | CLI의 정체성 | 동작하는 객체 |
| 무엇을 가지는가 | 이름, 그리고 어댑터를 만드는 방법 | 협력 객체와 상태 — argv 빌더, PATH 보강, job spawner, 이벤트 파서 |
| 몇 개 존재하는가 | CLI당 하나, 싱글턴으로 export | 세션마다 새로 하나 |
| 호출자가 무엇을 하는가 | 이름으로 부른다 | 결코 손대지 않는다 |
Claude는 인스턴스화할 클래스가 아니라 싱글턴으로 export됩니다. claude CLI는 하나뿐이기 때문입니다. Command.open(Claude, …)는 무언가를 생성하는 문장이 아니라 그것을 지목하는 문장으로 읽혀야 합니다.
import { Claude, Command } from 'activecli';
const command = Command.open(Claude, { workingDir: '/proj' });대입이 공짜인 이유
섹션 제목: “대입이 공짜인 이유”command.provider = Codex가 기계를 넘기는 일이었다면 대가가 있어야 합니다 — 시작할 프로세스, 찾아야 할 실행 파일, 조립할 환경. 그런 대가는 없습니다. 프로바이더는 그중 아무것도 지니고 있지 않기 때문입니다.
이것은 Command가 ActiveRecord의 Relation에서 빌려 온 바로 그 지연입니다. 세터는 의도를 쌓고, toArgv()는 무엇이 실행될지 보여주며, send()가 실행합니다. 커넥션을 바꾸는 일은 실행 전까지 아무 비용도 들지 않습니다.
| ActiveRecord | ActiveCLI |
|---|---|
| 커넥션 전환은 실행 전까지 비용이 없다 | command.provider = Codex는 다음 send() 전까지 비용이 없다 |
command를 다른 CLI로 향하게 하는 것은 대입하는 순간이 아니라 다음 send()에서 효력을 가집니다. CLI 프로세스는 다른 프로세스가 될 수 없으므로, 전환한다는 것은 프로세스를 교체한다는 뜻입니다 — 그리고 실행할 것이 생기기 전까지 그 비용을 치를 이유가 없습니다.
프로바이더가 어댑터를 만든다
섹션 제목: “프로바이더가 어댑터를 만든다”AbstractProvider는 하위 클래스가 채워야 할 멤버 두 개를 선언하며, 그 외에는 아무것도 요구하지 않습니다.
abstract get name(): string;abstract createAdapter(): AbstractAdapter;createAdapter()는 새 어댑터를 반환합니다. 세션은 자신만의 파이프와 상태를 가진 실행 중인 프로세스이므로 자신만의 어댑터를 가집니다. 프로바이더는 두 세션 모두가 그 이름으로 불린 대상일 뿐입니다.
프로바이더를 공짜로 교체할 수 있는 이유도 여기에 있습니다. send()가 어댑터를 요구하기 전까지 Codex에 관한 그 무엇도 살아 있지 않으므로, 그것을 지목하는 일은 그저 지목하는 일입니다.
이 구분이 사는 것
섹션 제목: “이 구분이 사는 것”이 라이브러리를 쓰기 전에 조사한 경쟁 플러그인은 여섯 개의 CLI 채널을 붙여 두었지만 그 사이에 공통 계약이 없습니다. send는 Claude에 대해 sessionId·disableThinking·agentPrompt를, Codex에 대해 threadId·baseUrl·apiKey·serviceTier를 받습니다 — 같은 동사에 다른 파라미터이므로, 호출자는 페이로드를 만들기 위해 지금 상대가 어느 프로바이더인지 알아야 합니다. 그것은 통합이 아니라 한 지붕 아래의 분리 수납입니다.
정체성을 기계와 분리해 두는 것이 다른 방식을 가능하게 합니다. 호출자는 어느 CLI인지를 한 단어로 말하고, 그 단어가 함축하는 것은 호출자가 아니라 어댑터의 문제입니다.
다음으로 볼 것
섹션 제목: “다음으로 볼 것”- AbstractProvider — 모든 프로바이더가 드러내는 표면, 그리고
ask()가 거기 있는 이유. - Claude 프로바이더 — 현재 존재하는 유일한 구현.
- 프로바이더 작성하기 — 직접 CLI를 붙이기.