Dirac — Accurate & Highly Token Efficient
Dirac is a TypeScript coding agent that reduces API costs by 64.8% while achieving better accuracy — using hash-anchored parallel edits, AST manipulation, and a suite of context curation optimizations. Explicitly no MCP. Built on a fork of Cline.
What makes Dirac different
Most coding agents treat file editing as a text manipulation problem: find line N, replace with new content. This breaks the moment the file shifts — even a single inserted line deranges every downstream line number. Dirac takes a different approach: it anchors edits to stable line hashes, uses AST-aware tools for structural code operations, and batches multiple file operations into a single LLM roundtrip. The result is an agent that is measurably more accurate on complex multi-file refactoring tasks and significantly cheaper to run.
The eval results are striking. On 8 real-world refactoring tasks against transformers, vscode, and django repos, Dirac achieved 8/8 correct at an average cost of $0.18 — versus the competition's $0.38–$0.73 range. That is a 2.8x cost reduction with better outcomes.
TerminalBench 2.0 Results
Dirac scored 65.2% on TerminalBench 2.0, beating Google's coding agent at 47.6% and Junie CLI at 64.3%. The eval tests complex, real-world refactoring tasks against public GitHub repos.
Hash-Anchored File Editing
The core innovation in Dirac is its hash-anchored editing system. When Dirac
reads a file, it computes a deterministic hash for each line. Edits are then
specified as anchor + end_anchor + replacement text rather than
start_line + end_line + text. This means:
- Edits survive file shifts — inserting 50 lines anywhere doesn't invalidate existing anchors
- Multiple disjoint edits can be applied in a single batch with no coordinate conflicts
- Anchor validation catches drift before the edit is applied, giving clear diagnostics
The EditExecutor in src/core/task/tools/handlers/edit-file/EditExecutor.ts
resolves anchors by checking that the anchor name starts with a capital letter,
exists in the file's line hash list, and that the provided content line matches
the actual file content at that hash. If any check fails, the edit fails with
a diagnostic explaining exactly what went wrong.
The batch processor (BatchProcessor.ts) applies multiple edits
in reverse line order — highest line index first — so that earlier edits don't
shift the coordinates of later ones.
Key files
src/core/task/tools/handlers/edit-file/EditExecutor.ts (anchor resolution, content matching),
src/core/task/tools/handlers/edit-file/BatchProcessor.ts (reverse-order batch application),
src/core/task/tools/handlers/edit-file/types.ts (AppliedEdit, FailedEdit, ResolvedEdit types),
src/utils/line-hashing.ts (splitAnchor, stripHashes, getDelimiter)
AST-Native Precision
Beyond hash-anchored text edits, Dirac has AST-aware tools that target specific symbols — classes, functions, interfaces — directly rather than by text position. This means:
- Edits are always syntactically valid — you can't break a brace balance via AST manipulation
- JSDoc comments, decorators, and type annotations are preserved automatically
- Function extraction and class refactoring work reliably across language boundaries
This is the structural equivalent of the hash-anchored system: both prevent the "friction" of coordinate-based editing. Where line numbers drift when the file changes, and raw text matching drifts when the surrounding code changes, AST targeting drifts only when the target symbol itself changes.
Token Efficiency Architecture
Dirac's token efficiency is not an accident — it is a deliberate engineering target backed by multiple mechanisms:
| Mechanism | How it works | Effect |
|---|---|---|
| Hash-anchored multi-file batching | Multiple files edited in single LLM roundtrip | One roundtrip instead of N sequential requests |
| get_file_skeleton | Maps project structure without reading every line | "Just-in-time" code loading keeps context tight |
| ContextManager truncation | Half-strategy and quarter-strategy for long context | Always relevant context in window |
| Concurrent tool calling | Parallel tool execution in single turn | Reduces turn count for complex tasks |
| Minimal system prompt | PRIME DIRECTIVES focus on efficiency, not boilerplate | Fewer tokens per turn |
The ContextManager (src/core/context/context-management/ContextManager.ts)
manages truncation with two strategies:
half truncation (remove the middle 50% of conversation) and
quarter truncation (remove the middle 25%). The choice depends on
context length and model limits.
State Mutex — Thread-Safe Concurrent State
Dirac uses a state mutex (implemented via the p-mutex
package) to serialize all state modifications in the main Task loop. This is
critical because the concurrent tool calling system can produce multiple tool
results simultaneously — without a mutex, two tool completions could corrupt
state simultaneously.
// From src/core/task/index.ts (line ~140)
private stateMutex = new Mutex()
private async withStateLock<T>(fn: () => T | Promise<T>): Promise<T> {
return await this.stateMutex.withLock(fn)
}
Every state modification — tool results, message updates, task history writes —
goes through withStateLock. This prevents race conditions between
the concurrent tool executor and the main task loop without sacrificing the
performance benefit of parallel tool calls.
Hook System
Dirac has an extensive hook system with 8 event types that scripts can intercept:
| Hook | When it fires | Purpose |
|---|---|---|
onTaskStart |
Before agent starts a task | Setup, environment validation |
onTaskComplete |
After task finishes successfully | Cleanup, notification, artifact publishing |
onTaskCancel |
When task is cancelled | Graceful shutdown, partial result handling |
onTaskResume |
When a paused task resumes | State reconstruction |
preToolUse |
Before any tool executes | Validation, blocking, input modification |
postToolUse |
After any tool completes | Logging, result inspection, side effects |
preCompact |
Before context compaction | Protect sensitive content from truncation |
preRequest |
Before sending LLM request | System prompt modification, context augmentation |
Hooks are discovered via auto-discovery in .claude/, .agents/,
or explicitly configured in AGENTS.md. The hook executor
(src/core/hooks/hook-executor.ts) runs hooks with structured JSON
via stdin and supports cancellation via AbortController. Hooks can return a
cancel decision, a contextModification to alter behavior,
or an errorMessage.
Key files
src/core/hooks/hook-executor.ts (executeHook, streaming callback, cancellation),
src/core/hooks/hook-factory.ts (HookFactory, hasHook, getHookInfo),
src/core/hooks/HookManager.ts (active hook execution tracking),
src/core/hooks/PreToolUseHookCancellationError.ts
Git Checkpoint System
Dirac includes a git checkpoint system that creates commits
before risky operations, enabling revert if something goes wrong. This is
built into the Task lifecycle and managed by CheckpointManager.
The checkpoint manager is initialized per workspace (not supported in multi-root
workspaces yet). If a checkpoint operation fails, a warning is displayed but
doesn't block task execution. The system uses buildCheckpointManager
from src/integrations/checkpoints/factory.ts.
Plan/Act Mode Separation
Dirac separates Plan mode and Act mode as first-class execution modes. In Plan mode, Dirac gathers information, asks clarifying questions, and presents a strategy before asking for user approval to switch to Act mode. This mirrors Claude Code's approach but with Dirac's own implementation.
The PlanModeRespondHandler handles plan-mode responses, producing
a structured plan that users can approve or modify before any code is written.
YOLO Mode
YOLO mode (dirac -y "prompt") runs fully
autonomously with auto-approval of all actions. This is useful for simple,
well-understood tasks where the overhead of per-action approval isn't worth it.
YOLO mode forces plain text output and exits automatically on task completion.
When YOLO mode is enabled, the DiracIgnoreController also sets
yoloMode = true, which may affect how certain tools behave.
Command Permissions — DIRAC_COMMAND_PERMISSIONS
Shell command validation in Dirac is configured via the
DIRAC_COMMAND_PERMISSIONS environment variable — a JSON object
that defines allow/deny glob patterns for shell commands. This is more flexible
than a simple ban list: you can allow npm * and git *
while denying rm -rf * and sudo *.
The validation flow: check for dangerous characters → parse into segments
(splitting on &&, ||, |, ;) →
check deny rules first → check allow rules → verify redirects if applicable.
Subshell contents ($(...) and (...)) are recursively
validated.
# Allow only npm and git commands
export DIRAC_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *"]}'
# Allow dev commands but deny dangerous ones
export DIRAC_COMMAND_PERMISSIONS='{"allow": ["npm *", "git *", "node *"], "deny": ["rm -rf *", "sudo *"]}'
Subagent System
Dirac has a SubagentToolHandler (src/core/task/tools/handlers/SubagentToolHandler.ts)
that spawns isolated child agents with their own configuration. Subagents can
be configured with specific tool scopes, model selections, and execution parameters.
The SubagentRunner and SubagentBuilder handle the
lifecycle. The AgentConfigLoader loads agent configurations.
Subagent results are streamed back to the parent, which can then synthesize
or act on the child's output.
Skills System — Auto-Discovery
Dirac's skills system uses auto-discovery from three locations:
AGENTS.md, .claude/, and .agents/
directories. Skills are injected into the system prompt at runtime.
The getOrDiscoverSkills function in
src/core/context/instructions/user-instructions/skills.ts handles
discovery and loading. Skills can be enabled or disabled via the
refreshDiracRulesToggles and refreshExternalRulesToggles
functions, allowing users to toggle rules at runtime without restarting.
Exponential Backoff Retry
Dirac's retry system uses exponential backoff with 2s, 4s,
and 8s delays between attempts. When an API request fails, the retry logic
in src/core/api/retry.ts automatically schedules re-attempts with
increasing delays to avoid hammering rate-limited endpoints.
The retry system also handles streaming failures — if the stream breaks mid-response, Dirac can recover and resume without losing the full conversation context.
Provider Support — 40+ APIs
Dirac is explicitly not tied to one model family. It ships with a large
provider catalog in src/core/api/providers/ covering 40+ APIs
including Anthropic, OpenAI, Google (Gemini), AWS Bedrock, Azure, and many
OpenAI-compatible gateways. Each provider is a separate handler file following
the ApiHandler interface.
The src/core/api/index.ts contains createHandlerForProvider
which instantiates the correct handler from configuration. Model metadata —
IDs, pricing, capability flags like supportsTools and
supportsThinking — is centralized in src/shared/api.ts.
Key files
src/core/api/index.ts (createHandlerForProvider),
src/shared/api.ts (model metadata, pricing, capability flags),
src/core/api/providers/ (individual provider implementations),
src/core/api/transform/ (stream normalization across providers)
No MCP — Explicit Design Decision
Unlike virtually every other agent in this directory set, Dirac does not implement MCP support. This is an explicit design decision, not an oversight. The team chose to focus on its own tool surface rather than maintaining a protocol bridge.
This puts Dirac in a unique position: it is the most accurate on structural code manipulation tasks among the TypeScript-based agents, but it cannot connect to external MCP servers. For users who need MCP integration, this is a significant gap. For users who want a tightly integrated, self-contained agent, this is a feature.
Architecture Overview
Extension entry point → webview → controller → task. The core modules:
src/core/task/— Task execution loop and state managementsrc/core/task/tools/— Tool implementations and handlerssrc/core/prompts/— System and tool prompt templatessrc/core/controller/— High-level extension coordinationsrc/core/context/— Context gathering and managementsrc/core/slash-commands/— Slash command definitionssrc/integrations/— Terminal, browser, editor API wrapperssrc/services/— Logging, telemetry, tree-sitter serviceswebview-ui/— React-based frontendcli/— TypeScript/Ink CLI
Dirac is a fork of Cline, and the architecture reflects that lineage. The core task loop, tool system, and context management are all built on Cline's foundation, with Dirac's innovations layered on top.
CLI Quick Reference
| Command | Description |
|---|---|
dirac "prompt" |
Run a task interactively |
dirac -p "prompt" |
Run in Plan mode |
dirac -y "prompt" |
Run in YOLO mode (auto-approve) |
dirac -T taskId "follow-up" |
Resume an existing task |
dirac --continue |
Resume most recent task |
dirac history |
List task history |
dirac auth |
Authenticate with a provider |
git diff | dirac "Review" |
Pipe context directly to Dirac |
How Dirac compares
Most accurate on structural edits
Hash-anchored editing and AST-native precision make Dirac the most reliable agent for complex multi-file refactoring. On eval tasks, Dirac achieves 8/8 correct while competitors fail 1-3 tasks.
Best for refactoringCheapest to run
At $0.18 average cost per task vs $0.38–$0.73 for competitors, Dirac is 2.8x cheaper. For teams running hundreds of tasks daily, this compounds significantly.
Best for cost-conscious teamsNo MCP integration
The tradeoff for tight integration is the absence of MCP support. If you need to connect to external MCP servers, Dirac is not the right choice.
Gap vs other agentsFork of Cline
Dirac builds on Cline's architecture, inheriting its strong tool surface and VS Code extension foundation. The innovations (hash anchors, AST, batching) are layered on top.
Cline-basedKey files
| File | Role |
|---|---|
src/core/task/index.ts |
Main Task class (1,868 lines) — agent loop, state mutex, tool orchestration |
src/core/task/tools/handlers/edit-file/EditExecutor.ts |
Hash-anchor resolution and edit application |
src/core/task/tools/handlers/edit-file/BatchProcessor.ts |
Reverse-order batch edit processor |
src/core/context/context-management/ContextManager.ts |
Context truncation with half/quarter strategies |
src/core/hooks/hook-executor.ts |
Hook execution with streaming and cancellation |
src/core/prompts/system-prompt/template.ts |
System prompt with PRIME DIRECTIVES |
src/core/api/providers/ |
40+ provider implementations |
src/core/api/retry.ts |
Exponential backoff retry (2s, 4s, 8s) |
cli/man/dirac.1.md |
Full CLI reference (man page) |
walkthrough/step1-4.md |
Feature showcases (AST precision, minimal roundtrips, hash anchors, speed) |