This commit is contained in:
liuyi2
2026-06-04 20:12:43 +08:00
parent 6b17a884e5
commit 7d05563e71
40 changed files with 814 additions and 395 deletions

197
AGENTS.md Normal file
View File

@@ -0,0 +1,197 @@
# 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`, and `TECH_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 `Command` pattern.
- 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` / `.so` extensions, 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 named `everjoy`, edition `2021`. 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
```bash
# 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
```bash
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.rs`
- `src/builtin/filetree/tests.rs`
- `src/builtin/agentchat/tests.rs`
- `src/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:
1. **`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).
2. **`Command` pattern** — Providers do not call system APIs directly. They return `Vec<Command>` from `handle_event`. The `App` in `main.rs` is 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.
3. **`ModuleRegistry`** — Holds provider factories, tool handlers, menu items, and status widgets. Provider instances are created lazily via `ensure_instance(id)`. It also implements `Registrar`, which is the interface exposed during plugin bootstrap.
4. **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`.
5. **Built-in vs External** — Built-in modules implement Runtime traits directly. External modules are planned to be loaded by `plugin::loader` and wrapped by `plugin::translator` into `Box<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::ToolInvoker` is a stub that returns `ToolOutput::err("not implemented")`.
- `agent::orchestrator::Orchestrator`, `agent::memory::MemorySystem` are also stubs.
- The `Tool` trait lives in `runtime::traits` and is the interface agents will call via LLM function calling.
When you add LLM integration, the expected flow is:
1. Agent decides to call a tool.
2. `ToolInvoker` looks up the tool by name in `ModuleRegistry::tools`.
3. `Tool::execute()` runs and returns `ToolOutput`.
4. 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 `PanelProvider` using the same pattern as the built-ins:
- Implement `as_any` / `as_any_mut` with `self`.
- Return `(EventResult, Vec<Command>)` from `handle_event`.
- Provide lifecycle methods (`focus_changed`, `hide`, `show`, `shutdown`).
- Keep `main.rs` as the single dispatcher for `Command`. Do not re-introduce a `ProviderCtx`-style black-box router.
- Use `anyhow::Result` for application-level error handling. `thiserror` is 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.md` and 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::Consumed` vs `EventResult::PassThrough` and on emitted `Command`s where appropriate.
- Run `cargo test` before 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 = false` in 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::Command` execution, 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 🐕*