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
Routing is transparent

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:

  1. Build API request (messages + system prompt + tools)
  2. Stream response via SSE
  3. If tool_use: dispatch tools (concurrent for safe tools)
  4. Collect results, re-submit to API
  5. Track episodes, check context thresholds
  6. Repeat until end_turn or max_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.