opencode-elf
v0.5.3
Published
Emergent Learning Framework (ELF) plugin for OpenCode - Learn from past successes and failures with hybrid global/project storage
Maintainers
Readme
OpenCode ELF Plugin
Emergent Learning Framework (ELF) for OpenCode - Learn from past successes and failures to continuously improve your AI coding assistant.
Overview
- Golden Rules: Constitutional principles that guide all actions
- Heuristics: Pattern-based suggestions triggered by keywords/regex
- Learnings: Automatic recording of tool execution failures and successes
- Context Injection: Relevant past experiences are injected into each conversation
- Hybrid Storage: Support for both global and project-scoped memories
- Hybrid Search: Combined vector (semantic) + FTS5 (keyword) search for best results
- Privacy Controls: Use
<private>tags to exclude sensitive data from storage - Automatic Cleanup: Configurable expiration for unused rules and old learnings
- Local-First: Uses local SQLite storage and local embeddings (no API calls)
Key Features
Hybrid Storage
ELF supports both global and project-scoped memories:
- Global memories: Shared across all projects (stored in
~/.opencode/elf/memory.db) - Project memories: Specific to each project (stored in
<project>/.opencode/elf/memory.db) - Project detection via
.gitor.opencodedirectories - Project memories are prioritized in context injection
- Add project memories to
.gitignorefor privacy, or commit for team sharing
Example usage:
"Add a global rule: Always validate user inputs"
"Add a project rule: This API requires JWT authentication"Context injection shows both, with project memories tagged:
Golden Rules:
- Always validate user inputs
- This API requires JWT authentication [project]Performance Optimizations
- Lazy Loading: Non-blocking plugin initialization - OpenCode starts instantly
- Parallel Database Queries: Global + project databases queried simultaneously (30-50% faster)
- Embedding Cache: LRU cache with 5-minute TTL reduces embedding generation by 60-70% on repeated queries
- Efficient Context Retrieval: Optimized vector similarity search with intelligent caching
Local-First Architecture
- Uses local SQLite storage (no cloud dependencies)
- Local embeddings with @xenova/transformers (no API calls)
- All data stays on your machine
- Works offline after initial model download (~90MB)
- Automatic cleanup prevents database from growing indefinitely
Hybrid Search (Vector + FTS)
ELF now supports hybrid search combining semantic vector search with SQLite FTS5 full-text search:
- Vector Search: Great for concepts ("how do I fix a database lock?")
- FTS Search: Superior for specifics ("error code 503", "function processData")
- Hybrid: Combines both approaches for best results
Results include a matchType indicator:
semantic- Found via vector similaritykeyword- Found via FTS keyword matchhybrid- Found by both (boosted score)
Example usage via the elf tool:
"Search my learnings for error code ENOENT"
"Find learnings about authentication failures"Privacy Controls
Protect sensitive data using <private> tags:
The API key is <private>sk-abc123xyz</private>How it works:
- Content wrapped in
<private>...</private>tags is never stored - If the entire content contains privacy tags, the learning is skipped
- Partial private content is replaced with
[REDACTED] - Works for both
contentandcontextin learnings
Installation
Add to your OpenCode config:
// opencode.jsonc
{
"plugin": ["opencode-elf@latest"]
}Using @latest ensures you always get the newest version automatically when OpenCode starts.
Restart OpenCode. The plugin will automatically load.
Architecture
┌──────────────────────────────────────────────────────┐
│ OpenCode ELF Plugin │
├──────────────────────────────────────────────────────┤
│ │
│ Initialization: LAZY LOADING │
│ ┌────────────────────────────────────────────────┐ │
│ │ Background: DB init + Model load + Seeding │ │
│ │ Returns hooks immediately (non-blocking) │ │
│ │ First interaction waits for init completion │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ Hooks: │
│ ┌─────────────────┐ ┌────────────────────┐ │
│ │ chat.params │─────▶│ Context Injection │ │
│ │ (pre-LLM) │ │ - Golden Rules │ │
│ │ │ │ - Past Learnings │ │
│ │ │ │ - Heuristics │ │
│ └─────────────────┘ └────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌────────────────────┐ │
│ │ event │─────▶│ Learning Loop │ │
│ │ (post-tool) │ │ - Record failures │ │
│ │ │ │ - Track patterns │ │
│ └─────────────────┘ └────────────────────┘ │
│ │
│ Storage: │
│ ┌──────────────────────────────────────────────┐ │
│ │ libsql (SQLite) │ │
│ │ ~/.opencode/elf/memory.db │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Embeddings: │
│ ┌──────────────────────────────────────────────┐ │
│ │ @xenova/transformers │ │
│ │ Model: Xenova/all-MiniLM-L6-v2 │ │
│ └──────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘How It Works
Context Injection (Before each message)
When you send a message to OpenCode, ELF:
- Generates an embedding for your message
- Searches for relevant Golden Rules and past Learnings
- Injects this context into the system prompt
Example injection:
[ELF MEMORY]
Golden Rules:
- Always validate user inputs before processing
- Use TypeScript strict mode for type safety
- This project requires JWT authentication [project]
Relevant Past Experiences:
✗ [85%] Tool 'bash' failed: command not found - npm
✗ [78%] API authentication failed without JWT token [project]
Applicable Heuristics:
- When working with npm, always check if node_modules existsLearning Loop (After each tool execution)
When a tool executes, ELF:
- Monitors the result (stdout, stderr, exit codes)
- Records failures automatically to project database
- Stores them with embeddings for future retrieval
Agent Tool (Programmatic Access)
The plugin provides an elf tool that agents can invoke to manage memory:
Available modes:
list-rules- List all golden rules (optionalscope: "global" | "project")list-heuristics- List all heuristics (optionalscope)list-learnings- View recent learnings (optionallimitandscope)add-rule- Add new golden rule (auto-generates embeddings, optionalscope)add-heuristic- Add new heuristic pattern (optionalscope)metrics- View performance metricssearch- Hybrid search across all learnings (requiresquery, optionallimit)
Examples:
"Add a global rule: Always use async/await"
"Add a project rule: This API requires authentication tokens"
"Show me project-specific golden rules"
"List all learnings from this project"
"Search learnings for error code ENOENT"Quick Start
1. Installation & First Run
After installing the plugin, restart OpenCode. The plugin uses lazy loading for fast startup:
What happens:
- OpenCode starts immediately (plugin loads in background)
- Database initialization happens asynchronously
- Embedding model loads in background (~90MB download on first run)
- Default data seeding occurs if needed (first run only)
You'll see output like:
ELF: Initializing in background...
ELF: Ready (took 2847ms)First interaction timing:
- Your first message will wait for initialization to complete (1-3 seconds)
- Subsequent messages are instant (no waiting)
- This moves the "loading time" from startup to first use
The plugin is ready to use! No manual setup required.
2. Verify Installation (Optional)
If you're developing locally, you can run the simulation test to verify everything works:
npm run test:simulateExpected output:
🤖 Starting ELF Simulation...
1️⃣ Seeding Golden Rule...
ELF: Loading embedding model...
ELF: Model loaded.
2️⃣ Simulating Chat Request...
✅ SUCCESS: Context injected Golden Rule into system prompt.
3️⃣ Simulating Tool Failure...
✅ Tool failure event processed.
4️⃣ Verifying Learning Retrieval...
✅ SUCCESS: Retrieved the learned failure from memory.
🎉 Simulation Complete.3. Start Using OpenCode
The plugin now works automatically! Golden rules and learnings will be injected into conversations as context.
Managing Data
The plugin automatically seeds default data on first run. You can view and manage this data in three ways:
1. Natural Conversation (Recommended)
Simply ask OpenCode to manage your ELF memory in natural language:
"Add a golden rule: Always use async/await instead of callbacks"
"Add a project-specific rule: This API requires JWT authentication"
"Show me my current golden rules"
"Show me project-specific learnings"
"Add a heuristic for npm errors"
"What have I learned recently?"OpenCode will automatically invoke the elf tool to:
- Add new golden rules (with automatic embedding generation)
- Add new heuristics
- List rules, heuristics, and learnings
- View performance metrics
2. Optional: Slash Commands
If you prefer slash commands for quick inspection, you can add them to your OpenCode config:
// opencode.jsonc or ~/.config/opencode/opencode.jsonc
{
"plugin": ["opencode-elf@latest"],
"command": {
"elf": {
"template": "Use the elf tool. Arguments: $ARGUMENTS",
"description": "ELF memory system. Commands: rules list, heuristics list, learnings list, search, metrics, rules add, heuristics add"
}
}
}Then you can use:
/elf
/elf rules list
/elf search "error code 503"
/elf metrics3. Using CLI Tools (Advanced)
For local development or advanced management, use the npm scripts (requires plugin directory access):
Golden Rules
Golden Rules are constitutional principles that should always guide the AI's behavior.
# Add a new rule
npm run rules:add "Always validate inputs before processing"
# List all rules
npm run rules:list
# Re-seed default rules (if you deleted them)
npm run rules:seedHeuristics
Heuristics are pattern-based suggestions triggered by regex matching.
# Add a new heuristic
npm run heuristics:add "npm install" "Check package.json exists first"
# List all heuristics
npm run heuristics:list
# Re-seed default heuristics (if you deleted them)
npm run heuristics:seedLearnings
View recorded successes and failures:
# View all learnings
npm run learnings:view
# View only failures
npm run learnings:view failure
# View only successes
npm run learnings:view successPerformance Metrics
Track ELF's performance and usage:
npm run metrics:viewThis shows:
- Average latency for context injection
- Total context injections
- Failures learned
- Recent activity
Cleanup & Maintenance
ELF includes automatic cleanup to prevent the database from growing indefinitely:
Automatic Cleanup (Default: Enabled)
- Runs once per day during normal operation
- Deletes golden rules with 0 hits after 90 days
- Deletes learnings older than 60 days
- Deletes heuristics older than 180 days
Manual Cleanup:
# Preview what would be deleted
npm run cleanup:preview
# Delete expired data
npm run cleanup:cleanConfiguration:
Edit src/config.ts to customize expiration settings:
export const RULE_EXPIRATION_DAYS = 90; // Delete unused rules
export const RULE_MIN_HITS_TO_KEEP = 1; // Rules with < 1 hits
export const LEARNING_EXPIRATION_DAYS = 60; // Delete old learnings
export const HEURISTIC_EXPIRATION_DAYS = 180; // Delete old heuristics
export const AUTO_CLEANUP_ENABLED = true; // Enable/disable auto-cleanupAfter editing config, rebuild with npm run build.
Configuration
The plugin can be configured by modifying src/config.ts and rebuilding with npm run build.
Query & Performance Settings
// Query limits
export const MAX_GOLDEN_RULES = 5; // Max rules to inject per message
export const MAX_RELEVANT_LEARNINGS = 10; // Max learnings to inject
export const SIMILARITY_THRESHOLD = 0.7; // Min similarity for relevance
// Embedding model
export const EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
// Hybrid storage
export const ENABLE_HYBRID_STORAGE = true; // Enable project-scoped memoriesExpiration Settings
export const RULE_EXPIRATION_DAYS = 90; // Delete unused rules after 90 days
export const RULE_MIN_HITS_TO_KEEP = 1; // Rules with 0 hits are candidates
export const LEARNING_EXPIRATION_DAYS = 60; // Delete learnings after 60 days
export const HEURISTIC_EXPIRATION_DAYS = 180; // Delete heuristics after 180 days
export const AUTO_CLEANUP_ENABLED = true; // Enable automatic cleanupDatabase Locations
Global Storage (cross-project):
- macOS/Linux:
~/.opencode/elf/memory.db - Windows:
C:\Users\<username>\.opencode\elf\memory.db
Project Storage (project-specific):
<project-root>/.opencode/elf/memory.db- Automatically detected by finding
.gitor.opencodedirectories
To query the database directly:
sqlite3 ~/.opencode/elf/memory.db "SELECT * FROM golden_rules"To reset all data:
rm -rf ~/.opencode/elf/Database Schema
The plugin uses SQLite with the following tables:
golden_rules
id(TEXT PK)content(TEXT)embedding(TEXT - JSON array)created_at(INTEGER - timestamp)hit_count(INTEGER - usage tracking)
learnings
id(TEXT PK)content(TEXT)category('success' | 'failure')embedding(TEXT - JSON array)created_at(INTEGER)context_hash(TEXT - for deduplication)
learnings_fts (FTS5 Virtual Table)
- Full-text search index on learnings
- Synced automatically via triggers
- Enables fast keyword-based search
heuristics
id(TEXT PK)pattern(TEXT - regex)suggestion(TEXT)created_at(INTEGER)
Project Structure
opencode-elf/
├── package.json # Dependencies & scripts
├── tsconfig.json # TypeScript config
├── README.md # This file
├── LICENSE # MIT license
│
├── src/
│ ├── index.ts # Plugin entry (hooks)
│ ├── config.ts # Configuration
│ │
│ ├── types/
│ │ └── elf.ts # TypeScript types
│ │
│ ├── db/
│ │ └── client.ts # Database client & schema
│ │
│ └── services/
│ ├── embeddings.ts # Vector embeddings
│ ├── metrics.ts # Performance tracking
│ ├── query.ts # Context builder
│ └── cleanup.ts # Automatic data cleanup
│
├── scripts/
│ ├── manage-rules.js # CLI: add/list/delete rules
│ ├── manage-heuristics.js # CLI: add/list/delete heuristics
│ ├── view-learnings.js # CLI: view learnings
│ ├── view-metrics.js # CLI: view metrics
│ ├── cleanup-expired.js # CLI: cleanup expired data
│ ├── seed-rules.js # Seed default rules
│ └── seed-heuristics.js # Seed default heuristics
│
└── tests/
├── simulate.ts # End-to-end simulation
├── test-hybrid.ts # Hybrid storage tests
└── benchmark.ts # Performance benchmarksDevelopment
Building
# Install dependencies
npm install
# Build for production
npm run build
# Watch mode for development
npm run devTesting
# Run end-to-end simulation
npm run test:simulate
# Test hybrid storage functionality
npm run test:hybrid
# Run performance benchmarks
npm run test:benchmarkLocal Development Installation
For local development without publishing to npm:
# Clone and build
git clone https://github.com/mark-hingston/opencode-elf.git
cd opencode-elf
npm install
npm run build
# Add to your opencode.jsonc using local path
{
"plugin": ["file:///absolute/path/to/opencode-elf"]
}Troubleshooting
Plugin Not Loading
- Check OpenCode logs for errors
- Verify plugin is in your
opencode.jsoncconfig - Ensure
dist/folder exists (runnpm run build) - Check for TypeScript compilation errors
Embedding Model Download
First run will download the model (~90MB). This takes 1-2 minutes. Subsequent runs are instant.
Performance Issues
Expected performance (lazy loading + optimizations enabled):
| Operation | First Run | Subsequent Runs | With Cache | |-----------|-----------|-----------------|------------| | Plugin startup | Returns immediately | Instant | - | | First message | 1-3s (waits for init) | ~200-500ms | ~100-200ms | | Context query | ~200-500ms | ~200-500ms | ~100-200ms | | Add golden rule | ~50-100ms | ~50-100ms | - | | Record learning | ~100-200ms | ~100-200ms | - |
Performance Optimizations:
- ✅ Parallel Database Queries: Global + project databases queried simultaneously (30-50% faster)
- ✅ Embedding Cache: LRU cache with 5-min TTL (60-70% faster on repeated prompts)
- ✅ Lazy Loading: Non-blocking startup (OpenCode ready instantly)
Note: With lazy loading, OpenCode starts immediately. Initialization happens in the background, so only your first interaction waits for the model to load.
If performance is slower than expected, check:
- Model is loaded (check logs for "ELF: Ready")
- Database isn't locked
- Sufficient disk space for embeddings cache (~90MB)
- First message timing is expected (includes initialization)
Run performance benchmark:
npm run test:benchmarkRoadmap
- [x] Core learning loop
- [x] Golden rules
- [x] Heuristics
- [x] CLI management tools
- [x] Performance metrics
- [x] Simulation testing
- [x] Hybrid storage (global + project-scoped memories)
- [x] Performance optimizations (parallel queries + embedding cache)
- [x] Hybrid search (vector + FTS5 full-text search)
- [x] Privacy controls (
<private>tag filtering) - [ ] Success detection (currently only failures are auto-recorded)
- [ ] Experiment tracking (hypothesis testing)
- [ ] Decision records (ADRs)
- [ ] Vector index optimization (avoid scanning all learnings)
- [ ] Export/import memory database
- [ ] Analytics dashboard
- [ ] Web UI for management
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Workflow
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run tests:
npm run test:simulate - Build:
npm run build - Commit your changes:
git commit -m 'Add my feature' - Push to the branch:
git push origin feature/my-feature - Submit a pull request
Code Style
- Use TypeScript strict mode
- Follow existing code patterns
- Add JSDoc comments for public APIs
- Keep functions small and focused
License
MIT
