Repository: coleam00/Archon
Stars: 18516
CLAUDE.md
Project Overview
Remote Agentic Coding Platform: Control AI coding assistants (Claude Code SDK, Codex SDK) remotely from Slack, Telegram, and GitHub. Built with Bun + TypeScript + SQLite/PostgreSQL, single-developer tool for AI-assisted development practitioners. Architecture prioritizes simplicity, flexibility, and user control.
Core Principles
Single-Developer Tool
- No multi-tenant complexity
Platform Agnostic
- Unified conversation interface across Slack/Telegram/GitHub/cli/web
- Platform adapters implement IPlatformAdapter
- Stream/batch AI responses in real-time to all platforms
Type Safety (CRITICAL)
- Strict TypeScript configuration enforced
- All functions must have complete type annotations
- No any types without explicit justification
- Interfaces for all major abstractions
Zod Schema Conventions
- Schema naming: camelCase, descriptive suffix (e.g., workflowRunSchema, errorSchema)
- Type derivation: always use z.infer<typeof schema> β never write parallel hand-crafted interfaces
- Import z from @hono/zod-openapi (not from zod directly)
- All new/modified API routes must use registerOpenApiRoute(createRoute({...}), handler) β the local wrapper handles the TypedResponse bypass
- Route schemas live in packages/server/src/routes/schemas/ β one file per domain
- Engine schemas live in packages/workflows/src/schemas/ β one file per concern (dag-node, workflow, workflow-run, retry, loop, hooks); index.ts re-exports all
- Engine schema naming: camelCase (e.g., dagNodeSchema, workflowBaseSchema, nodeOutputSchema)
- TRIGGER_RULES and WORKFLOW_HOOK_EVENTS are derived from schema .options β never duplicate as a plain array (exception: @archon/web must define a local constant since api.generated.d.ts is type-only and cannot export runtime values)
- loader.ts uses dagNodeSchema.safeParse() for node validation; graph-level checks (cycles, deps, $nodeId.output refs) remain as imperative code in validateDagStructure()
Git Workflow and Releases
- main is the release branch. Never commit directly to main.
- dev is the working branch. All feature work branches off dev and merges back into dev.
- To release, use the /release skill. It compares dev to main, generates changelog entries, bumps the version, and creates a PR to merge dev into main.
- Releases follow Semantic Versioning: /release (patch), /release minor, /release major.
- Changelog lives in CHANGELOG.md and follows Keep a Changelog format.
- Version is the single version field in the root package.json.
Git as First-Class Citizen
- Let git handle what git does best (conflicts, uncommitted changes, branch management)
- Surface git errors to users for actionable issues (conflicts, uncommitted changes)
- Handle expected failure cases gracefully (missing directories during cleanup)
- Trust git's natural guardrails (e.g., refuse to remove worktree with uncommitted changes)
- Use @archon/git functions for git operations; use execFileAsync (not exec) when calling git directly
- Worktrees enable parallel development per conversation without branch conflicts
- Workspaces automatically sync with origin before worktree creation (ensures latest code)
- NEVER run git clean -fd - it permanently deletes untracked files (use git checkout . instead)
Engineering Principles
These are implementation constraints, not slogans. Apply them by default.
KISS β Keep It Simple, Stupid
- Prefer straightforward control flow over clever meta-programming
- Prefer explicit branches and typed interfaces over hidden dynamic behavior
- Keep error paths obvious and localized
YAGNI β You Aren't Gonna Need It
- Do not add config keys, interface methods, feature flags, or workflow branches without a concrete accepted use case
- Do not introduce speculative abstractions without at least one current caller
- Keep unsupported paths explicit (error out) rather than adding partial fake support
DRY + Rule of Three
- Duplicate small, local logic when it preserves clarity
- Extract shared utilities only after the same pattern appears at least three times and has stabilized
- When extracting, preserve module boundaries and avoid hidden coupling
SRP + ISP β Single Responsibility + Interface Segregation
- Keep each module and package focused on one concern
- Extend behavior by implementing existing narrow interfaces (IPlatformAdapter, IAgentProvider, IDatabase, IWorkflowStore) whenever possible
- Avoid fat interfaces and "god modules" that mix policy, transport, and storage
- Do not add unrelated methods to an existing interface β define a new one
Fail Fast + Explicit Errors β Silent fallback in agent runtimes can create unsafe or costly behavior
- Prefer throwing early with a clear error for unsupported or unsafe states β never silently swallow errors
- Never silently broaden permissions or capabilities
- Document fallback behavior with a comment when a fallback is intentional and safe; otherwise throw
No Autonomous Lifecycle Mutation Across Process Boundaries
- When a process cannot reliably distinguish "actively running elsewhere" from "orphaned by a crash" β typically because the work was started by a different process or input source (CLI, adapter, webhook, web UI, cron) β it must not autonomously mark that work as failed/cancelled/abandoned based on a timer or staleness guess.
- Surface the ambiguous state to the user and provide a one-click action.
- Heuristics for recoverable operations (retry backoff, subprocess timeouts, hygiene cleanup of terminal-status data) remain appropriate; the rule is about destructive mutation of non-terminal state owned by an unknowable other party.
- Reference: #1216 and the CLI orphan-cleanup precedent at packages/cli/src/cli.ts:256-258.
Determinism + Reproducibility
- Prefer reproducible commands and locked dependency behavior in CI-sensitive paths
- Keep tests deterministic β no flaky timing or network dependence without guardrails
- Ensure local validation commands (bun run validate) map directly to CI expectations
Reversibility + Rollback-First Thinking
- Keep changes easy to revert: small scope, clear blast radius
- For risky changes, define the rollback path before merging
- Avoid mixed mega-patches that block safe rollback
Essential Commands
Development
Start server + Web UI together (hot reload for both)
bun run devOr start individually
bun run dev:server # Backend only (port 3090)
bun run dev:web # Frontend only (port 5173)Regenerating frontend API types (requires server to be running at port 3090):
bun run dev:server # must be running first
bun --filter @archon/web generate:typesOptional: Use PostgreSQL instead of SQLite by setting DATABASE_URL in .env:
docker-compose --profile with-db up -d postgres
Set DATABASE_URL=postgresql://postgres:postgres@localhost:5432/remote_coding_agent in .env
Testing
bun run test # Run all tests (per-package, isolated processes)
bun test --watch # Watch mode (single package)
bun test packages/core/src/handlers/command-handler.test.ts # Single fileTest isolation (mock.module pollution): Bun's mock.module() permanently replaces modules in the process-wide cache β mock.restore() does NOT undo it (oven-sh/bun#7823). To prevent cross-file pollution, packages that have conflicting mock.module() calls split their tests into separate bun test invocations: @archon/core (7 batches), @archon/workflows (5), @archon/adapters (3), @archon/isolation (3). See each package's package.json for the exact splits.
Do NOT run bun test from the repo root β it discovers all test files across all packages and runs them in one process, causing ~135 mock pollution failures. Always use bun run test (which uses bun --filter '*' test for per-package isolation).
Type Checking & Linting
bun run type-check
bun run lint
bun run lint:fix
bun run format
bun run format:checkPre-PR Validation
Always run before creating a pull request:
bun run validateThis runs check:bundled, type-check, lint, format check, and tests. All five must pass for CI to succeed.
ESLint Guidelines
Zero-tolerance policy: CI enforces --max-warnings 0. No warnings allowed.
When to use inline disable comments (// eslint-disable-next-line):
- Almost never - fix the issue instead
- Only acceptable when:
1. External SDK types are incorrect (document which SDK and why)
2. Intentional type assertion after validation (must include comment explaining the validation)
Never acceptable:
- Disabling no-explicit-any without justification
- Disabling rules to "make CI pass"
- Bulk disabling at file level (/ eslint-disable /)
Database
Auto-Detection (SQLite is the default β zero setup):
- Without DATABASE_URL: Uses SQLite at ~/.archon/archon.db (auto-initialized, recommended for most users)
- With DATABASE_URL set: Uses PostgreSQL (optional, for cloud/advanced deployments)
PostgreSQL only: Run SQL migrations (manual)
psql $DATABASE_URL < migrations/000_combined.sqlCLI (Command Line)
Run workflows directly from the command line without needing the server. Workflow and isolation commands require running from within a git repository (subdirectories work - resolves to repo root).
List available workflows (requires git repo)
bun run cli workflow listMachine-readable JSON output
bun run cli workflow list --jsonRun a workflow
bun run cli workflow run assist "What does the orchestrator do?"Run in a specific directory
bun run cli workflow run plan --cwd /path/to/repo "Add dark mode"Default: auto-creates worktree with generated branch name (isolation by default)
bun run cli workflow run implement "Add auth"Explicit branch name for the worktree
bun run cli workflow run implement --branch feature-auth "Add auth"Opt out of isolation (run in live checkout)
bun run cli workflow run quick-fix --no-worktree "Fix typo"Show running workflows
bun run cli workflow statusResume a failed workflow (re-runs, skipping completed nodes)
bun run cli workflow resume <run-id>Discard a non-terminal run
bun run cli workflow abandon <run-id>Delete old workflow run records (default: 7 days)
bun run cli workflow cleanup
bun run cli workflow cleanup 30 # Custom daysEmit a workflow event (used inside workflow loop prompts)
bun run cli workflow event emit --run-id <uuid> --type <event-type> [--data <json>]List active worktrees/environments
bun run cli isolation listClean up stale environments (default: 7 days)
bun run cli isolation cleanup
bun run cli isolation cleanup 14 # Custom daysClean up environments with branches merged into main (also deletes remote branches)
bun run cli isolation cleanup --mergedAlso remove environments with closed (abandoned) PRs
bun run cli isolation cleanup --merged --include-closedValidate workflow definitions and their referenced resources
bun run cli validate workflows # All workflows
bun run cli validate workflows my-workflow # Single workflow
bun run cli validate workflows my-workflow --json # Machine-readable outputValidate command files
bun run cli validate commands # All commands
bun run cli validate commands my-command # Single commandComplete branch lifecycle (remove worktree + local/remote branches)
bun run cli complete <branch-name>
bun run cli complete <branch-name> --force # Skip uncommitted-changes checkStart the web UI server (compiled binary only, downloads web UI on first run)
bun run cli serve
bun run cli serve --port 4000
bun run cli serve --download-only # Download without startingShow version
bun run cli versionArchitecture
Directory Structure
Monorepo Layout (Bun Workspaces):
packages/
βββ cli/ # @archon/cli - Command-line interface
β βββ src/
β βββ adapters/ # CLI adapter (stdout output)
β βββ commands/ # CLI command implementations
β βββ cli.ts # CLI entry point
βββ providers/ # @archon/providers - AI agent providers (SDK deps live here)
β βββ src/
β βββ types.ts # Contract layer (IAgentProvider, SendQueryOptions, MessageChunk β ZERO SDK deps)
β βββ registry.ts # Typed provider registry (ProviderRegistration records)
β βββ errors.ts # UnknownProviderError
β βββ claude/ # ClaudeProvider + parseClaudeConfig + MCP/hooks/skills translation
β βββ codex/ # CodexProvider + parseCodexConfig + binary-resolver
β βββ index.ts # Package exports
βββ core/ # @archon/core - Shared business logic
β βββ src/
β βββ config/ # YAML config loading
β βββ db/ # Database connection, queries
β βββ handlers/ # Command handler (slash commands)
β βββ orchestrator/ # AI conversation management
β βββ services/ # Background services (cleanup)
β βββ state/ # Session state machine
β βββ types/ # TypeScript types and interfaces
β βββ utils/ # Shared utilities
β βββ workflows/ # Store adapter (createWorkflowStore) bridging core DB β IWorkflowStore
β βββ index.ts # Package exports
βββ workflows/ # @archon/workflows - Workflow engine (depends on @archon/git + @archon/paths)
β βββ src/
β βββ schemas/ # Zod schemas for engine types
β βββ loader.ts # YAML parsing + validation (parseWorkflow)
β βββ workflow-discovery.ts # Workflow filesystem discovery (discoverWorkflows, discoverWorkflowsWithConfig)
β βββ executor-shared.ts # Shared executor infrastructure (error classification, variable substitution)
β βββ router.ts # Prompt building + invocation parsing
β βββ executor.ts # Workflow execution orchestrator (executeWorkflow)
β βββ dag-executor.ts # DAG-specific execution logic
β βββ store.ts # IWorkflowStore interface (database abstraction)
β βββ deps.ts # WorkflowDeps injection types (IWorkflowPlatform, imports from @archon/providers/types)
β βββ event-emitter.ts # Workflow observability events
β βββ logger.ts # JSONL file logger
β βββ validator.ts # Resource validation (command files, MCP configs, skill dirs)
β βββ defaults/ # Bundled default commands and workflows
β βββ utils/ # Variable substitution, tool formatting, execution utilities
βββ git/ # @archon/git - Git operations (no @archon/core dep)
β βββ src/
β βββ branch.ts # Branch operations (checkout, merge detection, etc.)
β βββ exec.ts # execFileAsync and mkdirAsync wrappers
β βββ repo.ts # Repository operations (clone, sync, remote URL)
β βββ types.ts # Branded types (RepoPath, BranchName, etc.)
β βββ worktree.ts # Worktree operations (create, remove, list)
β βββ index.ts # Package exports
βββ isolation/ # @archon/isolation - Worktree isolation (depends on @archon/git + @archon/paths)
β βββ src/
β βββ types.ts # Isolation types and interfaces
β βββ errors.ts # Error classifiers (classifyIsolationError, IsolationBlockedError)
β βββ factory.ts # Provider factory (getIsolationProvider, configureIsolation)
β βββ resolver.ts # IsolationResolver (request β environment resolution)
β βββ store.ts # IIsolationStore interface
β βββ worktree-copy.ts # File copy utilities for worktrees
β βββ providers/
β β βββ worktree.ts # WorktreeProvider implementation
β βββ index.ts # Package exports
βββ paths/ # @archon/paths - Path resolution and logger (zero @archon/* deps)
β βββ src/
β βββ archon-paths.ts # Archon directory path utilities
β βββ logger.ts # Pino logger factory
β βββ index.ts # Package exports
βββ adapters/ # @archon/adapters - Platform adapters (Slack, Telegram, GitHub, Discord)
β βββ src/
β βββ chat/ # Chat platform adapters (Slack, Telegram)
β βββ forge/ # Forge adapters (GitHub)
β βββ community/ # Community adapters (Discord)
β βββ utils/ # Shared adapter utilities (message splitting)
β βββ index.ts # Package exports
βββ server/ # @archon/server - HTTP server + Web adapter
β βββ src/
β βββ adapters/ # Web platform adapter (SSE streaming)
β βββ routes/ # API routes (REST + SSE)
β βββ index.ts # Hono server entry point
βββ web/ # @archon/web - React frontend (Web UI)
βββ src/
βββ components/ # React components (chat, layout, projects, ui, workflows)
βββ hooks/ # Custom hooks (useSSE, etc.)
βββ lib/ # API client, types, utilities
βββ stores/ # Zustand stores (workflow-store)
βββ routes/ # Route pages (ChatPage, WorkflowsPage, WorkflowBuilderPage, etc.)
βββ App.tsx # Router + layoutImport Patterns:
IMPORTANT: Always use typed imports - never use generic import * for the main package.
// β
CORRECT: Use import type for type-only imports
import type { IPlatformAdapter, Conversation, MergedConfig } from '@archon/core';// β
CORRECT: Use specific named imports for values
import { handleMessage, ConversationLockManager, pool } from '@archon/core';
// β
CORRECT: Namespace imports for submodules with many exports
import * as conversationDb from '@archon/core/db/conversations';
import * as git from '@archon/git';
// β
CORRECT: Import workflow engine types/functions from direct subpaths
import type { WorkflowDeps } from '@archon/workflows/deps';
import type { IWorkflowStore } from '@archon/workflows/store';
import type { WorkflowDefinition } from '@archon/workflows/schemas/workflow';
import { executeWorkflow } from '@archon/workflows/executor';
import { discoverWorkflowsWithConfig } from '@archon/workflows/workflow-discovery';
import { findWorkflow } from '@archon/workflows/router';
// β WRONG: Never use generic import for main package
import * as core from '@archon/core'; // Don't do this
// β WRONG: In @archon/web, never import from @archon/workflows (it's a server package)
import type { DagNode } from '@archon/workflows/schemas/dag-node'; // Don't do this from @archon/web
// β
CORRECT: Use re-exports from api.ts (derived from generated OpenAPI spec)
import type { DagNode, WorkflowDefinition } from '@/lib/api';
Database Schema
8 Tables (all prefixed with remote_agent_):
1. codebases - Repository metadata and commands (JSONB)
2. conversations - Track platform conversations with titles and soft-delete support
3. sessions - Track AI SDK sessions with resume capability
4. isolation_environments - Git worktree isolation tracking
5. workflow_runs - Workflow execution tracking and state
6. workflow_events - Step-level workflow event log (step transitions, artifacts, errors)
7. messages - Conversation message history with tool call metadata (JSONB)
8. codebase_env_vars - Per-project env vars injected into project-scoped execution surfaces (Claude, Codex, bash/script nodes, and direct chat when codebase-scoped), managed via Web UI or env: in config
Key Patterns:
- Conversation ID format: Platform-specific (thread_ts, chat_id, user/repo#123)
- One active session per conversation
- Codebase commands stored in filesystem, paths in codebases.commands JSONB
Session Transitions:
- Sessions are immutable - transitions create new linked sessions
- Each transition has explicit TransitionTrigger reason (first-message, plan-to-execute, reset-requested, etc.)
- Audit trail: parent_session_id links to previous session, transition_reason records why
- Only planβexecute creates new session immediately; other triggers deactivate current session
Architecture Layers
Package Split:
- @archon/paths: Path resolution utilities, Pino logger factory, web dist cache path (getWebDistDir), CWD env stripper (stripCwdEnv, strip-cwd-env-boot) (no @archon/* deps; pino and dotenv are allowed external deps)
- @archon/git: Git operations - worktrees, branches, repos, exec wrappers (depends only on @archon/paths)
- @archon/providers: AI agent providers (Claude, Codex) β owns SDK deps, IAgentProvider interface, sendQuery() contract, and provider-specific option translation. @archon/providers/types is the contract subpath (zero SDK deps, zero runtime side effects) that @archon/workflows imports from. Providers receive raw nodeConfig + assistantConfig and translate to SDK-specific options internally.
- @archon/isolation: Worktree isolation types, providers, resolver, error classifiers (depends only on @archon/git + @archon/paths)
- @archon/workflows: Workflow engine - loader, router, executor, DAG, logger, bundled defaults (depends only on @archon/git + @archon/paths + @archon/providers/types + @hono/zod-openapi + zod; DB/AI/config injected via WorkflowDeps)
- @archon/cli: Command-line interface for running workflows and starting the web UI server (depends on @archon/server + @archon/adapters for the serve command)
- @archon/core: Business logic, database, orchestration (depends on @archon/providers for AI; provides createWorkflowStore() adapter bridging core DB β IWorkflowStore)
- @archon/adapters: Platform adapters for Slack, Telegram, GitHub, Discord (depends on @archon/core)
- @archon/server: OpenAPIHono HTTP server (Zod + OpenAPI spec generation via @hono/zod-openapi), Web adapter (SSE), API routes, Web UI static serving (depends on @archon/adapters)
- @archon/web: React frontend (Vite + Tailwind v4 + shadcn/ui + Zustand), SSE streaming to server. WorkflowRunStatus, WorkflowDefinition, and DagNode are all derived from src/lib/api.generated.d.ts (generated from the OpenAPI spec via bun generate:types; never import from @archon/workflows)
1. Platform Adapters
- Implement IPlatformAdapter interface
- Handle platform-specific message formats
- Web (packages/server/src/adapters/web/): Server-Sent Events (SSE) streaming, conversation ID = user-provided string
- Slack (packages/adapters/src/chat/slack/): SDK with polling (not webhooks), conversation ID = thread_ts
- Telegram (packages/adapters/src/chat/telegram/): Bot API with polling, conversation ID = chat_id
- GitHub (packages/adapters/src/forge/github/): Webhooks + GitHub CLI, conversation ID = owner/repo#number
- Discord (packages/adapters/src/community/chat/discord/): discord.js WebSocket, conversation ID = channel ID
Adapter Authorization Pattern:
- Auth checks happen INSIDE adapters (encapsulation, consistency)
- Auth utilities co-located with each adapter (e.g., packages/adapters/src/chat/slack/auth.ts)
- Parse whitelist from env var in constructor (e.g., TELEGRAM_ALLOWED_USER_IDS)
- Check authorization in message handler (before calling onMessage callback)
- Silent rejection for unauthorized users (no error response)
- Log unauthorized attempts with masked user IDs for privacy
- Adapters expose onMessage(handler) callback; errors handled by caller
2. Command Handler (packages/core/src/handlers/)
- Process slash commands (deterministic, no AI)
- The orchestrator treats only these top-level commands as deterministic: /help, /status, /reset, /workflow, /register-project, /update-project, /remove-project, /commands, /init, /worktree
- /workflow handles subcommands like list, run, status, cancel, resume, abandon, approve, reject
- Update database, perform operations, return responses
3. Orchestrator (packages/core/src/orchestrator/)
- Manage AI conversations
- Load conversation + codebase context from database
- Variable substitution: $1, $2, $3, $ARGUMENTS
- Session management: Create new or resume existing
- Stream AI responses to platform
4. AI Agent Providers (packages/providers/src/)
- Implement IAgentProvider interface
- ClaudeProvider: @anthropic-ai/claude-agent-sdk
- CodexProvider: @openai/codex-sdk
- Streaming: for await (const event of events) { await platform.send(event) }
Configuration
Environment Variables:
see .env.example
see .archon/config.yaml setup as needed
Assistant Defaults:
The system supports configuring default models and options per assistant in .archon/config.yaml:
assistants:
claude:
model: sonnet # or 'opus', 'haiku', 'claude-*', 'inherit'
settingSources: # Controls which CLAUDE.md files Claude SDK loads
- project # Default: only project-level CLAUDE.md
- user # Optional: also load ~/.claude/CLAUDE.md
claudeBinaryPath: /absolute/path/to/claude # Optional: Claude Code executable.
# Native binary (curl installer at
# ~/.local/bin/claude) or npm cli.js.
# Required in compiled binaries if
# CLAUDE_BIN_PATH env var is not set.
codex:
model: gpt-5.3-codex
modelReasoningEffort: medium # 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'
webSearchMode: live # 'disabled' | 'cached' | 'live'
additionalDirectories:
- /absolute/path/to/other/repo
codexBinaryPath: /usr/local/bin/codex # Optional: custom Codex CLI binary pathdocs:
path: docs # Optional: default is docs/
Configuration Priority:
1. Workflow-level options (in YAML model, modelReasoningEffort, etc.)
2. Config file defaults (.archon/config.yaml assistants.*)
3. SDK defaults
Model Validation:
- Workflows are validated at load time for provider/model compatibility
- Claude models: sonnet, opus, haiku, claude-*, inherit
- Codex models: Any model except Claude-specific aliases
- Invalid combinations fail workflow loading with clear error messages
Running the App in Worktrees
Agents working in worktrees can run the app for self-testing (make changes β run app β test via curl β fix). Ports are automatically allocated to avoid conflicts:
Run in worktree (port auto-allocated based on path)
bun dev &
[Hono] Worktree detected (/path/to/worktree)
[Hono] Auto-allocated port: 3637 (base: 3090, offset: +547)
Test via web API (production path)
1) Create a conversation
curl -X POST http://localhost:3637/api/conversations \
-H "Content-Type: application/json" \
-d '{}'2) Send a message
curl -X POST http://localhost:3637/api/conversations/<conversationId>/message \
-H "Content-Type: application/json" \
-d '{"message":"/status"}'3) Fetch messages (polling)
curl http://localhost:3637/api/conversations/<conversationId>/messagesNote: SSE streaming is available at /api/stream/<conversationId>
Port Allocation:
- Worktrees: Automatic unique port (3190-4089 range, hash-based on path)
- Main repo: Default 3090
- Override: PORT=4000 bun dev (works in both contexts)
- Same worktree always gets same port (deterministic)
Important:
- Use the web API routes for manual validation (avoid running multiple platform adapters)
- Database is shared (same conversations/codebases available)
- Kill the server when done: pkill -f "bun.*dev" or use the specific port
Archon Directory Structure
User-level (~/.archon/):
~/.archon/
βββ workspaces/owner/repo/ # Project-centric layout
β βββ source/ # Cloned repo or symlink β local path
β βββ worktrees/ # Git worktrees for this project
β βββ artifacts/ # Workflow artifacts (NEVER in git)
β β βββ runs/{id}/ # Per-run artifacts ($ARTIFACTS_DIR)
β β βββ uploads/{convId}/ # Web UI file uploads (ephemeral)
β βββ logs/ # Workflow execution logs
βββ vendor/codex/ # Codex native binary (binary builds, user-placed)
βββ web-dist/<version>/ # Cached web UI dist (archon serve, binary only)
βββ update-check.json # Update check cache (binary builds, 24h TTL)
βββ archon.db # SQLite database (when DATABASE_URL not set)
βββ config.yaml # Global configuration (non-secrets)Repo-level (.archon/ in any repository):
.archon/
βββ commands/ # Custom commands
βββ workflows/ # Workflow definitions (YAML files)
βββ scripts/ # Named scripts for script: nodes (.ts/.js for bun, .py for uv)
βββ config.yaml # Repo-specific configuration- ARCHON_HOME - Override the base directory (default: ~/.archon)
- Docker: Paths automatically set to /.archon/
Development Guidelines
When Creating New Features
Quick reference:
- Platform Adapters: Implement IPlatformAdapter, handle auth, polling/webhooks
- AI Providers: Implement IAgentProvider, session management, streaming
- Slash Commands: Add to command-handler.ts, update database, no AI
- Database Operations: Use IDatabase interface (supports PostgreSQL and SQLite via adapters)
SDK Type Patterns
When working with external SDKs (Claude Agent SDK, Codex SDK), prefer importing and using SDK types directly:
// β
CORRECT - Import SDK types directly
import { query, type Options } from '@anthropic-ai/claude-agent-sdk';const options: Options = {
cwd,
permissionMode: 'bypassPermissions',
// ...
};
// Use type assertions for SDK response structures
const message = msg as { message: { content: ContentBlock[] } };
// β AVOID - Defining duplicate types
interface MyQueryOptions { // Don't duplicate SDK types
cwd: string;
// ...
}
const options: MyQueryOptions = { ... };
query({ prompt, options: options as any }); // Avoid 'as any'This ensures type compatibility with SDK updates and eliminates as any casts.
Testing
Unit Tests:
- Test pure functions (variable substitution, command parsing)
- Mock external dependencies (database, AI SDKs, platform APIs)
Integration Tests:
- Test database operations with test database
- Test end-to-end flows (mock platforms/AI but use real orchestrator)
- Clean up test data after each test
Mock isolation rules (IMPORTANT):
- Bun's mock.module() is process-global and irreversible β mock.restore() does NOT undo it
- Do NOT add afterAll(() => mock.restore()) for mock.module() cleanup β it has no effect
- Use spyOn() for internal modules that other test files import directly (e.g., spyOn(git, 'checkout')) β spy.mockRestore() DOES work for spies
- Never mock.module() a module path that another test file also mock.module()s with a different implementation
- When adding a new test file with mock.module(), ensure its package.json test script runs it in a separate bun test invocation from any conflicting files
Manual Validation: Use the web API (curl) or CLI commands directly for end-to-end testing of new features.
Logging
Structured logging with Pino (packages/paths/src/logger.ts):
import { createLogger } from '@archon/paths';const log = createLogger('orchestrator');
// Event naming: {domain}.{action}_{state}
// Standard states: _started, _completed, _failed, _validated, _rejected
async function createSession(conversationId: string, codebaseId: string) {
log.info({ conversationId, codebaseId }, 'session.create_started');
try {
const session = await doCreate();
log.info({ conversationId, codebaseId, sessionId: session.id }, 'session.create_completed');
return session;
} catch (e) {
const err = e as Error;
log.error(
{ conversationId, error: err.message, errorType: err.constructor.name, err },
'session.create_failed',
);
throw err;
}
}
Event naming rules:
- Format: {domain}.{action}_{state} β e.g. workflow.step_started, isolation.create_failed
- Avoid generic events like processing or handling
- Always pair _started with _completed or _failed
- Include context: IDs, durations, error details
Log Levels: fatal > error > warn > info (default) > debug > trace
Verbosity:
- CLI: archon --quiet (errors only) β suppresses Pino logs and workflow progress output
- CLI: archon --verbose (debug) β enables debug Pino logs and tool-level workflow progress events
- Server: LOG_LEVEL=debug bun run start
Never log: API keys or tokens (mask: token.slice(0, 8) + '...'), user message content, PII.
Command System
Variable Substitution:
- $1, $2, $3 - Positional arguments
- $ARGUMENTS - All arguments as single string
- $ARTIFACTS_DIR - External artifacts directory for the current workflow run (pre-created by executor)
- $WORKFLOW_ID - The workflow run ID
- $BASE_BRANCH - Base branch; auto-detected from git when worktree.baseBranch is not set; fails only if referenced in a prompt and auto-detection also fails
- $DOCS_DIR - Documentation directory path; configured via docs.path in .archon/config.yaml. Defaults to docs/. Never throws.
- $LOOP_USER_INPUT - User feedback provided via /workflow approve <id> <text> at an interactive loop gate. Only populated on the first iteration of a resumed interactive loop; empty string on all other iterations.
- $REJECTION_REASON - Reviewer feedback provided via /workflow reject <id> <reason> at an approval gate. Only populated in on_reject prompts; empty string elsewhere.
Command Types:
1. Codebase Commands (per-repo):
- Stored in .archon/commands/ (plain text/markdown)
- Discovered from the repository .archon/commands/ directory
- Surfaced via GET /api/commands for the workflow builder and invoked by workflow command: nodes
2. Workflows (YAML-based):
- Stored in .archon/workflows/ (searched recursively)
- Multi-step AI execution chains, discovered at runtime
- nodes: (DAG format): Nodes with explicit depends_on edges; independent nodes in the same topological layer run concurrently. Node types: command: (named command file), prompt: (inline prompt), bash: (shell script, stdout captured as $nodeId.output, no AI, receives managed per-project env vars in its subprocess environment when configured), loop: (iterative AI prompt until completion signal), approval: (human gate; pauses until user approves or rejects; capture_response: true stores the user's comment as $<node-id>.output for downstream nodes, default false), script: (inline TypeScript/Python or named script from .archon/scripts/, runs via bun or uv, stdout captured as $nodeId.output, no AI, receives managed per-project env vars in its subprocess environment when configured, supports deps: for dependency installation and timeout: in ms, requires runtime: bun or runtime: uv) . Supports when: conditions, trigger_rule join semantics, $nodeId.output substitution, output_format for structured JSON output (Claude and Codex), allowed_tools/denied_tools for per-node tool restrictions (Claude only), hooks for per-node SDK hook callbacks (Claude only), mcp for per-node MCP server config files (Claude only, env vars expanded at execution time), and skills for per-node skill preloading via AgentDefinition wrapping (Claude only), and effort/thinking/maxBudgetUsd/systemPrompt/fallbackModel/betas/sandbox for Claude SDK advanced options (Claude only, also settable at workflow level)
- Provider inherited from .archon/config.yaml unless explicitly set; per-node provider and model overrides supported
- Model and options can be set per workflow or inherited from config defaults
- interactive: true at the workflow level forces foreground execution on web (required for approval-gate workflows in the web UI)
- Model validation ensures provider/model compatibility at load time
- Commands: /workflow list, /workflow reload, /workflow status, /workflow cancel, /workflow resume <id> (re-runs failed workflow, skipping completed nodes), /workflow abandon <id>, /workflow cleanup [days] (CLI only β deletes old run records)
- Resilient loading: One broken YAML doesn't abort discovery; errors shown in /workflow list
- resolveWorkflowName() (in router.ts) resolves workflow names via a 4-tier fallback β exact, case-insensitive, suffix (-name), substring β with ambiguity detection; used by both the CLI and all chat platforms
- Router fallback: if no /invoke-workflow is produced, falls back to archon-assist (with "Routing unclear" notice); raw AI response returned only when archon-assist is unavailable
- Claude routing calls use tools: [] to prevent tool use at the API level; Codex tool bypass is detected and triggers the same fallback
Defaults:
- Bundled in .archon/commands/defaults/ and .archon/workflows/defaults/
- Binary builds: Embedded at compile time (no filesystem access needed) via packages/workflows/src/defaults/bundled-defaults.generated.ts
- Source builds: Loaded from filesystem at runtime
- Merged with repo-specific commands/workflows (repo overrides defaults by name)
- Opt-out: Set defaults.loadDefaultCommands: false or defaults.loadDefaultWorkflows: false in .archon/config.yaml
- After adding, removing, or editing a default file, run bun run generate:bundled to refresh the embedded bundle. bun run validate (and CI) run check:bundled and will fail loudly if the generated file is stale.
Global workflows (user-level, applies to every project):
- Path: ~/.archon/.archon/workflows/ (or $ARCHON_HOME/.archon/workflows/)
- Load priority: bundled < global < repo-specific (repo overrides global by filename)
- See the docs site at packages/docs-web/ for details
Error Handling
Database Errors:
// INSERT operations
try {
await db.query('INSERT INTO conversations ...', params);
} catch (error) {
log.error({ err: error, params }, 'db_insert_failed');
throw new Error('Failed to create conversation');
}// UPDATE operations - verify rowCount to catch missing records
try {
await db.updateConversation(conversationId, { codebase_id: codebaseId });
} catch (error) {
// updateConversation throws if no rows matched (conversation not found)
log.error({ err: error, conversationId }, 'db_update_failed');
throw error; // Re-throw to surface the issue
}
Git Operation Errors (don't fail silently):
// When isolation environment creation fails:
try {
// ... isolation creation logic ...
} catch (error) {
const err = error as Error;
const userMessage = classifyIsolationError(err);
log.error({ err, codebaseId, codebaseName }, 'isolation_creation_failed');
await platform.sendMessage(conversationId, userMessage);
}Pattern: Use classifyIsolationError() (from @archon/isolation) to map git errors (permission denied, timeout, no space, not a git repo) to user-friendly messages. Always log the raw error for debugging and send a classified message to the user.
API Endpoints
Web UI REST API (packages/server/src/routes/api.ts):
Workflow Management:
- GET /api/workflows - List available workflows; optional ?cwd=; returns { workflows: [...], errors?: [...] }
- POST /api/workflows/validate - Validate a workflow definition in-memory (no save); body: { definition: object }; returns { valid: boolean, errors?: string[] }
- GET /api/workflows/:name - Fetch a single workflow by name; optional ?cwd= query param; returns { workflow, filename, source: 'project' | 'bundled' }
- PUT /api/workflows/:name - Save (create or update) a workflow YAML; body: { definition: object }; validates before writing; requires ?cwd= or registered codebase
- DELETE /api/workflows/:name - Delete a user-defined workflow; bundled defaults cannot be deleted
Workflow Run Lifecycle:
- POST /api/workflows/runs/{runId}/resume - Mark a failed run as ready for auto-resume on next invocation
- POST /api/workflows/runs/{runId}/abandon - Abandon a non-terminal run (marks as cancelled)
- DELETE /api/workflows/runs/{runId} - Delete a terminal workflow run and its events
Codebases:
- GET /api/codebases / GET /api/codebases/:id - List / fetch codebases
- POST /api/codebases - Register a codebase (clone or local path)
- DELETE /api/codebases/:id - Delete a codebase and clean up resources
- GET /api/codebases/:id/env - List env var keys for a codebase (never returns values)
- PUT /api/codebases/:id/env / DELETE /api/codebases/:id/env/:key - Upsert / delete a single codebase env var
- GET /api/codebases/:id/environments - List tracked isolation environments for a codebase
Artifact Files:
- GET /api/artifacts/:runId/* - Serve a workflow artifact file by run ID and relative path; returns text/markdown for .md files, text/plain otherwise; 400 on path traversal (..), 404 if run or file not found
Command Listing:
- GET /api/commands - List available command names (bundled + project-defined); optional ?cwd=; returns { commands: [{ name, source: 'bundled' | 'project' }] }
Providers:
- GET /api/providers - List registered AI providers; returns { providers: [{ id, displayName, capabilities, builtIn }] }
System:
- GET /api/health - Health check with adapter/system status
- GET /api/update-check - Check for available updates; returns { updateAvailable, currentVersion, latestVersion, releaseUrl }; skips GitHub API call for non-binary builds
OpenAPI Spec:
- GET /api/openapi.json - Generated OpenAPI 3.0 spec for all Zod-validated routes
Webhooks:
- POST /webhooks/github - GitHub webhook events
- Signature verification required (HMAC SHA-256)
- Return 200 immediately, process async
Security:
- Verify webhook signatures (GitHub: X-Hub-Signature-256)
- Use c.req.text() for raw webhook body (signature verification)
- Never log or expose tokens in responses
@Mention Detection:
- Parse @archon in issue/PR comments only (not descriptions)
- Events: issue_comment only
- Note: Descriptions often contain example commands or documentation - these are NOT command invocations (see #96)
README.md
<p align="center">
<img src="assets/logo.png" alt="Archon" width="160" />
</p>
<h1 align="center">Archon</h1>
<p align="center">
The first open-source harness builder for AI coding. Make AI coding deterministic and repeatable.
</p>
<p align="center">
<a href="https://trendshift.io/repositories/13964" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13964" alt="coleam00%2FArchon | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
<p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
<a href="https://github.com/coleam00/Archon/actions/workflows/test.yml"><img src="https://github.com/coleam00/Archon/actions/workflows/test.yml/badge.svg" alt="CI" /></a>
<a href="https://archon.diy"><img src="https://img.shields.io/badge/docs-archon.diy-blue" alt="Docs" /></a>
</p>
---
Archon is a workflow engine for AI coding agents. Define your development processes as YAML workflows - planning, implementation, validation, code review, PR creation - and run them reliably across all your projects.
Like what Dockerfiles did for infrastructure and GitHub Actions did for CI/CD - Archon does for AI coding workflows. Think n8n, but for software development.
Why Archon?
When you ask an AI agent to "fix this bug", what happens depends on the model's mood. It might skip planning. It might forget to run tests. It might write a PR description that ignores your template. Every run is different.
Archon fixes this. Encode your development process as a workflow. The workflow defines the phases, validation gates, and artifacts. The AI fills in the intelligence at each step, but the structure is deterministic and owned by you.
- Repeatable - Same workflow, same sequence, every time. Plan, implement, validate, review, PR.
- Isolated - Every workflow run gets its own git worktree. Run 5 fixes in parallel with no conflicts.
- Fire and forget - Kick off a workflow, go do other work. Come back to a finished PR with review comments.
- Composable - Mix deterministic nodes (bash scripts, tests, git ops) with AI nodes (planning, code generation, review). The AI only runs where it adds value.
- Portable - Define workflows once in .archon/workflows/, commit them to your repo. They work the same from CLI, Web UI, Slack, Telegram, or GitHub.
What It Looks Like
Here's an example of an Archon workflow that plans, implements in a loop until tests pass, gets your approval, then creates the PR:
.archon/workflows/build-feature.yaml
nodes:
- id: plan
prompt: "Explore the codebase and create an implementation plan" - id: implement
depends_on: [plan]
loop: # AI loop - iterate until done
prompt: "Read the plan. Implement the next task. Run validation."
until: ALL_TASKS_COMPLETE
fresh_context: true # Fresh session each iteration
- id: run-tests
depends_on: [implement]
bash: "bun run validate" # Deterministic - no AI
- id: review
depends_on: [run-tests]
prompt: "Review all changes against the plan. Fix any issues."
- id: approve
depends_on: [review]
loop: # Human approval gate
prompt: "Present the changes for review. Address any feedback."
until: APPROVED
interactive: true # Pauses and waits for human input
- id: create-pr
depends_on: [approve]
prompt: "Push changes and create a pull request"
Tell your coding agent what you want, and Archon handles the rest:
You: Use archon to add dark mode to the settings pageAgent: I'll run the archon-idea-to-pr workflow for this.
β Creating isolated worktree on branch archon/task-dark-mode...
β Planning...
β Implementing (task 1/4)...
β Implementing (task 2/4)...
β Tests failing - iterating...
β Tests passing after 2 iterations
β Code review complete - 0 issues
β PR ready: https://github.com/you/project/pull/47
Previous Version
Looking for the original Python-based Archon (task management + RAG)? It's fully preserved on the archive/v1-task-management-rag branch.
Getting Started
Most users should start with the Full Setup - it walks you through credentials, installs the Archon skill into your projects, and gives you the web dashboard.
> Already have Claude Code and just want the CLI? Jump to the Quick Install.
Full Setup (5 minutes)
Clone the repo and use the guided setup wizard. This configures credentials, platform integrations, and copies the Archon skill into your target projects.
<details>
<summary><b>Prerequisites</b> - Bun, Claude Code, and the GitHub CLI</summary>
Bun - bun.sh
macOS/Linux
curl -fsSL https://bun.sh/install | bashWindows (PowerShell)
irm bun.sh/install.ps1 | iexGitHub CLI - cli.github.com
macOS
brew install ghWindows (via winget)
winget install GitHub.cliLinux (Debian/Ubuntu)
sudo apt install ghClaude Code - claude.ai/code
macOS/Linux/WSL
curl -fsSL https://claude.ai/install.sh | bashWindows (PowerShell)
irm https://claude.ai/install.ps1 | iex</details>
git clone https://github.com/coleam00/Archon
cd Archon
bun install
claudeThen say: "Set up Archon"
The setup wizard walks you through everything: CLI installation, authentication, platform selection, and copies the Archon skill to your target repo.
Quick Install (30 seconds)
Already have Claude Code set up? Install the standalone CLI binary and skip the wizard.
macOS / Linux
curl -fsSL https://archon.diy/install | bashWindows (PowerShell)
irm https://archon.diy/install.ps1 | iexHomebrew
brew install coleam00/archon/archonCompiled binaries need a CLAUDE_BIN_PATH. The quick-install binariesdon't bundle Claude Code. Install it separately, then point Archon at it:
> ``bash# macOS / Linux / WSL
curl -fsSL https://claude.ai/install.sh | bash
export CLAUDE_BIN_PATH="$HOME/.local/bin/claude"
> # Windows (PowerShell)
irm https://claude.ai/install.ps1 | iex
$env:CLAUDE_BIN_PATH = "$env:USERPROFILE\.local\bin\claude.exe"
`> Or set assistants.claude.claudeBinaryPathin~/.archon/config.yaml.
The Docker image ships Claude Code pre-installed. See AI Assistants β Binary path configuration for details.
Start Using Archon
Once you've completed either setup path, go to your project and start working:
cd /path/to/your/project
claudeUse archon to fix issue #42What archon workflows do I have? When would I use each one?The coding agent handles workflow selection, branch naming, and worktree isolation for you. Projects are registered automatically the first time they're used.
Important: Always run Claude Code from your target repo, not from the Archon repo. The setup wizard copies the Archon skill into your project so it works from there.
Web UI
Archon includes a web dashboard for chatting with your coding agent, running workflows, and monitoring activity. Binary installs: run archon serve to download and start the web UI in one step. From source: ask your coding agent to run the frontend from the Archon repo, or run bun run dev from the repo root yourself.
Register a project by clicking + next to "Project" in the chat sidebar - enter a GitHub URL or local path. Then start a conversation, invoke workflows, and watch progress in real time.
Key pages:
- Chat - Conversation interface with real-time streaming and tool call visualization
- Dashboard - Mission Control for monitoring running workflows, with filterable history by project, status, and date
- Workflow Builder - Visual drag-and-drop editor for creating DAG workflows with loop nodes
- Workflow Execution - Step-by-step progress view for any running or completed workflow
Monitoring hub: The sidebar shows conversations from all platforms - not just the web. Workflows kicked off from the CLI, messages from Slack or Telegram, GitHub issue interactions - everything appears in one place.
See the Web UI Guide for full documentation.
What Can You Automate?
Archon ships with workflows for common development tasks:
| Workflow | What it does |
|----------|-------------|
| archon-assist | General Q&A, debugging, exploration - full Claude Code agent with all tools |archon-fix-github-issue
| | Classify issue β investigate/plan β implement β validate β PR β smart review β self-fix |archon-idea-to-pr
| | Feature idea β plan β implement β validate β PR β 5 parallel reviews β self-fix |archon-plan-to-pr
| | Execute existing plan β implement β validate β PR β review β self-fix |archon-issue-review-full
| | Comprehensive fix + full multi-agent review pipeline for GitHub issues |archon-smart-pr-review
| | Classify PR complexity β run targeted review agents β synthesize findings |archon-comprehensive-pr-review
| | Multi-agent PR review (5 parallel reviewers) with automatic fixes |archon-create-issue
| | Classify problem β gather context β investigate β create GitHub issue |archon-validate-pr
| | Thorough PR validation testing both main and feature branches |archon-resolve-conflicts
| | Detect merge conflicts β analyze both sides β resolve β validate β commit |archon-feature-development
| | Implement feature from plan β validate β create PR |archon-architect
| | Architectural sweep, complexity reduction, codebase health improvement |archon-refactor-safely
| | Safe refactoring with type-check hooks and behavior verification |archon-ralph-dag
| | PRD implementation loop - iterate through stories until done |archon-remotion-generate
| | Generate or modify Remotion video compositions with AI |archon-test-loop-dag
| | Loop node test workflow - iterative counter until completion |archon-piv-loop
| | Guided Plan-Implement-Validate loop with human review between iterations |
Archon ships 17 default workflows - run archon workflow list or describe what you want and the router picks the right one.
Or define your own. Default workflows are great starting points - copy one from .archon/workflows/defaults/ and customize it. Workflows are YAML files in .archon/workflows/, commands are markdown files in .archon/commands/. Same-named files in your repo override the bundled defaults. Commit them - your whole team runs the same process.
See Authoring Workflows and Authoring Commands.
Add a Platform
The Web UI and CLI work out of the box. Optionally connect a chat platform for remote access:
| Platform | Setup time | Guide |
|----------|-----------|-------|
| Telegram | 5 min | Telegram Guide |
| Slack | 15 min | Slack Guide |
| GitHub Webhooks | 15 min | GitHub Guide |
| Discord | 5 min | Discord Guide |
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Platform Adapters (Web UI, CLI, Telegram, Slack, β
β Discord, GitHub) β
ββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Orchestrator β
β (Message Routing & Context Management) β
βββββββββββββββ¬ββββββββββββββββββββββββββββ¬ββββββββββββββββ
β β
βββββββββ΄βββββββββ βββββββββ΄βββββββββ
β β β β
βΌ βΌ βΌ βΌ
βββββββββββββ ββββββββββββββ ββββββββββββββββββββββββββββ
β Command β β Workflow β β AI Assistant Clients β
β Handler β β Executor β β (Claude / Codex) β
β (Slash) β β (YAML) β β β
βββββββββββββ ββββββββββββββ ββββββββββββββββββββββββββββ
β β β
ββββββββββββββββ΄βββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SQLite / PostgreSQL (7 Tables) β
β Codebases β’ Conversations β’ Sessions β’ Workflow Runs β
β Isolation Environments β’ Messages β’ Workflow Events β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββDocumentation
Full documentation is available at archon.diy.
| Topic | Description |
|-------|-------------|
| Getting Started | Setup guide (Web UI or CLI) |
| The Book of Archon | 10-chapter narrative tutorial |
| CLI Reference | Full CLI reference |
| Authoring Workflows | Create custom YAML workflows |
| Authoring Commands | Create reusable AI commands |
| Configuration | All config options, env vars, YAML settings |
| AI Assistants | Claude and Codex setup details |
| Deployment | Docker, VPS, production setup |
| Architecture | System design and internals |
| Troubleshooting | Common issues and fixes |
Telemetry
Archon sends a single anonymous event β workflow_invoked β each time a workflow starts, so maintainers can see which workflows get real usage and prioritize accordingly. No PII, ever.
What's collected: the workflow name, the workflow description (both authored by you in YAML), the platform that triggered it (cli, web, slack, etc.), the Archon version, and a random install UUID stored at ~/.archon/telemetry-id. Nothing else.
What's not collected: your code, prompts, messages, git remotes, file paths, usernames, tokens, AI output, workflow node details β none of it.
Opt out: set any of these in your environment:
ARCHON_TELEMETRY_DISABLED=1
DO_NOT_TRACK=1 # de facto standard honored by Astro, Bun, Prisma, Nuxt, etc.Self-host PostHog or use a different project by setting POSTHOG_API_KEY and POSTHOG_HOST`.
Contributing
Contributions welcome! See the open issues for things to work on.
Please read CONTRIBUTING.md before submitting a pull request.