This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ORCH is an AI agent runtime — not just a CLI tool. The core engine (domain + application + infrastructure) has zero dependencies on CLI/TUI layers. The npm package @oxgeneral/orch exports the full engine API via src/index.ts, enabling programmatic use from any Node.js application.
User-facing messaging: "Turn your AI tools into a coordinated engineering team" Technical category: "AI agent runtime" Internal architecture term: "ORCH Engine"
npm run build # Build ESM + DTS bundles (tsup)
npm run dev # Run via tsx (no build needed)
npm test # Run all tests (vitest)
npm test -- test/unit/application/orchestrator-resilience.test.ts # Single file
npm test -- --grep "state machine" # By pattern
npm run typecheck # tsc --noEmit (strict mode)
npm run coverage # Tests with coverage reportRelease: ./scripts/release.sh patch|minor|major && git push && git push --tags
Layered DDD with dependency injection — no frameworks, no decorators.
Domain (models, state machine, errors)
↓
Application (services, orchestrator, event bus)
↓
Infrastructure (adapters, file storage, process manager, templates, workspaces)
↓
CLI (Commander.js) / TUI (Ink/React)
src/container.ts defines two tiers:
- LightContainer: stores + services only — used by read-only commands (task, agent, logs, config, context, msg, goal, team, status)
- Container (extends Light): + orchestrator + adapters + ProcessManager + LiquidJS — used by run, tui, doctor
The CLI entry point (src/bin/cli.ts) determines which tier to build based on the subcommand, then lazy-imports only the needed command module.
src/domain/transitions.ts — tasks flow through: todo → in_progress → review → done, with retrying and failed branches. All transitions validated by canTransition(). Terminal statuses: done, failed, cancelled.
src/application/orchestrator.ts runs three phases per tick:
- Reconcile — check PIDs, detect stalls, handle zombies
- Dispatch — claim idle agents, launch adapter processes
- Collect — process completed runs, update stats, transition tasks
Uses a promise-chain mutex (stateMutex) for serializing state mutations. Lock ownership checked via isOwner getter.
src/infrastructure/adapters/interface.ts — adapters implement IAgentAdapter:
execute(params)spawns an external process, returns{ pid, events: AsyncGenerator<AgentEvent> }- Available: claude (Claude CLI), opencode (OpenCode, multi-provider), codex (OpenAI), cursor (Cursor IDE), shell (any command)
- Registered in
AdapterRegistry, resolved by agent'sadapterfield
All state in .orchestry/ — no database:
- Tasks/Agents/Goals/Teams: individual YAML files
- Runs: JSON metadata + append-only JSONL events
- State: single
state.json - Context/Messages: JSON files
Key utilities in src/infrastructure/storage/fs-utils.ts:
atomicWrite()— temp file + rename for corruption safetyreadJsonlTail()— reads last N lines without loading entire file (OOM protection)- All stores use
Promise.all()for parallel file reads
src/tui/App.tsx — Ink/React dashboard with batched message queue (80ms flush) to prevent OOM from high-frequency events. Detail strings capped at 2KB. RunId maps have LRU cap of 500.
src/infrastructure/skills/skill-loader.ts — loads plain Markdown skills from skills/library/ (26 skills). Two types:
- Library skills: alphanumeric+hyphen names (e.g.
qa,review,ship) — Markdown content injected into agent prompts at dispatch time - MCP skills: colon-separated names (e.g.
testing-suite:generate-tests) — handled natively by Claude CLI, skipped by SkillLoader
SkillLoader has process-lifetime in-memory cache, parallel reads via Promise.all(), and path traversal prevention (VALID_SKILL_NAME regex). Skills are auto-loaded by the orchestrator during dispatch based on each agent's skills field.
orch serve — headless daemon for CI/CD and server deployments. Files: src/cli/commands/serve.ts, src/cli/serve/once-runner.ts, src/cli/serve/structured-logger.ts.
--once— process all todo tasks, exit when all reach terminal status (exit code 1 if any failed)--log-format json|text— structured JSON (default) or human-readable text logs to stdout--log-file <path>— append logs to file alongside stdout--tick-interval <ms>— override polling interval--verbose— include high-frequencyagent:outputevents
StructuredLogger transforms OrchestratorEvent into flat log records. Idle tick throttling (every 6th idle tick logged) prevents log spam. Graceful shutdown with stream flush. Designed for pm2, systemd, or background execution.
- ESM:
"type": "module"— all local imports require.jsextension (import { Foo } from './foo.js') - TypeScript: strict mode +
noUncheckedIndexedAccess: true - Naming: camelCase for variables/functions, PascalCase for types/classes, kebab-case for filenames
- IDs: prefixed strings (
tsk_,agt_,run_,goal_,team_,msg_) generated by nanoid - Errors: domain errors extend
OrchestryErrorwithexitCodeand optionalhint - No
any: useunknownor proper generics
Tests mirror src/ under test/unit/. Mock factories in test/unit/application/helpers.ts provide makeTask(), makeAgent(), makeRun() and full buildDeps() for orchestrator testing. Vitest with threads pool (max 4).
Full process documented in docs/RELEASING.md. Summary:
- Update CHANGELOG.md — add release notes under new version header
- Update test count in
readme.mdbadge if it changed significantly - Run checks:
npm run typecheck && npm test— must be green
./scripts/release.sh patch|minor|majorThis automatically updates version in:
package.json+package-lock.json(vianpm version)src/bin/cli.ts(.version('X.Y.Z'))landing/index.html(vX.Y.Z — open source)
Then creates a git commit and tag.
git push && git push --tagsGitHub Actions (publish.yml) runs typecheck + tests, then publishes to npmjs.org.
CHANGELOG.md— release notesreadme.md— test count badge, feature descriptionslanding/index.html— stats bar (test count, feature count), terminal demo, feature descriptions