Package Structure
Oculus is organized as a collection of focused Go packages under pkg/, with the binary entry point in cmd/oculus/.
cmd/oculus/ CLI entry point (cobra)
pkg/
├── api/ Anthropic API client (SSE streaming, retry)
├── auth/ OAuth PKCE, keychain, trust dialog
├── bridge/ Multi-provider bridge (Anthropic, OpenAI, Ollama, CLI)
├── commands/ 54 slash commands
├── config/ Settings, loader, merge pipeline, validation
├── constants/ Product info, models, limits, OAuth
├── context/ System prompt, OCULUS.md, git helpers
├── entrypoints/ SDK, print, headless, MCP server modes
├── hooks/ Hook engine (command, HTTP, lifecycle)
├── lens/ Focus/Scan/Craft lens routing
├── memdir/ Auto-memory directory
├── migrations/ Data migration framework
├── query/ Conversation loop + tool dispatch
├── services/
│ ├── analytics/ Event logging
│ ├── bridge/ Remote session protocol
│ ├── compact/ Context compaction (RLM-inspired TF-IDF)
│ ├── cost/ Token cost tracking
│ ├── episodes/ Episode store + LCM context loop
│ ├── history/ Conversation history
│ ├── mcp/ MCP client (stdio + HTTP)
│ ├── memory/ Session memory extraction
│ ├── notifications/ OS notifications
│ ├── plugins/ Plugin loader
│ ├── policylimits/ Enterprise policy
│ ├── ratelimit/ Rate limit tracking
│ ├── sessions/ Session persistence
│ ├── tokencount/ Token estimation
│ └── toolsummary/ Tool use summaries
├── skills/ Skill loader
├── state/ Thread-safe AppState store
├── task/ Background task system
├── tool/ Tool interface + BaseTool
├── tools/ 40 tool implementations
├── tui/ Bubbletea terminal UI (16 files)
├── types/ Core types (message, permission, hooks)
└── utils/ Utilities (permissions, git, bash, format, array)
Lens System
The three-lens architecture routes different types of work to specialized model configurations. Each lens has its own model, provider, and persona — so you can use Opus for deep planning (Focus), Gemini Flash for fast exploration (Scan), and a local Ollama model for code edits (Craft).
User Message → LensRouter → Focus/Scan/Craft
↓
LensWorker (per-lens model + persona)
↓
Bridge (Anthropic/OpenAI/Ollama/CLI)
↓
API Response → Tool Dispatch → Results
The LensRouter classifies each turn based on the active tool set and category routes — you don't need to specify a lens manually unless you want to override the default.
Query Engine
The core conversation loop in pkg/query/ drives every turn:
- Build API request (messages + system prompt + tools)
- Stream response via SSE
- If
tool_use: dispatch tools (concurrent for safe tools) - Collect results, re-submit to API
- Track episodes, check context thresholds
- Repeat until
end_turnormax_tokens
Episode-Based Context Management
Conversations are persisted as typed episodes. As context grows, episodes transition through compaction stages instead of being truncated — preserving the history while keeping the active window lean.
Active Episode → Messages accumulate
↓ (τ_soft threshold)
Compacted Episode → Summary + keywords (messages freed)
↓ (age/count threshold)
Archived Episode → Deep compression
Episodes are also the handoff format between lenses. When Focus delegates to Scan, the episode summary is passed as context — the sub-lens gets the gist without the noise.