Your first conversation
For a second turn, streaming, or permission handling, open a Command and keep it.
import { Claude, Command, AssistantEvent } from 'activecli';
const command = Command.open(Claude, { workingDir: '/proj' });
command.subscribe((event) => { if (event instanceof AssistantEvent) process.stdout.write(event.text);});command.onPermission((request) => request.approve());
await command.setMessage('Find the bug').send();await command.setMessage('Now fix it').send();
command.close();Four things happen there
Section titled “Four things happen there”Four things happen there that are worth naming.
| Call | What it does |
|---|---|
Command.open(Claude, …) |
Mints a session id and describes the intent. No process starts. |
subscribe / onPermission |
Registered before anything runs, so the first event cannot be missed. |
setMessage(…) |
Accumulates. Returns this, so it chains. |
send() |
Spawns if needed, then writes the turn. The only point at which anything reaches the CLI. |
The second send() reuses the process
Section titled “The second send() reuses the process”The second send() reuses the running process. A new one is spawned only when something changed that a running CLI cannot be talked out of — a different provider, or a permission mode, which is a spawn-time flag.
Putting it together
Section titled “Putting it together”import { Claude, Command, AssistantEvent, ResultEvent } from 'activecli';
const command = Command.open(Claude, { workingDir: process.cwd(), permissions: 'ask-before-edit', model: 'opus',});
command.subscribe((event) => { if (event instanceof AssistantEvent && event.hasText()) { process.stdout.write(event.text); } if (event instanceof ResultEvent && event.raw['subtype'] !== 'success') { console.error('turn did not finish cleanly:', command.session?.diagnostics); }});
command.onPermission((request) => request.subtype === 'can_use_tool' ? request.approve() : request.deny('not allowed here'),);
await command.setMessage('Summarise this repository').send();await command.setAttachment('src/index.ts').setMessage('Now explain this file').send();
const sessionId = command.sessionId; // resume later with Command.resume(sessionId, Claude)command.close();Where to go next
Section titled “Where to go next”- Answering permission requests — what
onPermissionis actually deciding. - Attachments — what
setAttachmentdoes to the message. - Session lifecycle — resuming, interrupting, and stopping.