BREAKING CHANGES: - owlen-core no longer depends on ratatui/crossterm - RemoteMcpClient constructors are now async - MCP path validation is stricter (security hardening) This commit resolves three critical issues identified in project analysis: ## P0-1: Extract TUI dependencies from owlen-core Create owlen-ui-common crate to hold UI-agnostic color and theme abstractions, removing architectural boundary violation. Changes: - Create new owlen-ui-common crate with abstract Color enum - Move theme.rs from owlen-core to owlen-ui-common - Define Color with Rgb and Named variants (no ratatui dependency) - Create color conversion layer in owlen-tui (color_convert.rs) - Update 35+ color usages with conversion wrappers - Remove ratatui/crossterm from owlen-core dependencies Benefits: - owlen-core usable in headless/CLI contexts - Enables future GUI frontends - Reduces binary size for core library consumers ## P0-2: Fix blocking WebSocket connections Convert RemoteMcpClient constructors to async, eliminating runtime blocking that froze TUI for 30+ seconds on slow connections. Changes: - Make new_with_runtime(), new_with_config(), new() async - Remove block_in_place wrappers for I/O operations - Add 30-second connection timeout with tokio::time::timeout - Update 15+ call sites across 10 files to await constructors - Convert 4 test functions to #[tokio::test] Benefits: - TUI remains responsive during WebSocket connections - Proper async I/O follows Rust best practices - No more indefinite hangs ## P1-1: Secure path traversal vulnerabilities Implement comprehensive path validation with 7 defense layers to prevent file access outside workspace boundaries. Changes: - Create validate_safe_path() with multi-layer security: * URL decoding (prevents %2E%2E bypasses) * Absolute path rejection * Null byte protection * Windows-specific checks (UNC/device paths) * Lexical path cleaning (removes .. components) * Symlink resolution via canonicalization * Boundary verification with starts_with check - Update 4 MCP resource functions (get/list/write/delete) - Add 11 comprehensive security tests Benefits: - Blocks URL-encoded, absolute, UNC path attacks - Prevents null byte injection - Stops symlink escape attempts - Cross-platform security (Windows/Linux/macOS) ## Test Results - owlen-core: 109/109 tests pass (100%) - owlen-tui: 52/53 tests pass (98%, 1 pre-existing failure) - owlen-providers: 2/2 tests pass (100%) - Build: cargo build --all succeeds ## Verification - ✓ cargo tree -p owlen-core shows no TUI dependencies - ✓ No block_in_place calls remain in MCP I/O code - ✓ All 11 security tests pass Fixes: #P0-1, #P0-2, #P1-1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
OWLEN is a Rust-powered, terminal-first interface for interacting with local and cloud language models. It uses a multi-provider architecture with vim-style navigation and session management.
Status: Alpha (v0.2.0) - core features functional but expect occasional bugs and breaking changes.
Build, Test & Development Commands
Building
# Build all crates
cargo build
# Build release binary
cargo build --release
# Run the TUI (requires Ollama running)
./target/release/owlen
# or
cargo run -p owlen-cli
# Build for specific target (cross-compilation)
dev/local_build.sh x86_64-unknown-linux-gnu
Testing
# Run all tests
cargo test --all
# Test specific crate
cargo test -p owlen-core
cargo test -p owlen-tui
cargo test -p owlen-providers
# Linting and formatting
cargo clippy --all -- -D warnings
cargo fmt --all -- --check
# Pre-commit hooks (install once with `pre-commit install`)
pre-commit run --all-files
Developer Tasks
# Regenerate screenshots for documentation
cargo xtask screenshots
cargo xtask screenshots --no-png # skip PNG generation
cargo xtask screenshots --output images/
# Regenerate repository map after structural changes
scripts/gen-repo-map.sh
# Platform compatibility checks
scripts/check-windows.sh # Windows GNU toolchain smoke test
Running Individual Tests
# Run a specific test by name
cargo test test_name
# Run tests with output
cargo test -- --nocapture
# Run tests in a specific file
cargo test --test integration_test_name
Architecture & Key Concepts
Workspace Structure (Cargo workspace with 13+ crates)
- owlen-core: Core abstractions, provider traits, session management, MCP client layer (UI-agnostic)
- owlen-tui: Terminal UI built with ratatui (event loop, rendering, vim modes)
- owlen-cli: Entry point that parses args, loads config, launches TUI or headless flows
- owlen-providers: Concrete provider adapters (Ollama local, Ollama Cloud)
- owlen-markdown: Markdown parsing and rendering
- crates/mcp/: Model Context Protocol infrastructure
- llm-server: Wraps owlen-providers behind MCP boundary (generate_text tools)
- server: Generic MCP server for file ops and workspace tools
- client: MCP client implementation
- code-server: Code execution sandboxing
- prompt-server: Template rendering
- xtask: Development automation tasks (screenshots, etc.)
Dependency Boundaries
- owlen-core is the dependency ceiling: Must stay free of terminal logic, CLIs, or provider HTTP clients
- owlen-cli only orchestrates startup/shutdown: Business logic belongs in owlen-core or library crates
- owlen-mcp-llm-server is the only crate that directly talks to providers: UI/CLI communicate through MCP clients
Multi-Provider Architecture
[owlen-tui / owlen-cli]
│
│ chat + model requests
▼
[owlen-core::ProviderManager] ──> Arc<dyn ModelProvider>
│ ▲
│ │ implements ModelProvider
▼ │
[owlen-core::mcp::RemoteMcpClient] ────────┘
│ (JSON-RPC over stdio)
▼
┌────────────────────────────────────────────────┐
│ MCP Process Boundary (spawned per provider) │
│ │
│ crates/mcp/llm-server ──> owlen-providers::* │
└────────────────────────────────────────────────┘
Key points:
- ProviderManager tracks health, merges model catalogs, and dispatches requests
- RemoteMcpClient bridges MCP protocol to ModelProvider trait
- MCP servers isolate provider-specific code in separate processes
- Health & availability tracked via background workers and surfaced in TUI picker
Event Flow & TUI Architecture
- User input → Event loop → Message handler → Session controller → Provider manager → Provider
- Non-blocking design: TUI remains responsive during streaming (see
agents.mdfor planned improvements) - Modal workflow: Normal, Insert, Visual, Command modes (vim-inspired)
- AppMessage stream carries async events (provider responses, health checks)
Session & Conversation Management
- Conversation (owlen-core): Holds messages and metadata
- SessionController: High-level orchestrator managing history, context, model switching
- Conversations stored in platform-specific data directory (can be encrypted with AES-GCM)
Configuration
Platform-specific locations:
- Linux:
~/.config/owlen/config.toml - macOS:
~/Library/Application Support/owlen/config.toml - Windows:
%APPDATA%\owlen\config.toml
Commands:
owlen config init # Create default config
owlen config init --force # Overwrite existing
owlen config path # Print config location
owlen config doctor # Migrate legacy configs
Coding Conventions
Commit Messages
Follow Conventional Commits:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types: feat, fix, docs, style, refactor, test, chore, build, ci
Example: feat(provider): add support for Gemini Pro
Pre-commit Hooks
Hooks automatically run on commit (install with pre-commit install):
cargo fmtcargo checkcargo clippy --all-features- File hygiene (trailing whitespace, EOF newlines)
To bypass (not recommended): git commit --no-verify
Style Guidelines
- Run
cargo fmtbefore committing - Address all
cargo clippywarnings - Use
#[cfg(test)]modules for unit tests in same file - Place integration tests in
tests/directory
Provider Development
Adding a New Provider
Follow docs/adding-providers.md:
- Implement
ModelProvidertrait inowlen-providers - Set
ProviderMetadata::provider_type(Local/Cloud) - Register with
ProviderManagerin startup code - Optionally expose through MCP server
- Add integration tests following
crates/owlen-providers/testspattern - Document config in
docs/configuration.mdand defaultconfig.toml - Update
README.md,CHANGELOG.md,docs/troubleshooting.md
See docs/provider-implementation.md for trait-level details.
MCP Tool Naming
Enforce spec-compliant identifiers: ^[A-Za-z0-9_-]{1,64}$
- Use underscores or hyphens (e.g.,
web_search,filesystem_read) - Avoid dotted names (legacy incompatible)
- Qualify with
{server}__{tool}when multiple servers overlap (e.g.,filesystem__read)
Repository Automation
OWLEN includes Git-aware automation for code review and commit templating:
CLI Commands
# Generate commit message from staged diff
owlen repo commit-template
owlen repo commit-template --working-tree # inspect unstaged
# Review branch or PR
owlen repo review
owlen repo review --owner Owlibou --repo owlen --number 42 --token-env GITHUB_TOKEN
TUI Commands
:repo template # inject commit template into chat
:repo review [--base BRANCH] [--head REF] # review local changes
Key Files & Entry Points
Main Entry Points
crates/owlen-cli/src/main.rs- CLI entry point (argument parsing, config loading)crates/owlen-tui/src/app/mod.rs- Main TUI application and event dispatchcrates/owlen-core/src/provider.rs- ModelProvider trait definition
Configuration & State
crates/owlen-core/src/config.rs- Configuration loading and parsingcrates/owlen-core/src/session.rs- Session and conversation managementcrates/owlen-core/src/storage.rs- Persistence layer
Provider Infrastructure
crates/owlen-providers/src/ollama/- Ollama local and cloud providerscrates/mcp/llm-server/src/main.rs- MCP LLM server processcrates/owlen-core/src/mcp/remote_client.rs- MCP client implementation
Testing Strategy
Unit Tests
Place in #[cfg(test)] modules within source files for isolated component testing.
Integration Tests
Place in tests/ directories:
crates/owlen-providers/tests/- Provider integration tests- Test registration, model aggregation, request routing, health transitions
Focus Areas
- Command palette state machine
- Agent response parsing
- MCP protocol abstractions
- Provider manager health cache
- Session controller lifecycle
Documentation Structure
README.md- User-facing overview, installation, featuresCONTRIBUTING.md- Contribution guidelines, development setupdocs/architecture.md- High-level architecture (read first!)docs/repo-map.md- Workspace layout snapshotdocs/adding-providers.md- Provider implementation checklistdocs/provider-implementation.md- Trait-level provider detailsdocs/testing.md- Testing guidedocs/troubleshooting.md- Common issues and solutionsdocs/configuration.md- Configuration referencedocs/platform-support.md- OS support matrix
Important Implementation Notes
When Working on TUI Code
- Modal state machine is critical: Normal ↔ Insert ↔ Visual ↔ Command
- Status line shows current mode (use as regression check)
- Non-blocking event loop planned (see
agents.md) - Command palette state lives in
owlen_tui::state - Follow Model-View-Update pattern for new features
When Working on Providers
- Never import providers directly in owlen-tui or owlen-cli
- All provider communication goes through owlen-core abstractions
- Health checks run on background workers
- Model discovery fans out through ProviderManager
When Working on MCP Integration
- RemoteMcpClient implements both MCP client traits and ModelProvider
- MCP servers are short-lived, narrowly scoped binaries
- Tool calls travel same transport as chat requests
- Consent prompts surface in UI via session events
Platform Support
- Primary: Linux (Arch AUR:
owlen-git) - Supported: macOS 12+ (requires Command Line Tools for OpenSSL)
- Experimental: Windows (GNU toolchain, some Docker features disabled)
Cross-platform testing: Use dev/local_build.sh and scripts/check-windows.sh
Dependencies & Async Runtime
- Async runtime: tokio with "full" features
- TUI framework: ratatui 0.29 with palette features
- HTTP client: reqwest with rustls-tls (no native-tls)
- Database: SQLx with sqlite, tokio runtime
- Serialization: serde + serde_json
- Testing: tokio-test for async test utilities
Security & Privacy
- Local-first: LLM calls route through local Ollama by default
- Session encryption: Set
privacy.encrypt_local_data = truefor AES-GCM storage - No telemetry sent
- Outbound requests only when explicitly enabling remote tools/providers
- Config migrations carry schema version and warn on deprecated keys