10 KiB
Everjoy — Agent Onboarding Guide
This file is written for AI coding agents. It assumes you know Rust but nothing about this project. For background, also read
doc/Purpose.MD,doc/Architecture.md,doc/Architecture-Runtime-and-Plugin.md,doc/Plugin-Lifecycle-Tool-vs-Provider.md, andTECH_STACK.md.
Project Overview
Everjoy is an AI-native TUI IDE / workflow tool written in Rust. It is currently at version 0.1.0 and is best described as an early MVP:
- A terminal user interface built with
ratatui+crossterm. - Three-panel layout: Sidebar (file tree), Main (editor / terminal), Agent (chat).
- A runtime architecture based on providers, tools, and an explicit
Commandpattern. - An agent framework skeleton (roles, orchestrator, memory, tool invoker) that is not yet fully wired to an LLM.
- A plugin subsystem designed for external
.wasm/.soextensions, but the loader and translator are currently stubs.
The project is organized as a single Cargo crate. There is no pyproject.toml, package.json, or other build system — Rust/Cargo is the only toolchain you need.
Key Configuration Files
Cargo.toml— Single crate namedeverjoy, edition2021. Lists all production dependencies (TUI, async, serialization, SQLite, text/files, config, utils).[dev-dependencies]is empty.Cargo.lock— Committed; pin your dependency versions..gitignore— Standard Rust ignore (/target).
No custom rustfmt.toml or clippy.toml was found; use the default toolchain settings.
Build and Run
# Compile
cargo build
# Run the TUI application
cargo run
# Fast check
cargo check
# Release build
cargo build --release
The binary runs in the terminal using the alternate screen. Current controls:
Ctrl+Q— quit.Ctrl+J/Ctrl+K/Ctrl+L— focus Sidebar / Main / Agent panel.Alt+<underlined letter>— open menubar.- Press any key from the splash screen to enter the main UI.
Test Commands
cargo test
There are 23 unit tests, all passing. They live in inline #[cfg(test)] modules inside the built-in providers:
src/builtin/editor/tests.rssrc/builtin/filetree/tests.rssrc/builtin/agentchat/tests.rssrc/builtin/terminal/tests.rs
There are no integration tests or benchmarks yet. Keep it that way unless a feature genuinely needs them.
Code Organization
src/
main.rs # App struct, event loop, command dispatch, terminal setup
lib.rs # Re-exports: agent, builtin, plugin, runtime, ui
runtime/ # Core engine interfaces and registry
mod.rs
traits.rs # Plugin, PanelProvider, Tool, Command, Source, Registrar, etc.
registry.rs # ModuleRegistry: lazy provider factories, tool/menu/status storage, bidding
lifecycle.rs # LifecycleManager (mostly TODO)
ctx.rs # RuntimeServices trait (ProviderCtx was removed)
ui/ # TUI framework helpers
mod.rs
layout.rs # Three-panel ratios (sidebar 20%, main 52%, agent 28%)
focus.rs # FocusManager across panels
menubar.rs # Top menubar with Alt+ shortcuts
statusbar.rs # Bottom status bar
splash.rs # Startup splash screen
scheduler.rs # RenderScheduler (stub)
router.rs # EventRouter (stub)
agent/ # Agent framework skeleton
mod.rs
roles.rs # Built-in roles: secretary, driver, negfeedback, executor
orchestrator.rs # Multi-agent coordinator (stub)
memory.rs # MemorySystem (stub)
tools.rs # ToolInvoker (stub)
plugin/ # External plugin adapter (designed but not implemented)
mod.rs
loader.rs # ExtensionLoader (stub)
translator.rs # C-ABI ↔ Rust trait wrapper (stub)
surface.rs # EjSurface / EjCell for plugin rendering
panic.rs # Panic isolation helper
builtin/ # Built-in panel providers
mod.rs
editor/mod.rs # EditorProvider (Main panel, text files)
filetree/mod.rs # FileTreeProvider (Sidebar, project navigator)
agentchat/mod.rs # AgentChatProvider (Agent panel, chat UI)
terminal/mod.rs # TerminalProvider (Main panel, mock shell)
tasktree/mod.rs # TaskTreeProvider (Main panel, task view stub)
Runtime Architecture
The project centers on the Runtime Subsystem (src/runtime/). Understand these concepts before changing anything:
-
PanelProvider— Anything that renders into one of the three panels (Sidebar,Main,Agent). Each provider has:id()— unique string id.render(area, buf)— called every frame.handle_event(event) -> (EventResult, Vec<Command>)— input handling plus explicit commands back to the app.focus_changed,hide,show,shutdown— lifecycle hooks.can_open(source, mime_hint) -> i32— bidding score; higher wins when the app needs to present content.as_any/as_any_mut— implemented so the app can downcast to concrete types (e.g. to read editor cursor state for the status bar).
-
Commandpattern — Providers do not call system APIs directly. They returnVec<Command>fromhandle_event. TheAppinmain.rsis the sole dispatcher. Commands are:Present { panel, source, mime_hint }— let providers bid for the source.Launch { panel, tool_id, params }— direct dispatch by provider id.FocusPanel(panel)— change focus.ShowMessage { msg, level }— show a transient status message.
-
ModuleRegistry— Holds provider factories, tool handlers, menu items, and status widgets. Provider instances are created lazily viaensure_instance(id). It also implementsRegistrar, which is the interface exposed during plugin bootstrap. -
Tool vs Provider lifecycle — Tools are registered once at plugin bootstrap and live until unload. Providers are created/destroyed per panel instance. This separation is intentional; see
doc/Plugin-Lifecycle-Tool-vs-Provider.md. -
Built-in vs External — Built-in modules implement Runtime traits directly. External modules are planned to be loaded by
plugin::loaderand wrapped byplugin::translatorintoBox<dyn PanelProvider>/Box<dyn Tool>.
Current Built-in Providers
| Id | Panel | Purpose |
|---|---|---|
filetree |
Sidebar | Project file navigator; emits Command::Present on Enter to open files in Main. |
editor |
Main | Simple text editor with line numbers, basic Rust highlighting, and cursor movement. |
terminal |
Main | Mock integrated terminal with command history (help, clear, pwd, ls, agents, version). |
agentchat |
Agent | Chat UI with backend selector (Claude / GPT-4 / Local), message scrolling, and input. |
tasktree |
Main | Visual stub of agent task lists. |
Default active providers are hard-coded in main.rs: Sidebar = filetree, Main = editor, Agent = agentchat.
Agent Framework
The agent code is intentionally lightweight and not yet connected to an LLM:
agent::roles::builtin_roles()defines four roles (secretary,driver,negfeedback,executor) with icons, colors, system prompts, and allowed tools.agent::tools::ToolInvokeris a stub that returnsToolOutput::err("not implemented").agent::orchestrator::Orchestrator,agent::memory::MemorySystemare also stubs.- The
Tooltrait lives inruntime::traitsand is the interface agents will call via LLM function calling.
When you add LLM integration, the expected flow is:
- Agent decides to call a tool.
ToolInvokerlooks up the tool by name inModuleRegistry::tools.Tool::execute()runs and returnsToolOutput.- Result goes back into the agent conversation loop.
Code Style Guidelines
- Follow the existing module structure: one folder per subsystem,
mod.rs+ focused files. - Implement
PanelProviderusing the same pattern as the built-ins:- Implement
as_any/as_any_mutwithself. - Return
(EventResult, Vec<Command>)fromhandle_event. - Provide lifecycle methods (
focus_changed,hide,show,shutdown).
- Implement
- Keep
main.rsas the single dispatcher forCommand. Do not re-introduce aProviderCtx-style black-box router. - Use
anyhow::Resultfor application-level error handling.thiserroris a dependency but not yet used heavily. - Prefer explicit TODO comments for missing functionality; many already exist. Do not delete them unless you actually implement the feature.
- Avoid adding heavy new dependencies without updating
TECH_STACK.mdand this file.
Testing Instructions
- Add unit tests inside
#[cfg(test)] mod tests;in the relevant module. Reference the existing test modules for style. - Use
crossterm::event::{Event, KeyCode, KeyEvent}to simulate keyboard input in tests. - Assert on
EventResult::ConsumedvsEventResult::PassThroughand on emittedCommands where appropriate. - Run
cargo testbefore finishing any change. The current baseline is 23 passed tests.
Security Considerations
- There are no secrets, API keys, or credentials in this repository.
- Future AI provider API keys are planned to live in
~/.config/everjoy/config.toml(not yet implemented). Do not commit keys. - Tool execution is intended to be guarded by
auto_execute_tools = falsein user config; dangerous operations should require user confirmation. Respect this design if you wire up tool execution. - External plugins are designed to be sandboxed via WASM / C-ABI translation with panic isolation. Until the loader and translator are implemented, no external plugins are actually loaded.
- The terminal provider is currently a mock shell. If you replace it with real
tokio::process::Commandexecution, sanitize inputs and require explicit user approval before running commands.
Useful References
doc/Purpose.MD— Why the project exists.doc/Architecture.md— High-level architecture and data flows.doc/Architecture-Runtime-and-Plugin.md— Runtime vs Plugin subsystem responsibilities.doc/Plugin-Lifecycle-Tool-vs-Provider.md— Why tools and providers have separate lifecycles.TECH_STACK.md— Dependency map and design decisions (note: some entries are aspirational).
Have fun building Everjoy. — For Hoshu Chiu & Everjoy Chiu 🐕