Command
ActiveCli.Command.Command — 조립 중인 CLI 호출, 그리고 그것이 속한 살아 있는 세션.
ActiveRecord는 의도를 쌓아 마지막에 실행하는 방식으로 쿼리를 만듭니다. where(...).limit(...)는 to_a가 실행되기 전까지 아무것도 바꾸지 않고, to_sql은 실행하지 않은 채 무엇이 실행될지 보여줍니다. 커맨드라인에 대해서도 같은 구조입니다 — 세터가 쌓고, toArgv()가 보여주고, send()가 실행합니다.
class Command { static open(provider: AbstractProvider, options?: CommandOptions): Command; static resume(sessionId: string, provider: AbstractProvider, options?: CommandOptions): Command;
readonly sessionId: string; readonly workingDir: string | null;
get provider(): AbstractProvider; set provider(provider: AbstractProvider); setProvider(provider: AbstractProvider): this;
get message(): string; set message(message: string); setMessage(message: string): this;
get attachments(): readonly AbstractAttachment[]; set attachments(attachments: readonly (AbstractAttachment | string)[]); setAttachment(attachment: AbstractAttachment | string): this; setAttachments(attachments: readonly (AbstractAttachment | string)[]): this;
get model(): string | null; set model(model: string | null); setModel(model: string | null): this;
get permissions(): PermissionMode | null; set permissions(permissions: PermissionMode | string | null); setPermissions(permissions: PermissionMode | string | null): this;
get session(): Session | null; isLive(): boolean;
subscribe(listener: EventListener): this; onPermission(responder: PermissionResponder): this;
toArgv(): string[]; toPayload(): Record<string, unknown>; inspect(): string; toString(): string;
send(): Promise<this>; interrupt(): this; close(): this;
get mcp(): McpCommands; get auth(): AuthCommands;
version(): Promise<string | null>; usage(): Promise<string>; update(): Promise<CommandResult>; describe(options?: { timeoutMs?: number }): Promise<Record<string, unknown> | null>; supportsModel(model: string, options?: { timeoutMs?: number }): Promise<boolean>; exec(args: readonly string[], options?: { timeoutMs?: number }): Promise<CommandResult>;}공개 생성자는 없습니다. open()은 스스로 만든 id 아래에서 새 대화를 시작하고, resume()은 id가 가리키는 대화를 이어갑니다.
조립과 조회
섹션 제목: “조립과 조회”| 멤버 | 설명 |
|---|---|
open(provider, options) |
의도를 서술합니다. 프로세스는 시작되지 않습니다. |
resume(sessionId, provider, options) |
이미 디스크에 존재하는 대화에 대해 같은 일을 합니다. |
setProvider(p) |
다음 send()에 적용됩니다. CLI 프로세스는 다른 CLI가 될 수 없으므로 전환은 프로세스를 교체하는 일이고, 실행할 것이 생기기 전까지 그 비용을 치를 이유가 없습니다. 실행 중이던 프로세스는 즉시 물러나므로 provider와 session이 어긋나는 일은 없습니다. |
setPermissions(p) |
인스턴스 또는 이름을 받으며, 하이픈과 언더스코어를 모두 허용합니다. 모르는 이름에는 throw합니다 — 호출자가 요청한 것과 다른 모드로 조용히 실행되는 것은 아무도 승인하지 않은 편집으로 끝납니다. |
setAttachment(a) |
단수형은 목록 전체를 교체합니다. 맨 문자열은 경로로 읽힙니다. |
attachments |
send()가 비웁니다 — 첨부는 그것을 실어 나른 턴에 속합니다. |
toArgv() |
to_sql에 대응합니다. 메시지는 여기 없습니다 — 세션 모드의 CLI는 프롬프트를 stdin에서 읽습니다. |
toPayload() |
다음 턴이 stdin에 쓰일 JSON 한 줄. |
inspect() |
이 커맨드에 관한 모든 것을, 사람이 읽도록. toString()이 여기에 위임하므로 로그에 남기거나 보간한 커맨드는 [object Object]가 아니라 전체 상태로 읽힙니다. |
const command = Command.open(Claude, { workingDir: '/proj' });command.setModel('opus').setPermissions('plan').setMessage('Plan the migration');
command.toArgv();// [...streamFlags, '--session-id', '…', '--permission-mode', 'plan', '--model', 'opus']
console.log(command.inspect());// provider: claude// command: claude -p --output-format stream-json …// stdin: {"type":"user","message":{"role":"user","content":"Plan the migration"}}// model: opus// permissions: plan// attachments: 0// workingDir: /proj// session: 3f2a… (not started)| 멤버 | 설명 |
|---|---|
send() |
필요하면 spawn한 뒤 조립된 턴을 보냅니다. 무엇이든 CLI에 닿는 유일한 지점입니다. 첫 호출에서 프로세스가 시작되고 이후에는 재사용되지만, provider나 permission 모드가 바뀌었다면 예외입니다 — 후자는 spawn 시점 플래그입니다. attachments를 비웁니다. |
interrupt() |
대화를 버리지 않고 진행 중인 턴을 멈춥니다. 아래 순서 주의사항을 보십시오. |
close() |
프로세스 트리를 통해 CLI와 그것이 spawn한 모든 것을 정지시킵니다. |
subscribe(listener) |
CLI가 방출하는 각 이벤트를 전달받습니다. 체이닝됩니다. |
onPermission(responder) |
권한 요청에 답합니다. null을 반환하면 요청이 미응답으로 남아 CLI가 멈추므로, 다른 무언가가 답할 때만 그렇게 하십시오. |
session / isLive() |
구동이 아니라 조회를 위해 노출됩니다 — pid, mode, stderr. 이를 지나쳐 직접 spawn하거나 쓰는 호출자는 이 구조와 함께가 아니라 이 구조를 우회해 일하고 있는 것입니다. |
일회성 명령
섹션 제목: “일회성 명령”이들은 세션을 쓰지 않습니다. 각각 CLI를 실행하고, 출력된 것을 읽고, 종료합니다. 세션은 대화이고, 이쪽은 답이 있는 질문입니다.
| 멤버 | 설명 |
|---|---|
version() |
claude --version(출력은 2.1.170 (Claude Code))에서 뽑아낸 버전 번호. throw 대신 null — 답하지 않는 CLI는 전파할 예외가 아니라 다뤄야 할 사실입니다. 출력에 알아볼 수 있는 번호는 없지만 비어 있지도 않다면, CLI가 아무 말도 안 했다고 주장하는 대신 그 텍스트를 반환합니다. |
usage() |
CLI가 출력하는 그대로의 사용량 리포트. --no-session-persistence로 실행됩니다. |
update() |
CLI를 제자리에서 업데이트합니다. 업데이트는 내려받고 링크하므로 타임아웃은 120초입니다. 무엇이 성공인지가 설치 방식마다 다르므로 CommandResult 전체를 반환합니다. |
describe() |
이 설치본이 지금 여기서 할 수 있는 것: 슬래시 커맨드, 허용되는 모델, 이 디렉토리에서 실제로 적용되는 설정. 어느 것도 추측할 수 없고 모두 설치본마다, 프로젝트마다 다릅니다. 자체적인 단명 ephemeral 세션에서 실행되므로 아무것도 방해하지 않고 트랜스크립트 엔트리도 남기지 않습니다. 타임아웃(기본 15초) 시 null. |
supportsModel(m) |
여기서 그 모델을 실제로 실행할 수 있는지를, 가능한 가장 저렴한 턴을 보내고 돌아오는지 보아 확인합니다. “이 모델을 써도 되는가”에 답하는 명령은 없습니다 — 자격은 계정과 조직, 때로는 잔액에 달려 있습니다. 요청 비용이 드니 답을 캐시하십시오. 어떤 거부는 0으로 종료하고 본문에서 그렇게 말하므로, 종료 코드뿐 아니라 출력도 확인합니다. |
exec(args) |
탈출구. CLI는 이 라이브러리가 감싸는 속도보다 빠르게 서브커맨드를 얻으며, 누구도 우리를 기다려야 해서는 안 됩니다. 셸 없이 전달되므로 인용과 메타문자가 리터럴로 유지됩니다. |
await command.version(); // '2.1.170'await command.supportsModel('haiku');await command.exec(['mcp', 'list']);
const description = await command.describe();description?.['models'];description?.['agents'];CommandOptions
섹션 제목: “CommandOptions”ActiveCli.Command.CommandOptions — 호출자가 Command.open이나 Command.resume에 넘길 수 있는 것.
interface CommandOptions { workingDir?: string; permissions?: PermissionMode | string; model?: string; env?: NodeJS.ProcessEnv;}넷 다 선택 사항이며 각각 커맨드에 동등한 세터가 있습니다. 흔한 경우가 한 번의 호출로 읽히도록 존재합니다.
PermissionResponder
섹션 제목: “PermissionResponder”type PermissionResponder = ( request: PermissionRequestEvent,) => Record<string, unknown> | null;반환값은 request.approve()나 request.deny(reason)으로 만드십시오. 이들이 CLI가 짝짓는 request_id를 담고 있습니다. null 반환은 의도적으로 요청을 미응답으로 남깁니다.
Command를 통해 닿는 서브커맨드
섹션 제목: “Command를 통해 닿는 서브커맨드”command.mcp와 command.auth는 CLI 자신의 서브커맨드를 감싼 클래스를 반환합니다.
AuthCommands
섹션 제목: “AuthCommands”ActiveCli.Auth.AuthCommands — claude auth 명령들을 메서드로.
class AuthCommands { constructor( runner: CommandRunner, command: string, workingDir: string | null, augmentedPath?: AugmentedPath, );
login(method?: LoginMethod | string): LoginSession; status(): Promise<AuthStatus>;}status()는 8초 타임아웃으로 claude auth status --json을 실행하며, stdout이 비어 있으면 stderr을 읽습니다.
class AuthStatus { constructor(loggedIn: boolean, raw: Readonly<Record<string, unknown>>, text: string);
readonly loggedIn: boolean; readonly raw: Readonly<Record<string, unknown>>; readonly text: string;
static parse(stdout: string): AuthStatus;}--json이 파싱 가능한 것을 내놓지 못하면 산문을 읽는 쪽으로 폴백합니다 — 오래된 CLI는 그 플래그를 지원하지 않을 수 있고, “로그인되어 있지 않음”은 그래도 맞히는 것이 가장 값진 답입니다. CLI가 버전마다 로그인 플래그를 다르게 써 왔기 때문에, loggedIn은 loggedIn·logged_in·authenticated를 차례로 보고, 그다음 account 객체의 존재를, 마지막으로 텍스트를 봅니다.
class LoginMethod { static readonly SUBSCRIPTION: LoginMethod; // name 'claudeai', flag '--claudeai' static readonly CONSOLE: LoginMethod; // name 'console', flag '--console'
readonly name: string; readonly flag: string;
static named(name: string | undefined | null): LoginMethod;}
class LoginSession { constructor(process: ChildProcess);
get pid(): number | null; get output(): string;
onUrl(listener: (url: string) => void): this; onFinished(listener: (code: number | null) => void): this; submitCode(code: string): this; cancel(): this;}LoginMethod.named()는 PermissionMode.named()와 달리 거부하는 대신 기본값으로 갑니다. 그것이 CLI 자신의 기본값이고, 인식되지 않는 이름은 대개 업데이트되지 않은 호출자를 뜻하기 때문입니다.
claude auth login은 답이 있는 질문이 아니라 사용자와의 대화입니다. OAuth URL을 출력하고, 기다리고, 코드를 다시 붙여넣어야 할 수도 있습니다. LoginSession이 의도적으로 하지 않는 두 가지가 있습니다.
| 하지 않는 것 | 이유 |
|---|---|
| URL을 열지 않음 | CLI가 이미 시도합니다 — macOS에서는 open, Windows에서는 rundll32, WSL에서는 레지스트리 — 그리고 그것이 성공했는지 보고하지 않습니다. 우리가 직접 열면 CLI가 성공한 환경에서는 두 번 열립니다. |
| 코드가 필요한지 추론하지 않음 | CLI는 모든 흐름에서 Paste code here if prompted를 동일하게 출력합니다. 실제로 필요한지는 브라우저의 콜백 페이지가 CLI의 loopback 서버에 닿을 수 있는지에 달려 있고, 이는 브라우저 쪽에서 결정되며 겉으로 드러나지 않습니다. 필드를 제공하고 사용자가 쓰게 두십시오. |
URL은 stdout과 stderr 양쪽에서 찾습니다. 플랫폼에 따라 어느 쪽에서든 관측된 적이 있기 때문입니다.
const login = command.auth.login('console');login.onUrl((url) => showToUser(url));login.onFinished((code) => console.log('exited', code));login.submitCode(pastedCode);McpCommands
섹션 제목: “McpCommands”ActiveCli.Mcp.McpCommands — claude mcp 명령들을 메서드로.
class McpCommands { constructor( runner: CommandRunner, command: string, workingDir: string | null, parser?: McpOutputParser, );
list(): Promise<McpListEntry[]>; get(name: string): Promise<McpServer | null>; all(): Promise<McpServer[]>; add(name: string, config: Record<string, unknown>, options?: { scope?: McpServerScope | string }): Promise<void>; remove(name: string, options?: { scope?: McpServerScope | string }): Promise<void>;}| 메서드 | 타임아웃 | 비고 |
|---|---|---|
list() |
20초 | 이름과 상태만 — claude mcp list가 보고하는 것이 그것뿐입니다. 목록 조회는 모든 서버를 프로브하므로 가장 느립니다. 명령이 실패하면 []를 반환합니다. MCP가 설정되지 않은 것이 평범한 경우이고, 출력에서 실패와 구분되지 않습니다. |
get(name) |
12초 | 서버 하나의 전모, 또는 CLI가 서술하지 않으면 null. |
all() |
— | 목록을 조회한 뒤 각각을 서술합니다. 서버당 두 개의 명령이라 눈에 띄게 느리며, 전송 방식 정보가 필요할 때만 가치가 있습니다. |
add(…) |
15초 | mcp add-json을 실행합니다. “이미 존재함”은 예외로 올리지 않고 상태로 받아들입니다 — CLI가 실패를 종료 코드가 아니라 출력으로 보고하기 때문입니다. |
remove(…) |
10초 | project 및 local scope 서버에는 scope를 전달하십시오. 그러지 않으면 CLI가 엉뚱한 설정 파일을 보고 제거할 것을 찾지 못합니다. |
await command.mcp.add('playwright', { command: 'npx', args: ['@executeautomation/playwright-mcp-server'],}, { scope: 'user' });