This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a monorepo for the headless-coder-sdk - a unified TypeScript SDK that provides a consistent interface for multiple headless AI coders (Codex, Claude, Gemini). It uses pnpm workspaces and is published as separate npm packages under the @headless-coder-sdk/* scope.
The SDK standardizes threads, streaming, structured outputs, permissions, and sandboxing across different AI coder backends, allowing seamless switching with a single line of code.
Install dependencies:
pnpm installBuild all packages:
pnpm build
# Internally runs: node ./scripts/run-workspaces.mjs buildLint:
pnpm lintTest all packages:
pnpm test
# Internally runs: node ./scripts/run-workspaces.mjs testRun smoke tests:
pnpm smoke
# Set HEADLESS_CODER_KEEP_SMOKE_TMP=1 to keep temp project for inspectionRun ACP server E2E tests:
pnpm acp:e2eWork with specific packages:
# Build a single package
pnpm --filter @headless-coder-sdk/core build
# Run ACP server in dev mode
pnpm --filter @headless-coder-sdk/acp-server devRun examples:
pnpm run examplesThe SDK uses an adapter pattern with a central registry to provide unified access to different AI coder backends:
@headless-coder-sdk/core- Defines shared types (HeadlessCoder,ThreadHandle,CoderStreamEvent) and the adapter registry (factory.ts)- Adapter packages - Each adapter (codex, claude, gemini) implements the
HeadlessCoderinterface and exports:CODER_NAME- unique provider identifier constantcreateAdapter()- factory function withcoderNameproperty for auto-registrationcreateHeadless*()- convenience helper that registers and returns coder in one call
HeadlessCoder- Main interface withstartThread(),resumeThread(),getThreadId(),close()ThreadHandle- Returned by startThread/resumeThread, hasrun()andrunStreamed()methodsCoderStreamEvent- Unified event types:init,message,tool_use,tool_result,progress,permission,file_change,plan_update,usage,error,cancelled,doneRunResult- ContainsthreadId,text,json,usage,raw
Adapters register themselves via registerAdapter(factory) which reads the factory's coderName property. Users then call createCoder(CODER_NAME, opts) to instantiate.
packages/
├── core/ # Shared types, factory, registry
├── codex-adapter/ # OpenAI Codex SDK wrapper (server-only, Node.js)
├── claude-adapter/ # Anthropic Claude Agent SDK wrapper
├── gemini-adapter/ # Google Gemini CLI wrapper
├── acp-server/ # Next.js ACP protocol server (REST + streaming)
└── examples/ # Example scripts demonstrating runtime wiring
Important: The Codex adapter (and other CLI-based adapters) must run in Node.js environments. They cannot run in browser/client contexts. In frameworks like Next.js:
- Use lazy imports in server components/API routes:
const { createHeadlessCodex } = await import('@headless-coder-sdk/codex-adapter') - Routes should export
runtime = 'nodejs' - Gate with
if (typeof window !== 'undefined')checks
Each adapter normalizes provider-specific events into the unified CoderStreamEvent schema. The originalItem field always preserves the raw provider event for debugging.
Adapters must emit at minimum: init → message → done
All adapters support cooperative cancellation:
RunOpts.signal(AbortSignal) for individual runsthread.interrupt(reason?)for thread-level cancellation- Aborted runs throw
AbortErrorwithcode: 'interrupted' - Streams emit a
cancelledevent before ending
Each publishable package uses tsup for dual ESM/CJS builds:
- Entry points at
dist/*.js(ESM) anddist/*.cjs(CJS) - Type definitions at
dist/*.d.ts - Exports defined via
package.json"exports" field for proper resolution - All packages emit flattened entry points (no deep
dist/*/srcpaths)
- Unit tests: Verify provider event mapping to
CoderStreamEvent - Integration tests: Test
init → message → donesequences - Smoke tests: Build, pack tarballs, install in throwaway project, exercise both ESM/CJS
- ACP E2E: Test the ACP server's REST + streaming endpoints
Test files use Node.js test runner (tsx --test) and are located in test/*.test.ts within adapter packages.
A Next.js application that exposes the Headless Coder SDK via the Agent Communication Protocol:
- Dynamic provider config via
acp.config.json(validated against schema) - NDJSON streaming for real-time responses
- Optional Bearer token auth via
ACP_TOKENenv var - Key routes:
GET /api/acp/agents- List enabled agentsPOST /api/acp/sessions- Create threadPOST /api/acp/messages?stream=true- Stream events as NDJSON
Sessions are in-memory by default (add Redis/Postgres for persistence).
See docs/create-your-own-adapter.md for detailed guide. Key requirements:
- Export
CODER_NAMEconstant andcreateAdapter()function - Assign
createAdapter.coderName = CODER_NAME - Implement
HeadlessCoderinterface - Map provider events to unified
CoderStreamEventtypes - Support cancellation via AbortSignal
- Preserve raw events in
originalItemfield
- Helper factories (
createHeadlessCodex/Claude/Gemini) register adapters and return coders in one call package.jsonexposed via exports map for runtime version inspection- Server-only adapters use direct SDK calls with AbortSignal-based cancellation (no worker assets needed)