For humans: This file contains instructions for AI coding assistants. See README.md for user documentation.
Efrit is an AI-powered Emacs coding assistant built on the Zero Client-Side Intelligence principle: Efrit is a pure executor that delegates ALL cognitive computation to Claude.
ZERO CLIENT-SIDE INTELLIGENCE: Efrit must NEVER contain:
- Pattern recognition or parsing logic
- Decision-making heuristics
- Pre-written solutions or templates
- Task-specific logic
- Flow control decisions
Efrit's ONLY job: execute what Claude tells it to do. See ARCHITECTURE.md for details.
This is the most important rule. After completing ANY task:
git add -A
git commit -m "Descriptive message"
git pushDo NOT batch multiple fixes. Do NOT wait until "end of session". Push immediately after each completed task.
This project uses bd (beads) for ALL issue tracking. Do NOT use markdown TODOs or other tracking methods.
bd ready # Find unblocked work
bd create "Title" -t bug|feature|task -p 0-4
bd update <id> --status in_progress
bd close <id> --reason "Done"
bd sync # Export to JSONL (auto-commits)Issue Types: bug, feature, task, epic, chore
Priorities: 0=Critical, 1=High, 2=Medium, 3=Low, 4=Backlog
- Check ready work:
bd ready - Claim task:
bd update <id> --status in_progress
- Compile:
make compile - Commit and push immediately
- Close issue:
bd close <id> --reason "Done"
YOU MUST VERIFY ALL CHANGES BEFORE REPORTING COMPLETION.
Acceptable:
- ✅
make compilepasses - ✅
emacs --batchexecution without errors - ✅ Actual API calls for efrit-chat/efrit-do changes
Unacceptable:
- ❌ "The code looks correct"
- ❌ "This should work"
- ❌ Reading code and assuming it works
- Use
lexical-binding: tin all elisp files - Use
efrit-prefix for public functions - Use
efrit--(double dash) for private functions - Docstrings required for all functions
- NO client-side intelligence
efrit/
├── lisp/
│ ├── efrit.el # Main entry point
│ ├── core/ # Core functionality
│ │ ├── efrit-chat.el # Chat interface
│ │ ├── efrit-tools.el # Tool implementations
│ │ ├── efrit-session.el # Session management
│ │ └── efrit-do-schema.el # Tool schemas
│ ├── interfaces/ # User-facing interfaces
│ │ ├── efrit-do.el # Command execution
│ │ ├── efrit-agent.el # Agent mode
│ │ └── efrit-remote-queue.el # AI-to-AI queue
│ ├── tools/ # Individual tool implementations
│ └── support/ # UI helpers (dashboard, todos)
├── test/ # Test files
├── docs/ # Documentation
├── mcp/ # MCP server (TypeScript/Node)
├── .beads/ # Issue tracker database
└── ARCHITECTURE.md # Core design principles
- ARCHITECTURE.md - Pure Executor principle (READ FIRST)
- lisp/interfaces/efrit-do.el - Main command execution
- lisp/core/efrit-do-schema.el - Tool definitions
- lisp/core/efrit-tools.el - Tool system prompt and utilities
Adding a new tool:
- Add handler to
lisp/tools/efrit-tool-*.el - Add schema to
lisp/core/efrit-do-schema.el - Add dispatch entry in
lisp/interfaces/efrit-do.el
Fixing a bug:
bd create "Bug: description" -t bug -p 1- Fix the bug
make compile- Commit and push
bd close <id> --reason "Fixed"
When breaking down large features into tasks, use beads dependencies to sequence work - NOT phases or numbered steps.
Words like "Phase 1", "Step 1", "first", "before" trigger temporal reasoning that flips dependency direction. Your brain thinks:
- "Phase 1 comes before Phase 2" → "Phase 1 blocks Phase 2" →
bd dep add phase1 phase2
But that's backwards! The correct mental model:
- "Phase 2 depends on Phase 1" →
bd dep add phase2 phase1
Solution: Use requirement language, not temporal language
Instead of phases, name tasks by what they ARE, and think about what they NEED:
# ❌ WRONG - temporal thinking leads to inverted deps
bd create "Phase 1: Create buffer layout" ...
bd create "Phase 2: Add message rendering" ...
bd dep add phase1 phase2 # WRONG! Says phase1 depends on phase2
# ✅ RIGHT - requirement thinking
bd create "Create buffer layout" ...
bd create "Add message rendering" ...
bd dep add msg-rendering buffer-layout # msg-rendering NEEDS buffer-layoutVerification: After adding deps, run bd blocked - tasks should be blocked by their prerequisites, not their dependents.
Example breakdown (for a multi-part feature):
# Create tasks named by what they do, not what order they're in
bd create "Implement conversation region" -t task -p 1
bd create "Add header-line status display" -t task -p 1
bd create "Render tool calls inline" -t task -p 2
bd create "Add streaming content support" -t task -p 2
# Set up dependencies: X depends on Y means "X needs Y first"
bd dep add header-line conversation-region # header needs region
bd dep add tool-calls conversation-region # tools need region
bd dep add streaming tool-calls # streaming needs tools
# Verify with bd blocked - should show sensible blocking
bd blocked# Compile all elisp
make compile
# Run tests (if available)
make test
# Quick elisp check
emacs --batch --eval "(progn (require 'efrit) (message \"OK\"))"- Commit and push after EVERY fix
- Use bd for all task tracking
- Verify changes compile before reporting done
- Keep changes minimal and focused
- Batch commits until end of session
- Add client-side intelligence
- Create files without reading existing code first
- Skip compilation verification
Remember: Efrit is a Pure Executor. Claude thinks, Efrit executes.