Introduction
Cerebro is human-centric. The cortex is yours—plain markdown you can read, edit, and open in any tool. AI integration is optional, not a requirement.
The problem
You have side projects. Maybe 3, maybe 10. You work on them in bursts: a flurry of commits, then months of silence.
When you return, the question is always the same:
“Wait, what work was in progress? Where did things leave off?”
You scroll through git history. You grep for “TODO”. You search your notes. By the time you remember, you’ve already lost 20 minutes.
The solution
Cerebro watches your development activity and builds a personal mission control dashboard. It aggregates:
- AI sessions: Your AI pair-programming history (OpenCode, Pi, or both)
- Git activity: Commits, branches, file changes
- TODOs: TODO, Needs Fixing, Hack, and Placeholder comments in your code
- Manual notes: Your status, next actions, goals
When you return to a dormant project, your dashboard answers the question in seconds.
How it works
Cerebro uses a two-repo architecture:
flowchart LR
subgraph "Sources"
G[Git repos]
O[AI sessions]
end
subgraph "cerebro (CLI)"
C[cerebro build]
end
subgraph "cortex (your data directory)"
CT[content/ generated md]
NB[content/notes/ manual]
BK[book/ rendered HTML]
end
subgraph "cerebro-mcp"
MS[MCP server]
end
subgraph "cerebro-tui"
TUI[Terminal UI]
end
subgraph "Consumers"
TERM[Terminal user]
MDB[mdbook]
OC[OpenCode]
end
G -->|scrape| C
O -->|scrape| C
C -->|writes| CT
NB -. manual .-> CT
CT -->|mdbook| BK
NB -->|mdbook| BK
MS -->|reads| CT
MS -->|reads| NB
OC <-->|JSON-RPC| MS
TUI -->|reads| CT
TUI -->|reads| NB
TERM <-->|interactive| TUI
The two repos
| Repo | Purpose |
|---|---|
~/Projects/cerebro | Source code: command-line tool, MCP, Terminal User Interface, shared types |
~/Projects/<name> | Your cortex: config, generated content, manual notes, rendered HTML |
The three binaries
| Binary | Purpose | Used by |
|---|---|---|
cerebro | Command-line tool: scrape repos, generate markdown | You, cron |
cerebro-mcp | MCP server: answer questions about projects | Any MCP client |
cerebro-tui | Terminal UI: browse projects interactively | You |
Content boundaries
Your cortex’s content/ has a clear split:
| Path | Generated by | Edit? |
|---|---|---|
content/index.md | cerebro | No |
content/projects/*.md | cerebro | No |
content/journal/ | cerebro | No |
content/today.md | cerebro | No |
content/this-week.md | cerebro | No |
content/intent/ | Human | Yes |
content/notes/ | Human | Yes |
content/SUMMARY.md | Human | Yes |
An AGENTS.md file in your cortex enforces the boundary. AI agents should not edit manual content.
Why it works
Cerebro succeeds when it reduces context-switching friction. You’ll notice it when:
- You return to a project you haven’t touched in weeks
- You have too many projects to track mentally
- You use AI assistants and want to remember what you worked on
Key concepts
| Term | Meaning |
|---|---|
| Collector | A source of data (git, opencode, todos, notes) |
| Generator | Creates output files from collected data |
| Project | A repo + notes + activity you’re tracking |
| Dashboard | The generated markdown output |
| Cortex | Your data directory: config, content, rendered book |
| Terminal User Interface | Terminal UI for interactive browsing |
Architecture
Cerebro is a Rust workspace with four crates:
- cerebro: Command-line tool: coordinates collectors and generators, manages cache
- cerebro-core: Shared types, traits, collector implementations, and query operations
- cerebro-mcp: MCP server for OpenCode integration
- cerebro-tui: Ratatui terminal UI for browsing projects interactively
Next steps
- Quick Start: Get running in 5 minutes
- Dashboard Guide: Understand the output
- Configuration: Customize for your projects
Quick start
Get your dashboard running in 5 minutes.
1. Install
cargo install --path crates/cerebro
2. Create config
Place config.toml at the XDG default location ($XDG_CONFIG_HOME/cerebro/config.toml, or ~/.config/cerebro/config.toml if XDG_CONFIG_HOME is unset — same on every platform):
[settings]
output_dir = "~/Projects/cortex/content"
[[projects]]
name = "my-project"
repo_path = "~/Projects/my-project"
active = true
(Override the path with --config <file> or by setting CORTEX_PATH and putting config.toml in the cortex data dir — see Configuration.)
3. Build
cerebro build
This generates your dashboard in the output_dir you set in config (default: ~/Projects/cortex/content/).
4. Serve
cd ~/Projects/cortex && mdbook serve --port 3456
Open http://localhost:3456 to see your dashboard.
Your first win
After a few days of use, come back to a dormant project. Instead of:
“Wait, what work was in progress? Where did things leave off?”
Your dashboard shows:
- Last activity: 3 days ago: “feat: add user authentication”
- OpenCode session: “Debugging JWT token refresh”
- TODOs left:
src/auth.rs:42: “TODO: handle expired tokens” - Next action: From your notes: “Test the login flow”
That’s the moment cerebro earns its place in your workflow.
Next steps
- Configuration: Customize settings
- Dashboard Guide: Understand the output
- Commands: All available commands
Dashboard guide
Understanding the output cerebro generates.
Output structure
{cerebro-dashboard}/
├── index.md # Your mission control
├── today.md # Today's activity
├── this-week.md # This week's activity
├── projects/
│ └── {name}.md # Per-project pages
└── journal/
└── {year}/
└── {mm}/
└── {dd}.md # Daily journal pages
Index.md: Your mission control
The main dashboard shows:
Activity overview
- Projects at a glance: Which have recent activity
- Git commit counts: How much you’ve shipped
- AI-assisted sessions: How many sessions your AI coding tools logged (OpenCode + Pi, only shown if configured)
- TODO totals: Work waiting
Project cards
Each project shows:
- Last activity: When you last worked on it
- Recent commits: What you shipped
- AI-assisted sessions: AI-assisted work
- TODOs: Outstanding items
- Status: From your notes, such as “paused” or “actively developing”
- Next action: Your stated next step
Projects/{name}.md: Deep dive
Click any project card to see full context:
Header
- Repository path
- Last activity timestamp
- Status badge
Your notes
Whatever you’ve written in notes/projects/{name}.md:
## Status
building: integrating Stripe API
## Next
- Test webhook endpoints locally
- Deploy to staging
Git activity
Recent commits with messages, dates, and files changed.
AI-assisted sessions
AI-assisted work sessions with titles and timestamps.
TODOs
List of TODO, Needs Fixing, Hack, and Placeholder items found in code:
- TODO: handle expired JWT tokens (src/auth.rs:42)
- FIXME: race condition in cache (src/cache.rs:18)
- HACK: temporary rate limiting (src/api.rs:103)
Today.md: Daily pulse
Today’s activity, scoped to the last 24 hours.
This-week.md: Weekly review
This week’s activity, scoped to 7 days.
Journal/{date}.md: Historical record
Daily pages with all activity for that date. Useful for:
- Reviewing what you accomplished
- Identifying patterns in your work
- Journaling your development journey
Using the dashboard
For context switching
When returning to a project:
- Open the dashboard
- Find your project by searching or clicking
- Read: status → next → TODOs → recent activity
For daily standup
- Check
today.mdfor today’s work - Review
this-week.mdfor the week’s progress
For planning
- Read your notes in each project
- Review TODOs to see what’s left
Customization
The dashboard is just markdown. You can:
- Add it to git for version control
- Deploy it to a static host
- Customize the templates in
generators/ - Add your own data sources
Terminal User Interface guide
Launch the terminal UI with:
cerebro tui
Or run the standalone binary directly:
cerebro-tui
The Terminal User Interface provides a rich, interactive terminal interface for browsing all your project activity data, including sessions, commits, TODOs, journal entries, and manual notes, without leaving your terminal.
Layout
┌─────────────────────────────────────────────────────┐
│ Cerebro TUI │
├──────────┬──────────────────────────────────────────┤
│ Dashboard│ [Content area: changes per view] │
│ Projects │ │
│ Journal │ │
│ TODOs │ │
│ Config │ │
├──────────┴──────────────────────────────────────────┤
│ r refresh b build Space leader q quit ? help │
└─────────────────────────────────────────────────────┘
Navigation
Main views
| Key | Action |
|---|---|
j / ↓ | Next item in sidebar |
k / ↑ | Previous item in sidebar |
Tab | Cycle focus between sidebar and content |
Shift+Tab | Cycle focus backward |
Enter | Switch to selected view from sidebar |
1 / g | Dashboard |
2 | Projects |
3 | Journal |
4 | TODOs |
5 / G | Config |
Leader key
Press Space then a letter to jump directly:
| Sequence | Action |
|---|---|
Space d | Dashboard |
Space p | Projects |
Space j | Journal |
Space t | TODOs |
Space c | Config |
The leader key is active everywhere except during text input, such as journal
editing, note editing, or form input. Press Esc to cancel a pending leader
sequence.
Focus management
| Key | Action |
|---|---|
Right / l | Move focus from sidebar to content |
Left / h | Move focus from content to sidebar |
Views
Dashboard
Summary of all projects with session counts, commit counts, and TODO counts.
Press Enter on a project to drill into its detail view.
Projects
Without a selected project, the view shows a list of all projects with activity
counts. Navigate with j/k, press Enter to select.
After selecting a project, three tabs appear:
| Key | Tab |
|---|---|
1 | Sessions |
2 | Commits |
3 | TODOs |
Tab | Next tab |
Shift+Tab | Previous tab |
Within each tab, navigate items with j/k. Press Enter on a TODO to preview
its full context. Press e to edit the project’s status/next notes. Press Esc
to deselect and return to the project list.
Journal
Shows journal entries from all projects. Date headers, such as ### 2026-04-25,
group the entries, or the view shows a single entry per project if no date
headers exist.
| Key | Action |
|---|---|
j / ↓ | Next entry |
k / ↑ | Previous entry |
g | First entry |
G | Last entry |
Enter | View full entry |
i | Edit journal for the selected project |
Esc | Close preview / cancel edit |
Journal entries come from ## Journal sections in your project notes files under
content/notes/projects/. If no journal sections exist, the view is empty.
TODOs
Aggregates TODOs from all projects. Navigate with j/k, press Enter to
preview the full TODO with file context. Press Esc to close preview.
Config
Toggle project active/inactive status and add new projects. The TUI writes changes to config.toml on disk.
Global commands
| Key | Action |
|---|---|
r | Refresh data from all sources |
b | Run cerebro build, which generates the dashboard |
q | Quit |
? | Toggle help overlay |
Esc | Close overlays, go back |
Mouse support
Click any item in the sidebar to switch views. Click a project in the project list to select it.
What the Terminal User Interface does
- Browse OpenCode sessions, git commits, and TODOs per project
- View project status and next-session notes
- Edit status and next notes directly from the TUI
- Read journal entries from daily logs
- Toggle project active/inactive status
- Add new projects to config
- Refresh data on demand
- Build the markdown dashboard from the TUI
What the Terminal User Interface doesn’t do yet
- Session message content: Sessions show titles and timestamps but not individual messages
- Session preview: Can’t view the conversation inside a session
- Commit diff: Shows commit messages but not file changes
- TODO editing: Can view TODOs but not mark them complete, since they are code comments
- Search/filter: No text search across sessions, commits, or TODOs yet
- Activity graph: No heatmap or timeline visualization
- Multi-project comparison: Can’t view two projects side by side
- Git branch info: Doesn’t show current branch or uncommitted changes per project
Configuration
Cerebro reads a config.toml file. The CLI and TUI use the XDG default
($XDG_CONFIG_HOME/cerebro/config.toml, or ~/.config/cerebro/config.toml
if XDG_CONFIG_HOME is unset — same on every platform).
The MCP server uses CORTEX_PATH to locate the cortex data directory (falls back to ~/Projects/cortex).
Where the config is loaded from
Priority:
--config <path>CLI flag (explicit override)- XDG config dir (
$XDG_CONFIG_HOME/cerebro/config.toml, falling back to$HOME/.config/cerebro/config.toml) — the default CORTEX_PATHenv var (legacy fallback for users who colocate config with their cortex data dir)./config.tomlin the current working directory (last-resort fallback)
Example Configuration
[settings]
opencode_db_path = "~/.local/share/opencode/opencode.db"
pi_sessions_path = "~/.pi/agent/sessions"
output_dir = "./content"
[[projects]]
name = "my-project"
repo_path = "~/Projects/my-project"
active = true
Settings
| Setting | Required | Description | Default |
|---|---|---|---|
output_dir | No | Where to write the generated dashboard | ./content |
opencode_db_path | No | Path to OpenCode’s session database. Only needed if you want OpenCode session history. | None |
pi_sessions_path | No | Path to Pi’s sessions directory. Only needed if you want Pi session history. | None |
Project Configuration
Define each project with:
| Setting | Description | Required |
|---|---|---|
name | Project name | Yes |
repo_path | Path to git repository | Yes |
active | Include in dashboard generation | Yes |
Manual notes
Manual notes aren’t configured per-project. They live directly in your cortex:
| Path | Purpose | Edited by |
|---|---|---|
content/intent/ | Goals for daily, weekly, monthly, or yearly periods | Human only |
content/notes/ | Evergreen notes and documentation | Human only |
content/SUMMARY.md | Navigation structure | Human only |
An AGENTS.md file in your cortex defines the boundary between auto-generated
and manual content. Cerebro only writes to content/index.md,
content/projects/, content/journal/, content/today.md, and
content/this-week.md.
Commands
Cerebro provides the following command-line tool commands:
Build commands
cerebro build
Generate the dashboard. Uses cached data where fresh.
cerebro build # Build all projects
cerebro build --project my-proj # Build specific project
cerebro build --fresh # Force rebuild, ignoring cache
cerebro serve
Serve the generated dashboard with mdBook locally.
cerebro serve # Default port is 3000
cerebro serve --port 8080 # Custom port
Note: this spawns mdbook in the configured output_dir and exits immediately.
For a persistent server, run mdbook serve directly in your cortex.
Status commands
cerebro status
Check cache status for all projects.
cerebro status
Shows last build time per project, cache TTL remaining, and any stale data.
Query commands
These commands read cortex data and output JSON: useful for scripting or piping.
cerebro projects list
List all tracked projects.
cerebro projects list # All projects
cerebro projects list --active-only # Only active projects
cerebro projects read <name>
Read the full context for a specific project.
cerebro projects read my-project
Returns sessions, commits, TODOs, and manual notes for the named project.
cerebro journal read <date>
Read a journal entry for a specific date.
cerebro journal read 2026-05-17
cerebro journal today
Read today’s journal entry.
cerebro journal today
cerebro intent <period> <identifier>
Read intent/goals for a period.
cerebro intent daily 2026-05-17
cerebro intent weekly 2026-W20
cerebro intent monthly 2026-05
cerebro intent yearly 2026
cerebro todos
Search TODO comments across all projects.
cerebro todos # All TODOs
cerebro todos --keyword FIXME # Filter by keyword
cerebro todos --project my-project # Filter by project
cerebro stats
Get overall cortex statistics.
cerebro stats
Returns project count, session count, commit count, TODO count, and recent activity metrics.
MCP commands
cerebro mcp
Start the MCP server. Works with any MCP-compliant client, including OpenCode, Claude, and Cursor. Reads from the cortex directory containing the config file.
cerebro mcp
Reads CORTEX_PATH to locate the cortex data dir (falls back to ~/Projects/cortex). For the CLI/TUI config, see Configuration.
TUI
cerebro tui
Launch the Terminal User Interface for browsing projects interactively.
cerebro tui
The Terminal User Interface shares data types with the command-line tool,
cerebro-core, and reads the same cortex content that mdbook renders. A
standalone binary cerebro-tui is also available for running the Terminal User
Interface independently.
MCP tools reference
The cerebro MCP server provides 7 tools that allow AI assistants to query cortex
data. All tools are thin wrappers over cerebro-core query operations.
Tool list
list_projects
List all tracked projects with their metadata.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
active_only | boolean | No | If true, only return active projects. Default: true |
Returns: array of project objects with name, repo_path, active status, and last activity timestamp.
read_project
Read combined generated and manual content for a specific project.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Project name to read |
Returns: combined content including project config, sessions, commits, TODOs, and manual notes, covering status, next actions, and journal excerpts.
read_journal
Read a journal entry for a specific date.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
date | string | Yes | Date in YYYY-MM-DD format |
Returns: journal entry content for the specified date, or empty if no entry exists.
read_today
Read today’s journal entry.
Parameters: none
Returns: today’s journal entry content, or empty if no entry exists for today.
read_intent
Read intent/goals for a specific period.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
period | string | Yes | Period type: daily, weekly, monthly, or yearly |
identifier | string | Yes | Period identifier, such as 2026-04-15 for daily, 2026-W15 for weekly, 2026-04 for monthly, 2026 for yearly |
Returns: intent/goals content for the specified period.
search_todos
Search TODO comments across all projects.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
keyword | string | No | Filter by keyword: TODO, Needs Fixing, Hack, or Placeholder |
project | string | No | Filter by project name |
Returns: array of TODO items with path, line number, keyword type, and comment text.
get_stats
Get overall cortex statistics.
Parameters: none
Returns: statistics including project count, total TODO count, session count, commit count, and recent activity metrics.
Configuration
The MCP server reads CORTEX_PATH to locate the cortex data directory. If unset, it falls back to ~/Projects/cortex. Most users with the default layout don’t need to set it.
Start it from any MCP-compliant client. The example below uses OpenCode:
{
"mcpServers": {
"cerebro": {
"command": "cerebro",
"args": ["mcp"],
"env": {
"CORTEX_PATH": "/path/to/cortex"
}
}
}
}
Configure the command and environment variables according to your client’s documentation. The server speaks standard MCP (JSON-RPC) and has no dependency on any specific client.
Command-line tool equivalents
Each MCP tool has a corresponding command-line tool command that returns the same data as human-readable output. If you don’t use an AI assistant, the command-line tool commands give you direct access to your cortex from the terminal.
| MCP tool | Command-line tool equivalent |
|---|---|
list_projects | cerebro projects list |
read_project | cerebro projects read <name> |
read_journal | cerebro journal read <date> |
read_today | cerebro journal today |
read_intent | cerebro intent <period> <identifier> |
search_todos | cerebro todos |
get_stats | cerebro stats |
Troubleshooting
Common issues and solutions.
Installation
“Command not found: cerebro”
Make sure the cargo bin is in your PATH:
export PATH="$HOME/.cargo/bin:$PATH"
Add this to your shell profile, such as ~/.zshrc or ~/.bashrc, to persist.
“Couldn’t find cerebro”
Run cargo install cerebro from the project directory:
cargo install --path .
Configuration
“Config file not found”
Create config.toml:
cp config.toml.example config.toml
Or create manually:
[settings]
opencode_db_path = "~/.local/share/opencode/opencode.db"
pi_sessions_path = "~/.pi/agent/sessions"
output_dir = "./content"
[[projects]]
name = "my-project"
repo_path = "~/Projects/my-project"
active = true
“Couldn’t parse config”
Check your TOML syntax:
- Use
=not: - String values need quotes:
"path"notpath - Arrays use brackets:
[[projects]]not[projects]
Validate with:
cargo run -- build
“Repo not found”
Verify repo_path exists:
ls -la ~/Projects/my-project
Build
“No activity found”
Likely causes:
- No git commits yet: Commit something first
- No AI sessions: Only relevant if you have configured
opencode_db_pathorpi_sessions_path - No TODOs: Add a TODO or Needs Fixing comment
This is normal for new projects. Activity appears as you work.
“Build is slow”
First build can be slow on large repos. Subsequent builds use cache.
Force a fresh build:
cerebro build --fresh
Clear cache manually:
cerebro status
# Note the cache location
rm {cache_file}
“OpenCode database not found”
Set the correct path in config:
opencode_db_path = "~/.local/share/opencode/opencode.db"
Verify the file exists:
ls -la ~/.local/share/opencode/opencode.db
MCP server
“MCP tools not available”
Restart OpenCode after configuring the MCP server.
Set CORTEX_PATH if your cortex data dir is not at the default ~/Projects/cortex:
export CORTEX_PATH="~/Projects/my-data-dir"
Test the server directly:
cargo run -p cerebro-mcp
“No projects found in MCP”
Verify your config includes the project and it’s set to active = true.
Serving
“Port already in use”
Pick a different port:
cerebro serve --port 3001
“Dashboard not updating”
Rebuild with --fresh:
cerebro build --fresh
Then refresh your browser.
General
“Unexpected behavior”
Run with debug logging:
RUST_LOG=debug cargo run -- build
“It’s broken and the cause is unclear”
Clear everything and start fresh:
# Remove output
rm -rf ~/cerebro-dashboard
# Remove cache
rm -rf ~/Library/Caches/cerebro # macOS
rm -rf ~/.cache/cerebro # Linux
# Rebuild
cerebro build --fresh
Development setup
Prerequisites
- Rust 1.85 or later
- Cargo, included with Rust
Building
# Development build
cargo build
# Release build
cargo build --release
Testing
cargo test
Quality checks
# Format check
cargo fmt --check
# Lint check
cargo clippy -- -D warnings
# All quality checks
cargo check
Workspace structure
cerebro/
├── crates/
│ ├── cerebro/ # CLI tool
│ ├── cerebro-core/ # Shared types
│ ├── cerebro-mcp/ # MCP server
│ └── cerebro-tui/ # Terminal UI
├── docs/ # Documentation
└── book.toml # mdbook config
Running locally
# Build and run
cargo run -- build
# With arguments
cargo run -- build --project my-project
MCP server
The MCP server lives in crates/cerebro-mcp/ and provides standard MCP tools for querying cortex data.
Building
cargo build --release -p cerebro-mcp
The binary is output to target/release/cerebro-mcp.
Running
# Via cargo
cargo run -p cerebro-mcp
# Or directly
./target/release/cerebro-mcp
Environment variables
| Variable | Default | Description |
|---|---|---|
CORTEX_PATH | ~/Projects/cortex (MCP) / unset (CLI, TUI) | Path to the cortex data directory. Used by cerebro-mcp to read generated content; legacy fallback for cerebro/cerebro-tui config lookup. The CLI/TUI prefer the XDG config (~/.config/cerebro/config.toml) and only fall through to CORTEX_PATH if no XDG config exists. |
RUST_LOG | info | Log level: debug, info, warn, or error |
Testing guide
Cerebro uses a multi-layer testing strategy across its four crates.
Running tests
cargo test --workspace # All tests
cargo test -p cerebro-core # Core library only
cargo test -p cerebro-mcp # MCP server only
cargo test -p cerebro-tui # TUI only
cargo test -- --nocapture # Show stdout during tests
Test layers
Unit tests
Each module contains #[cfg(test)] blocks testing individual functions, methods,
and state transitions. These are the fastest and most numerous tests.
Examples:
- Collector parsing, including TODO regular expression matching and git commit parsing
- Action enum serialization/deserialization
- Key binding resolution
- Input mode state checks
- Config loading and validation
Integration tests
The TUI crate uses ratatui::backend::TestBackend to render components to an
in-memory buffer and assert on the rendered content:
#![allow(unused)]
fn main() {
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend)?;
// Render component, assert buffer string
}
For full app lifecycle tests, construct Home directly with mock dependencies,
call set_cerebro_config() and do_initial_load_sync().await, then render the
terminal and assert against it.
Navigation tests verify home.view_name() changes, not just that text appears
somewhere in the buffer, since sidebar text is always visible.
Behavior-driven development tests
Write user-facing scenarios in Gherkin with step definitions in tests/cucumber.rs. The BDD workflow:
- Write Gherkin scenarios describing user behavior in terms of what the user does and sees
- Write step definitions that assert against real component state, such as
home.view_name() - Run: must go red before implementation changes
- Implement to make it green
- Commit only when the full suite passes
Step definitions must never assert against buffer text that happens to match: that produces false positives. Always check component state directly.
System tests with Ghostty
The TUI has system-level tests using Ghostty devtools for:
- Terminal lifecycle, including startup, shutdown, and panic recovery
- Layout adaptation on resize
- UI responsiveness during long operations
These tests run against the actual binary in a real terminal, not a mock backend.
Test coverage by crate
| Crate | Unit | Integration | BDD | System |
|---|---|---|---|---|
cerebro | Config, cache, generators | — | — | — |
cerebro-core | Collectors, types, queries | — | — | — |
cerebro-mcp | Server setup | — | — | — |
cerebro-tui | Components, actions, keybindings | Full app render | User scenarios | Ghostty lifecycle |
Adding tests
Adding a new component: add at least one snapshot test using assert_buffer_snapshot! to verify rendered output.
Adding a new function or method: add unit tests for happy path and edge cases.
Fixing a bug: add a regression test that would have caught the bug.
Refactoring: ensure existing tests still pass. Add tests if coverage was zero.
AI workflow audit plan
Based on Dru Knox, Stop Prompting, Start Engineering, YouTube, February 25, 2026.
This document captures actionable infrastructure changes to improve how cerebro manages AI workflows. The goal is to treat AI context and session behavior as observable, measurable systems, not as opaque chat sessions.
Mindset: from individual contributor to tech lead
When using AI agents, the human role shifts from individual contributor to tech lead. The job is no longer writing all the code directly. It’s ensuring the team writes good code by maintaining standards, documentation, and quality gates.
This document notes this shift but requires no tool changes. It’s a framing device for the infrastructure decisions that follow.
Problem: Non-deterministic tooling waste
AI agents are non-deterministic. They repeat tasks, thrash between approaches, make unnecessary tool calls, and burn tokens on false paths. Without telemetry, this waste is invisible. Instrument the agent’s behavior to detect and eliminate waste programmatically, not by watching every session.
Audit Infrastructure
1. Session log analysis scripts
OpenCode stores session logs locally in SQLite at ~/.local/share/opencode/opencode.db. The
database is already read for dashboard rendering. Add analysis scripts to
extract waste patterns from session history.
Waste patterns to detect
| Pattern | Description | Signal |
|---|---|---|
| Thrashing | Agent switches approaches repeatedly without progress | Same tool called >3x with similar args in one session |
| Retry loops | Agent retries after failures without changing strategy | cargo check fails, agent reruns same command |
| Over-tooling | Agent uses tools when direct reasoning suffices | File read followed by immediate file read of same content |
| Apology tax | Agent apologizes and backtracks | Phrases like “sorry,” “you’re absolutely right” in assistant messages |
| Token bloat | Context window fills with redundant planning | Repeated restatement of plan without new information |
Proposed script: cerebro audit sessions
A new command-line tool subcommand, or standalone script in scripts/, that:
- Queries the opencode database for recent sessions
- Parses message history for waste patterns
- Outputs a summary report with:
- Sessions analyzed
- Waste events detected, grouped by category
- Estimated token cost of waste
- Suggested configuration changes
Example output:
Sessions analyzed: 12
Thrashing events: 3 (25% of sessions)
Retry loops: 7 (58% of sessions)
Over-tooling: 2 (17% of sessions)
Top suggestion: Add `cargo check` pre-validation to agent context
Affected sessions: 5
Waste pattern: Agent runs `cargo check`, sees error, proposes fix,
runs `cargo check` again without applying fix first.
2. Static analysis for hallucination prevention
As agents perform more divergent and inductive reasoning, the risk of hallucinated APIs, incorrect types, and phantom dependencies increases. Static analysis is the first line of defense: it catches nonsense before it compiles, before it runs, before it gets committed.
The project already enforces clippy::pedantic at the workspace level. This section proposes expanding
that enforcement specifically for AI-generated code paths.
Current state
The project already forbids unwrap(), panic!(), and expect() in production code. This
is an excellent baseline. Extend this philosophy.
Proposed additions
| Check | Purpose | How |
|---|---|---|
| Import validation | Prevent phantom crate references | cargo check in CI must pass before any PR |
| Type strictness | Prevent inferred-type hallucinations | Enable clippy::pedantic lints that reject implicit conversions |
| Documentation coverage | Force agents to document public APIs | cargo doc --no-deps must pass without warnings |
| Dead code detection | Catch abandoned experiments | clippy::dead_code as deny, not warn |
| TODO/Needs Fixing enforcement | Prevent permanent placeholder code | Already exists; ensure it runs on AI-generated commits too |
Pre-flight hook for agent sessions
Consider a lightweight cargo alias or script that agents run before submitting:
# In .cargo/config.toml or agent context
[alias]
preflight = "fmt --check && clippy --workspace -- -D warnings && check"
Instruct agents to run cargo preflight before declaring a task complete. This
catches hallucinations at the agent’s workstation, not in CI.
3. Configuration to prevent thrashing
OpenCode and similar agents accept configuration that shapes their behavior.
Codify anti-thrashing settings in the project’s AGENTS.md and explore opencode
configuration options.
Token budget awareness
| Setting | Suggestion | Rationale |
|---|---|---|
| Context window limit | Explicitly state the project’s preferred max | Prevents agents from stuffing irrelevant files into context |
| Tool call budget | Configure max sequential tool calls | Forces agent to reason rather than search |
| Retry limit | Cap retries at 2 with forced pause | Prevents infinite retry loops on flaky operations |
Context hygiene rules
Add to AGENTS.md:
## Agent Efficiency Rules
1. **Run `cargo preflight` before every completion claim.**
2. **If a command fails twice with the same error, stop and ask.**
3. **Do not read a file you already read in this session unless it changed.**
4. **Before using a new crate or API, verify it exists in `Cargo.lock`.**
5. **Keep context focused: only include files relevant to the current task.**
4. CI/CD Integration
Woodpecker CI is already in use. The audit plan should extend CI to catch AI workflow regressions.
Proposed CI checks
| Stage | Check | Trigger |
|---|---|---|
| Lint | cargo clippy --workspace -- -D warnings | Every push |
| Format | cargo fmt --check | Every push |
| Doc | cargo doc --no-deps | Every push |
| Test | cargo test --workspace | Every push |
| Audit | Run session waste analyzer on last 7 days | Weekly cron, or run manually |
The weekly audit stage is lightweight: it queries the local opencode database, or a centralized copy, and posts a summary comment on the PR if waste patterns spike.
5. Plugin and Linter Integration
Rather than building custom LLM-as-judge evals, leverage existing tools and explore opencode plugins that reduce waste.
Tool Inventory
| Tool | Current Use | AI Workflow Enhancement |
|---|---|---|
clippy | Lint enforcement | Add lints that catch common AI mistakes, such as unused imports from hallucinated code |
cargo-deny | License checking | Could extend to detect unexpected crate additions |
lefthook | Pre-commit hooks | Add hook that warns if AGENTS.md context is stale |
dprint | Markdown formatting | Ensure AGENTS.md and context files stay parseable |
Opencode plugin opportunities
The opencode ecosystem may support plugins that:
- Intercept tool calls and cache file reads for the session duration
- Enforce a “budget” of tool calls per task
- Auto-run
cargo preflightbefore allowing the agent to report success - Summarize session waste and append it to the cortex journal
These are research items. This plan flags them for investigation.
Measurement: tracked metrics
The audit infrastructure collects:
| Metric | Source | Target |
|---|---|---|
| Session duration | opencode.db | Reduce median by 20% |
| Tool calls per session | opencode.db | Reduce median by 30% |
cargo check failures before success | opencode.db + git log | Reduce retry rate below 10% |
| Clippy warnings on AI-generated commits | git diff + cargo clippy | Zero warnings |
| Agent backtracks, such as saying “sorry” or “you’re right” | opencode.db message text | Reduce by 50% |
Immediate actions
- Write the session analyzer script. A standalone Rust binary or Python script that
reads
opencode.dband outputs waste statistics. - Update
AGENTS.mdwith agent efficiency rules. Codify the anti-thrashing guidelines. - Add
cargo preflightalias. Make it trivial for agents to self-check. - Schedule weekly CI audit job. Run the analyzer and surface trends.
- Research opencode plugin API. Determine if custom plugins can enforce tool budgets.
Deferred actions
- Evals / statistical testing: Not pursued. The cost of LLM-as-judge evals outweighs the benefit for this project. The project uses deterministic static analysis instead.
- Automated context updates via PR scanning: Interesting but requires a stable context
format first. Revisit after
AGENTS.mdstructure matures.
References
- Dru Knox, “Stop Prompting, Start Engineering: The ‘Context as Code’ Shift,” YouTube, February 25, 2026
- Cerebro AGENTS.md: Current agent guidelines
- Architecture Decision Record 0005: Model Context Protocol server for OpenCode: Existing OpenCode integration
Architecture
Cerebro is a Rust workspace with four crates that share types and read from a common cortex data directory.
System overview
flowchart TB
subgraph cerebro_repo["~/Projects/cerebro (source code)"]
CLI_CRATE["crates/cerebro<br/>CLI binary"]
CORE_CRATE["crates/cerebro-core<br/>shared types"]
MCP_CRATE["crates/cerebro-mcp<br/>MCP library"]
TUI_CRATE["crates/cerebro-tui<br/>TUI binary"]
DOCS["docs/<br/>tool documentation"]
end
subgraph cortex_dir["~/Projects/<name> (your cortex)"]
CFG["config.toml"]
AUTO["content/ (auto-generated)<br/>index.md, projects/, journal/"]
MANUAL["content/ (manual)<br/>intent/, notes/, SUMMARY.md"]
BOOK["book/<br/>rendered HTML"]
AGENTS["AGENTS.md<br/>edit boundaries"]
end
subgraph external["External data sources"]
GIT["project git repos"]
OODB["opencode sessions.db"]
end
subgraph consumers["Consumers"]
MDBOOK["mdbook"]
OPENCODE["OpenCode"]
TERMINAL["Terminal user"]
end
CLI_CRATE -->|scrapes| GIT
CLI_CRATE -->|queries| OODB
CLI_CRATE -->|reads| CFG
CLI_CRATE -->|writes| AUTO
TUI_CRATE -->|reads same data| AUTO
TUI_CRATE -->|reads| MANUAL
TUI_CRATE -->|uses types| CORE_CRATE
MCP_CRATE -->|reads| AUTO
MCP_CRATE -->|reads| MANUAL
MANUAL -. "human edits" .-> MANUAL
AGENTS -. "defines boundaries" .-> MANUAL
AUTO -->|mdbook src| MDBOOK
MANUAL -->|mdbook src| MDBOOK
MDBOOK -->|renders| BOOK
OPENCODE <-->|JSON-RPC| MCP_CRATE
TERMINAL <-->|interactive| TUI_CRATE
style CLI_CRATE fill:#4a9eff,color:#fff
style TUI_CRATE fill:#9b59b6,color:#fff
style MCP_CRATE fill:#ff6b6b,color:#fff
style AUTO fill:#ff8
style MANUAL fill:#f8f
style BOOK fill:#8f8
The two repos
cerebro at ~/Projects/cerebro
The source code repository. Contains:
- Command-line tool binary at
crates/cerebro/: Scrapes data sources and generates markdown - Shared types at
crates/cerebro-core/: Config, ProjectContext, Storage trait, collectors, queries - Model Context Protocol, or MCP, server at
crates/cerebro-mcp/: JSON-Remote Procedure Call, or RPC, server for OpenCode - Terminal User Interface, or TUI, at
crates/cerebro-tui/: Ratatui terminal UI for interactive browsing - Documentation at
docs/: mdBook docs about the cerebro tool itself
Your cortex
The cortex, named whatever you choose: cortex in this project’s case. Contains:
- config.toml: Project list and settings. Read by cerebro.
- content/: mdBook source. A mix of auto-generated and manual content.
- book/: Rendered HTML output
- AGENTS.md: Defines edit boundaries for AI agents
- book.toml: mdBook configuration
The four binaries
cerebro, the command-line tool
cerebro build # Scrape repos, generate markdown
cerebro build --fresh # Ignore cache
cerebro build --project X # Single project
cerebro status # Show cache status
cerebro serve # Spawn mdbook in output_dir
cerebro serve --port N # Custom port
cerebro mcp # Start MCP server
cerebro tui # Launch terminal UI
Data flow:
- Reads
config.tomlfrom your cortex - For each project: scrapes git, queries OpenCode DB, scans TODOs, reads manual notes
- Writes markdown to
output_dir. The default is./content. - Cache stored alongside config to avoid redundant scraping
cerebro-mcp, the Model Context Protocol server
# Started by OpenCode via opencode.json:
# "command": ["cerebro", "mcp"]
# Or standalone:
cerebro-mcp # Reads CORTEX_PATH env var; falls back to ~/Projects/cortex
Tools provided:
read_project: Combined generated + manual content for a projectlist_projects: All tracked projects with metadataread_journal: Journal entry by dateread_today: Today’s journal entryread_intent: Goals by period. Options are daily, weekly, monthly, or yearly.search_todos: Search TODOs across projectsget_stats: Overall statistics
Path resolution:
CORTEX_PATHenv var. Optional: points to the cortex data dir; falls back to~/Projects/cortexif unset.- Reads generated content from
{CORTEX_PATH}/content/ - Reads manual notes from
{CORTEX_PATH}/content/notes/and{CORTEX_PATH}/content/intent/
cerebro-tui, the Terminal UI
Interactive terminal UI for browsing projects without opening a browser. Shares
cerebro-core types with the command-line tool and reads the same cortex content
that mdbook renders.
Views:
- Dashboard: Project overview with summary cards and scrollable project table
- Projects: Tabbed Sessions/Commits/TODOs per project, detail modals, manual note editor
- Journal: Date-grouped entries with activity indicators, entry editor
- TODOs: Cross-project table with project/keyword filter cycling, text search
- Config: Project list with active toggle, add-project form, Tom’s Obvious Minimal Language, or TOML, persistence
See TUI Architecture for the component pattern and data flow.
Content boundaries
The cortex’s content/ has a strict split between auto-generated and manual content:
Auto-generated: cerebro writes, humans don’t edit
| File | Content |
|---|---|
index.md | Dashboard homepage with project cards |
projects/{name}.md | Per-project status pages |
journal/{year}/{mm}/{dd}.md | Daily activity logs |
today.md | Last 24 hours of activity |
this-week.md | Last 7 days of activity |
Manual: humans write, cerebro doesn’t modify
| File | Content |
|---|---|
intent/daily/ | Daily goals |
intent/weekly/ | Weekly goals |
intent/monthly/ | Monthly goals |
intent/yearly/ | Yearly goals |
notes/ | Evergreen notes and documentation |
notes/projects/ | Per-project manual notes |
SUMMARY.md | mdBook navigation structure |
An AGENTS.md file in your cortex enforces the boundary, instructing AI agents
which files are safe to overwrite and which are human-only.
Cron workflow
0 5,9 * * * cerebro build && mdbook build
59 23 * * * cerebro build && mdbook build
Runs from your cortex. Scrape → generate → render, three times daily.
Shared type system
All binaries share types from cerebro-core:
ProjectConfig : Name, repo_path, active flag
ProjectContext : Full context: commits, sessions, todos, notes
ManualNotes : Status, next actions, journal excerpt
Config : Settings + project list
Cache : Timestamps for incremental scraping
The TUI uses these same types to render views, ensuring consistency between what you see in the browser via mdbook, the terminal via the TUI, and what OpenCode reads via MCP.
Data flow pipeline
flowchart TD
subgraph Config["Configuration"]
C[config.toml]
end
subgraph Collect["Collectors"]
O[OpenCode] --> S[Session history]
G[Git] --> CMT[Commits, files]
T[TODOs] --> TC[TODO/FIXME comments]
N[Notes] --> MN[Manual notes]
end
subgraph Generate["Generators"]
D[Dashboard] --> MD[Markdown]
J[Journal] --> MJ[Markdown]
P[Periodic] --> MP[Markdown]
PR[Project] --> MPR[Markdown]
end
subgraph Out["Output"]
ODIR[content/]
end
C --> Collect
Collect --> Generate
Generate --> Out
Collection sources
| Source | Data Collected |
|---|---|
| OpenCode | Session history, AI interactions |
| Git | Commits, file changes, branch activity |
| TODOs | TODO, FIXME, HACK, XXX comments |
| Manual Notes | Status, journal entries, intent |
Crate details
cerebro
The main command-line tool binary. Entry point that:
- Parses command-line arguments via clap
- Coordinates collection and generation
- Manages the file-based cache
- Spawns the TUI and MCP server as subcommands
crates/cerebro/src/
├── main.rs # Entry point, clap subcommands
├── cli.rs # CLI argument definitions
├── config.rs # Configuration loading
├── cache.rs # Cache management
└── generators/ # Output generation (dashboard, project, journal, periodic)
cerebro-core
Shared library used by all other crates. Contains:
- Types:
ProjectConfig,ProjectContext,ManualNotes,Config,Cache - Traits:
Storage,Git,Databasefor dependency injection - Collectors: OpenCode, Git, TODO, and manual notes collection implementations
- Queries: Read operations used by command-line tool subcommands and MCP tools
crates/cerebro-core/src/
├── lib.rs # Type definitions, re-exports
├── storage.rs # Storage, Git, Database traits
├── collectors/ # Data collection implementations
└── queries/ # Read operations (projects, journal, todos, intent, stats)
cerebro-mcp
Model Context Protocol, or MCP, server for OpenCode integration. Provides JSON-RPC tools that allow AI assistants to query cortex data.
crates/cerebro-mcp/src/
├── server.rs # MCP server setup and event loop
└── tools/ # Tool implementations (re-export cerebro-core queries)
cerebro-tui
Ratatui terminal UI for interactive project browsing. Uses the Component + Action pattern with async tokio event loop.
crates/cerebro-tui/src/
├── app.rs # App struct, initialization
├── action.rs # Action enum, View, KeyBinding, InputMode
├── tui.rs # Terminal setup, event loop
├── cli.rs # TUI-specific CLI args
├── errors.rs # Error handling
├── lib.rs # Library re-exports
└── components/ # UI components (home, dashboard, projects, journal, todos, config, sidebar, help, widgets)
Architecture diagrams
Architectural diagrams for cerebro, following the C4 model and Simon Brown’s diagramming principles.
Created with D2. Source files live in diagrams/assets/current/.
For standalone SVG generation, such as for sharing, run just diagrams.
Current architecture
System context, C4 level 1
Shows cerebro, the developer, external systems such as git repos and the OpenCode DB, the cortex data directory, and AI agents connected via MCP.
Note: mdbook is an implementation detail of cortex, rendering
content/tobook/, not a consumer. The real consumers are AI agents, such as OpenCode and Pi, via the MCP server, and the developer via the Terminal User Interface.
Source: diagrams/assets/current/01-system-context.d2
Data flow pipeline
4-phase pipeline: scrape, process, store, consume. Shows how data flows from external sources through collectors and generators into the cortex, then to consumers, including AI agents via MCP and the Terminal User Interface.
Source: diagrams/assets/current/02-data-flow.d2
Component structure, C4 level 3
Crate-level overview with module detail layers.
| Layer | Description |
|---|---|
| Crate Overview | 4 crates + cross-crate dependencies |
| cerebro modules | Command-line tool binary module structure |
| cerebro-core modules | Shared types library |
| cerebro-mcp modules | MCP server module structure |
| cerebro-tui modules | Terminal User Interface binary module structure |
Source: diagrams/assets/current/03-components.d2
Type model
Core types from cerebro-core and their relationships.
Source: diagrams/assets/current/04-data-model.d2
Diagram conventions
All diagrams follow Simon Brown’s principles:
- Title: “Diagram Type: Scope” on every diagram
- Legend: Explains shapes, colors, line styles
- Directional arrows: One-way only, with specific labels
- Explicit types:
[Person],[Software System],[Container],[Component] - Stand-alone: Makes sense without a presenter
All diagrams import nord-config.d2 for consistent Nord dark theming via D2’s theme-overrides system.
Terminal User Interface architecture
The cerebro TUI, in crates/cerebro-tui, is a Ratatui-based terminal app that
provides interactive browsing of project activity data. It uses the
Component + Action architecture pattern recommended by the Ratatui community.
Architecture pattern
Component trait
All UI components implement the Component trait:
#![allow(unused)]
fn main() {
pub trait Component {
fn init(&mut self, area: Size) -> Result<()>;
fn handle_events(&mut self, event: Option<Event>) -> Result<Option<Action>>;
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>>;
fn handle_mouse_event(&mut self, mouse: MouseEvent) -> Result<Option<Action>>;
fn update(&mut self, action: Action) -> Result<Option<Action>>;
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()>;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn help_entries(&self) -> Vec<HelpEntry>;
}
}
Each component encapsulates its own state, including scroll positions, filter
selections, and text input focus, and communicates with other components through
the Action dispatch system.
Action dispatch
The Action enum is the single communication channel between components and the Home orchestrator:
Component ──Action──> Home ──Action──> Component
Actions fall into these categories:
| Category | Examples |
|---|---|
| Lifecycle | Tick, Render, Resize, Quit, ClearScreen |
| Data loading | DataLoaded, LoadError, TriggerRefresh, RefreshComplete |
| Navigation | SwitchView, SelectProject, SwitchTab |
| UI state | ShowPreview, ClosePreview, SetInputMode |
| Config | ToggleProjectActive, SubmitAddProject, ConfigUpdated |
| Build | TriggerBuild, BuildOutput, BuildComplete, CloseBuildLog |
| Text input | StartJournalEntry, StartManualNote, SaveInput, CancelInput |
Key binding system
A context-based system resolves key bindings to prevent collisions:
| Context | Keys | Purpose |
|---|---|---|
| Global | q, r, b, ?, Space, 1-5 | Always active: quit, refresh, build, help, leader key, view switching |
| Sidebar | j/k, arrows, Tab, l | Navigate sidebar list, switch focus to content |
| Content | Tab, Shift+Tab, h | Switch focus back to sidebar |
The leader key, Space, followed by d/p/j/t/c provides an alternative view switching path.
Component hierarchy
Home (orchestrator)
├── Sidebar (project list, view navigation)
├── Dashboard (summary cards, project table)
├── Projects (tabbed sessions/commits/todos, note editor)
├── Journal (date-grouped entries, entry editor)
├── Todos (filterable table, search)
├── Config (project toggle list, add-project form)
├── Help (keybinding overlay)
└── Widgets (shared)
├── Spinner (loading indicator)
├── Toast (notifications)
├── EmptyState (no-data display)
├── SummaryCard (dashboard stats)
├── BuildLog (build output modal)
└── Modal (generic overlay)
Data flow
The TUI follows a Flux-inspired data flow:
1. Home.do_initial_load_sync() collects data from cortex
2. Action::DataLoaded(contexts) dispatched to all components
3. Each component stores relevant data in its own state
4. User interaction → Component.handle_key_event() → Action
5. Home.update() processes Action, may dispatch follow-up Actions
6. Components receive Actions via Component.update()
7. Frame render → Component.draw() reads from local state
Async operations
Long-running operations spawn tokio tasks and report back via Actions:
- Data refresh:
trigger_refresh()spawnscollect_all()in a tokio task, sendsDataLoadedorLoadErroron completion - Config writes:
spawn_config_toggle()writes TOML viatoml_edit, sendsConfigUpdated - Build:
TriggerBuildspawnscerebro buildas a subprocess, streams output viaBuildOutputevents
Input modes
The TUI supports three input modes tracked by InputMode:
| Mode | Description | Active when |
|---|---|---|
| Navigation | Default: keys control UI | Always, except during text editing |
| JournalEntry | Multi-line text editing with ratatui-textarea | Journal view, editing an entry |
| ManualNote | Multi-line text editing with ratatui-textarea | Projects view, editing status/next sections |
In editing modes, the text input widget intercepts most navigation keys. Escape
cancels input and returns to Navigation mode.
Testing strategy
The TUI uses three testing layers:
| Layer | Tool | Purpose |
|---|---|---|
| Unit tests | #[cfg(test)] | Component state transitions, action handling, key binding parsing |
| Integration tests | ratatui::backend::TestBackend | Render full app to buffer, assert on content |
| BDD tests | Cucumber, defined in tests/cucumber.rs | User-facing scenarios in Gherkin |
| System tests | Ghostty devtools | Terminal lifecycle, resize, responsiveness |
Integration tests construct Home directly, call set_cerebro_config() and
do_initial_load_sync().await, then render and assert against the buffer. BDD
tests assert against real component state, such as home.view_name(), not buffer
text.
Architecture decisions
This directory records the architectural decisions made during the development of Cerebro. Each decision follows the Nygard ADR format: context, decision, consequences.
Decisions
| # | Title | Status |
|---|---|---|
| 0001 | Record architecture decisions | Accepted |
| 0002 | Monorepo structure with four crates | Accepted |
| 0003 | Four data collectors | Accepted |
| 0004 | Markdown output with mdbook | Accepted |
| 0005 | MCP server for OpenCode | Accepted |
| 0006 | Cache strategy | Accepted |
| 0007 | TOML configuration format | Accepted |
| 0008 | TODO regular expression pattern | Accepted |
| 0009 | Storage trait for DI | Accepted |
| 0010 | Why Cerebro uses MCP | Accepted |
1. Record architecture decisions
Date: 2026-04-22
Status
Accepted
Context
The project needs to record the architectural decisions made on this project.
Decision
Architecture Decision Records are used, as described by Michael Nygard in his article “Documenting Architecture Decisions.”
Consequences
See Michael Nygard’s article, linked in the preceding section. For a lightweight ADR toolset, see Nat Pryce’s adr-tools.
2. Monorepo structure with four crates
Date: 2026-04-22
Status
Accepted
Context
Cerebro needs to support multiple use cases: command-line tool usage for building dashboards, library usage for other tools, OpenCode MCP integration, and an interactive terminal UI for browsing project data.
Decision
This project uses a Rust workspace with four crates:
- cerebro: command-line tool, the main binary
- cerebro-core: Shared types, traits, collectors, and queries, the library crate
- cerebro-mcp: MCP server for OpenCode integration, a binary crate
- cerebro-tui: Ratatui terminal UI for interactive browsing, a binary crate
Consequences
Pros
- Clear separation between command-line tool, library, MCP, and Terminal User Interface concerns
- other tools can use cerebro-core as a library
- Each crate can evolve independently
- Terminal User Interface shares types with command-line tool, ensuring consistency across delivery surfaces
Cons
- Workspace complexity
- More build artifacts to manage
Notes
See crates/ directory for actual structure.
3. Four data collectors
Date: 2026-04-22
Status
Accepted
Context
Cerebro aggregates signals from development activity to generate context. The design must specify which data sources to collect from.
Decision
Cerebro collects data from four sources:
| Source | Implementation | Data |
|---|---|---|
| OpenCode | SQLite via opencode_db_path | Session history |
| Git | git2 crate | Commits, modified files |
| TODOs | Regex scan on source files | TODO, Needs Fixing, Hack, and Placeholder comments |
| Manual Notes | notes/projects/{name}.md | Status, journal, intent |
Consequences
Pros
- Comprehensive signal aggregation
- Each collector is independent
- Manual notes provide human context
Cons
- Git collection can be slow on large repos
- TODO regular expression may false-positive on strings
Notes
The collectors/mod.rs module orchestrates the collectors. Each is async and can run in parallel.
4. Markdown output with mdbook serving
Date: 2026-04-22
Status
Accepted
Context
Cerebro generates a personal dashboard. The design must determine the output format and serving method.
Decision
Output is markdown files served via mdbook:
- Generators produce
.mdfiles - mdbook serves as a local web server
- Output location configurable via
config.toml
Output structure
{dashboard_path}/
├── index.md # Overview
├── projects/
│ └── {name}.md # Per-project pages
├── today.md # Today's activity
├── this-week.md # Weekly activity
└── journal/
└── {year}/
└── {mm}/
└── {dd}.md # Daily journal
Consequences
Pros
- Human-readable output
- Easy to version control
- mdbook provides search/navigation
Cons
- Requires mdbook for serving
- Static generation without live updates
Notes
Default port is 3000. Use cerebro serve --port X to customize.
5. MCP server for OpenCode integration
Date: 2026-04-22
Status
Accepted
Context
AI assistants (OpenCode) should be able to query the dashboard directly. This requires a tool protocol.
Decision
The cerebro-mcp crate implements an MCP server:
Tools exposed
| Tool | Purpose |
|---|---|
read_project | Read project info + generated page |
list_projects | List all tracked projects |
get_stats | Overall statistics |
read_journal | Read journal entry by date |
read_today | Read today’s journal |
read_intent | Read intent for daily, weekly, monthly, or yearly periods |
search_todos | Search TODOs across projects |
Configuration
Reads CORTEX_PATH to locate the cortex data directory; falls back to ~/Projects/cortex if unset. (See ADR-0011 for the CLI/TUI config priority.)
Consequences
Pros
- AI can access context directly
- Enables AI-assisted workflow
- Standard protocol
Cons
- MCP SDK dependency
- Server maintenance
Notes
Binary: target/release/cerebro-mcp. Configure in OpenCode’s opencode.json.
6. Cache strategy for collectors
Date: 2026-04-22
Status
Accepted
Context
Collectors (git, opencode, todos) can be expensive. Avoid re-running them on every build.
Decision
Cerebro uses file-based caching:
- Cache stored in
cache.jsonin system cache directory - Each entry tracks:
opencode_last_query,git_last_scan,todo_last_scan --freshflag bypasses cache entirely--project <name>limits to specific project
Cache Invalidation
Cerebro invalidates the cache when:
--freshflag for user override- Timestamps in
cache.jsonshow data is stale - Manual
cerebro statusshows cache status
Consequences
Pros
- Fast subsequent builds
- Respects user intent
Cons
- Stale data if TTL too long
- System cache dependency
Notes
See cache.rs for implementation.
7. TOML configuration format
Date: 2026-04-22
Status
Accepted
Context
Cerebro needs a user-friendly configuration format that supports multiple projects and settings.
Decision
Cerebro uses TOML with two sections:
[settings]
opencode_db_path = "..." # Path to AI tool's SQLite DB (optional)
output_dir = "..." # Dashboard output directory
[[projects]]
name = "project-name" # Display name
repo_path = "~/Projects/..." # Git repository path
active = true # Include in build
Cerebro reads manual notes from a fixed path derived from the output directory
({output_dir}/notes/projects/{name}.md) rather than a per-project note_path field.
Consequences
Pros
- TOML is human-readable and well-supported in Rust
- Clear separation between settings and project definitions
- Easy to add new fields without breaking existing configs
Cons
- Requires toml parsing dependency
~path expansion needed
Notes
Parsed in cerebro-core via toml crate. ~ expansion in config.rs.
8. TODO regular expression pattern and file scanning
Date: 2026-04-22
Status
Accepted
Context
Cerebro must find TODO, Needs Fixing, Hack, and Placeholder comments reliably in source code while avoiding false positives.
Decision
Regex pattern:
(?i)\b(TODO|FIXME|HACK|XXX)\b[\s:]*(.*)$
Features:
- Case-insensitive matching using the
(?i)flag - Word boundary anchors using
\b - Captures keyword + optional text after
:or whitespace
File scanning:
- Uses
ignore::WalkBuilderto respect.gitignore - Skips binary/non-source extensions (png, jpg, pdf, zip, etc.)
Sorting priority from highest to lowest covers Needs Fixing, then TODO, then Hack, then Placeholder.
Consequences
Pros
- Case-insensitive catches
todo:,TODO:,Todo: - Respects gitignore automatically
- Skips common false positives, such as minified JS and images
Cons
- Won’t catch multi-line comments
- Won’t catch TODO in strings without keyword prefix
Notes
See collectors/todos.rs for implementation.
9. Storage trait for dependency injection
Date: 2026-04-22
Status
Accepted
Context
Collectors need to read files and access git/OpenCode databases. Collectors must support testing without real filesystems.
Decision
Cerebro defines traits for dependency injection:
#![allow(unused)]
fn main() {
pub trait Storage: Send + Sync {
async fn exists(&self, path: &Path) -> bool;
async fn read_to_string(&self, path: &Path) -> Result<String>;
// ...
}
pub trait Git: Send + Sync {
async fn commits(&self, repo_path: &Path) -> Result<Vec<GitCommit>>;
async fn modified_files(&self, repo_path: &Path) -> Result<Vec<FileChange>>;
}
}
Implementations:
TokioStorage: Uses tokio for async file I/OGit2: Usesgit2crate for git operations, with commits limited to 20 per projectSqlxDb: Usessqlxfor OpenCode SQLite queries
Consequences
Pros
- Testable without real filesystem
- Swap implementations, such as mock storage for tests
- Clear abstraction boundaries
Cons
- Trait indirection overhead
- More complex DI setup
Notes
See cerebro-core/src/storage.rs.
Why Cerebro uses Model Context Protocol, or MCP: The right tool for the job
The problem to solve
The cerebro MCP server solves this specific problem:
Provide AI assistants with standardized, secure access to cerebro’s aggregated developer activity data to enable context-aware assistance during coding tasks.
More specifically:
- Cerebro collects and processes data from: OpenCode sessions, git commits, TODO comments, manual notes, etc.
- A cortex, located via
CORTEX_PATH(defaulting to~/Projects/cortex), stores this data - The value isn’t just in collecting it, but in making it accessible to reduce context switching
- AI assistants, such as those in OpenCode, Claude Code, and others, need to query this data to provide relevant, personalized assistance
Why MCP is the right tool
1. MCP matches the data access pattern perfectly
MCP has three core concepts that align exactly with cerebro’s needs:
Resources, for data exposure:
- Cerebro’s primary asset is its data store
- MCP Resources are perfect for exposing:
- Project information via the projects resource URI
- Journal entries via the journal resource URI with a date parameter
- Intent and goals via the intent resource URI with period and identifier parameters
- TODO items via the todos resource URI with a project filter
- Search results via the search resource URI with a query parameter
Tools, for actions:
- Beyond reading data, users may want to trigger actions:
cerebro_build- Trigger a dashboard rebuildadd_journal_entry- Add a manual noteupdate_project_status- Mark project as active/inactiverefresh_cache- Force data re-collection
Prompts, for predefined interactions:
- Common query patterns to define:
summarize_recent_activityget_project_statusfind_related_todos
2. Standards-based integration eliminates custom work
Without MCP, cerebro would need to:
- Build custom HTTP/WebSocket APIs for each assistant type
- Document and maintain separate integrations for OpenCode, Claude Code, etc.
- Handle authentication, rate limiting, and error handling per integration
With MCP:
- Implement the MCP protocol once
- Any MCP-compatible assistant can automatically discover and use cerebro’s capabilities
- OpenCode, Claude Code, and other MCP hosts handle the client-side complexity
- Single source of truth for what data/actions are available
3. Security and boundaries by design
MCP provides natural security boundaries:
- The assistant can only access what the MCP server explicitly exposes
- No risk of arbitrary database access or unintended side effects
- Tool invocations require explicit approval, configurable via permissions
- Read-only resources vs. write-capable tools are clearly distinguished
4. Layering on existing infrastructure is efficient
Cerebro already had:
- Data collection logic (OpenCode, git, TODOs)
- Processing and storage systems
- Dashboard generation algorithms
- Query capabilities for the web interface
The MCP server is a thin adaptation layer that:
- Exposes existing capabilities through standard MCP interfaces
- Requires minimal new business logic
- Leverages all the existing data processing work
- Avoids re-implementing core cerebro features
5. Enables ecosystem benefits
By adopting MCP, cerebro gains:
- Compatibility with growing list of MCP-hosted assistants
- Future-proofing as more tools adopt the standard
- Ability to benefit from MCP ecosystem improvements
- Reduced integration burden for users trying multiple assistants
Why it’s not just a Retrieval-Augmented Generation problem
While a Retrieval-Augmented Generation, or RAG, system could use cerebro’s data, Model Context Protocol is more appropriate because:
RAG is a pattern. MCP is a protocol.
- Retrieval-Augmented Generation describes how to retrieve and use context within a single Large Language Model, or LLM, interaction
- MCP defines how systems connect to provide that context and more
MCP is broader than just retrieval:
- RAG focuses on fetching relevant information for a prompt
- MCP also supports invoking actions, through tools, and defining interaction patterns, through prompts
- Cerebro isn’t just about reading data - it may want to allow assistants to trigger builds, add notes, etc.
They can work together:
- An agent could use MCP to discover what cerebro offers
- Then use RAG techniques to find the most relevant parts of large datasets
- Or use MCP resources as the data source for a RAG system
Concrete example: How an assistant uses Cerebro MCP
When a user asks in OpenCode: “What work happened yesterday on the web-project?”
- OpenCode, acting as MCP host, connects to cerebro-mcp server
- Server advertises available resources/tools via MCP protocol
- OpenCode sees resources like:
cerebro://journal/2026-04-16,cerebro://projects/web-project - OpenCode reads the journal resource for yesterday
- Optionally reads project-specific data
- Combines this with the user’s question to form a complete prompt for the LLM
- LLM responds with a summary of yesterday’s work on web-project
- If the user wants to take action, for example marking this project as inactive, OpenCode can invoke the appropriate MCP tool
Addressing the “RAG-shaped problem” intuition
You’re right that at its core, this is about making data available to augment LLM context - which is what RAG does. However:
- MCP solves the connection and discovery layer: how to get the data to the assistant
- RAG solves the relevance filtering layer: how to find the right data within a large corpus
- Cerebro needs both layers:
- MCP provides standardized access to the cerebro data store
- RAG-like techniques could help cerebro rank journal entries by relevance, or help the assistant process large result sets
MCP is the right foundational choice because it establishes the standard connection mechanism. Whether that data then gets used via simple retrieval, RAG, or other methods is a separate concern that MCP doesn’t prescribe—it simply makes the data available in a standardized way.
Conclusion
The cerebro MCP is the right tool for the job because:
- Job: Make cerebro’s developer activity data accessible to AI assistants in a standard, secure way
- MCP Fit: MCP’s Resources/Tools/Prompts map directly to cerebro’s data access and action needs
- Advantages: Standards-based, secure, leverages existing infrastructure, enables ecosystem benefits
- Not Overkill: It’s not unnecessarily complex—it’s the minimally sufficient standard protocol for this exact use case
- Complementary: Works well with RAG techniques rather than replacing them
This isn’t just following trends. It applies the right tool, MCP, to the right problem: standardized AI assistant access to structured personal data stores.
11. XDG as default config location; CORTEX_PATH becomes a legacy fallback
Date: 2026-06-06
Status
Accepted. Supersedes the implicit decision in 0007: TOML configuration format (which assumed CORTEX_PATH would point at the config).
Context
The cerebro CLI and TUI load config.toml via this priority:
CORTEX_PATH env var > --config CLI flag > XDG config dir > ./config.toml
That ordering made CORTEX_PATH the de-facto requirement: a user who didn’t set it would either find an XDG config (if one happened to exist) or fail. The README and onboarding docs reflected this by instructing users to “place config.toml in your cortex” — i.e., the directory pointed to by CORTEX_PATH.
In practice, this means the user had to:
- Decide on a cortex location.
- Set
CORTEX_PATHin their shell rc. - Restart the shell.
- Only then would the binary find the config.
If any step was missed, the binary either picked up an unintended file (the ./config.toml fallback) or errored out. The MCP server kept working because it falls back to ~/Projects/cortex, but the CLI/TUI did not — they sat silently on an absent config.
The principle of least surprise: a freshly installed tool should not require environment variables to do its job. The XDG base-directory spec already gives us a sensible default: $XDG_CONFIG_HOME/cerebro/config.toml, falling back to $HOME/.config/cerebro/config.toml when XDG_CONFIG_HOME is unset. The same path works on every platform, which avoids the surprise of dirs::config_dir() returning ~/Library/Application Support on macOS for users coming from Linux. The CORTEX_PATH mechanism was solving two unrelated problems at once (where is my config, where is my data) and the conflation was the source of the confusion.
Decision
The CLI and TUI (cerebro, cerebro-tui) use this new resolution order:
--config <path>CLI flag — explicit override, always wins.- XDG config dir (
$XDG_CONFIG_HOME/cerebro/config.toml, or$HOME/.config/cerebro/config.tomlifXDG_CONFIG_HOMEis unset) — the default. Users who do nothing get this. Same path on every platform. CORTEX_PATHenv var — legacy fallback. If a user already hasCORTEX_PATHpointing at a directory that containsconfig.toml, it still works../config.tomlin the current working directory — last-resort fallback (unchanged).
The MCP server (cerebro-mcp) is unchanged: it still uses CORTEX_PATH to locate the cortex data directory (where generated content/ lives) and falls back to ~/Projects/cortex. This is a separate concern from config and was never the source of confusion.
crates/cerebro/src/main.rs::resolve_config_path and crates/cerebro-tui/src/bin/cerebro-tui.rs::resolve_config_path both implement the new priority. Integration tests in crates/cerebro/tests/integration.rs now pass --config explicitly so they don’t pick up a developer’s real XDG config from their machine.
Consequences
Pros
- New users: install, run
cerebro build, it reads the XDG default. No env vars required. - Existing users with
CORTEX_PATHset: still work via fallback #3. - Power users / scripted runs:
--configalways wins, no surprises. CORTEX_PATHis now documented for what it is: a data-directory pointer used by the MCP server, not a config prerequisite.
Cons
- A user with both an XDG config and
CORTEX_PATHset to a different cortex will silently get the XDG config. This is the desired behavior, but it meansCORTEX_PATHno longer “wins” — release notes should call this out. - Tests now depend on
--configbeing passed; future test authors must remember to do so. The existing integration tests were updated as part of this change. - The example
config.tomlin the README was reworked; any external tutorials/screenshots showing the old “put it in your cortex” pattern are stale.
Migration
For users currently relying on CORTEX_PATH to point at their config:
# Old: config was at $CORTEX_PATH/config.toml
# New: move it to the XDG location
mv "$CORTEX_PATH/config.toml" ~/.config/cerebro/config.toml
# CORTEX_PATH can stay set (MCP still uses it) or be unset if you accept the default
For users with no config at all, run cerebro build once after install and follow the “config not found” error message — it points at the XDG path.
Notes
- The
dirscrate handleshome_dir(); we readXDG_CONFIG_HOMEourselves with$HOME/.configas the fallback so the resolution is identical on every platform. We intentionally do NOT usedirs::config_dir(), which on macOS returns~/Library/Application Supportand would surprise users coming from Linux. - The integration tests previously relied on
CORTEX_PATHfor config; they now pass--configexplicitly. Unit tests inmain.rs::testswere already structured to not depend on a real XDG config and required no changes. - The MCP server’s use of
CORTEX_PATHis intentionally not touched — it serves a different purpose (data dir, not config).