AGENTS.md
AGENTS.mdAGENTS.mdroot
Quality
96/100
Scores the file, not the repository.Length
1,567 words
21 headings · 9 code blocksRepository
30
— · pushed 39 days agoLast changed
3 days ago
First indexed 3 days ago.1# AGENTS.md - FlyCrys23Instructions for AI agents working on this codebase.45## Rules67- **NEVER use deprecated GTK4 APIs.** No TreeView, TreeStore, CellRenderer*, ListStore (deprecated one). Use modern replacements: ListView, ColumnView, TreeListModel, SignalListItemFactory, TreeExpander, gio::ListStore. No `#[allow(deprecated)]` annotations.89- **ALWAYS check latest GTK4 API docs before using any GTK API.** Do NOT rely on memory or training data — GTK4 APIs change between versions and many are deprecated. Use Context7 (`resolve-library-id` + `query-docs` for `gtk4-rs`) or fetch the official docs at https://docs.rs/gtk4 and https://docs.gtk.org/gtk4/ to verify that the API you're about to use is current and not deprecated. When in doubt, look it up.1011- **Handle errors properly — show the reason to the user.** Never silently swallow errors. Follow these rules:1213 **In UI code:** Show meaningful error messages to the user. Use `gtk::AlertDialog` (NOT the deprecated `MessageDialog`):14```rust15 // Correct pattern — AlertDialog with detail16 let dialog = gtk::AlertDialog::builder()17 .message("Operation failed")18 .detail(&format!("Could not open file: {err}"))19 .build();20 dialog.show(Some(&window));21```22 For errors inside the agent panel, use `agent_widgets::create_system_message()` to display the error inline with the reason.2324 **In service code:** Return `Result<T, String>` (or a proper error type) with a descriptive message that includes the underlying cause. Never discard the `why`:25```rust26 // Bad — reason is lost27 fs::read_to_string(path).ok();28 command.spawn().ok();2930 // Good — reason is propagated31 fs::read_to_string(path).map_err(|e| format!("Failed to read {}: {e}", path.display()))?;32```3334 **Specifically:**35 - `eprintln!()` is acceptable for debug/logging but is NOT a substitute for user-visible feedback36 - `.unwrap()` is only acceptable on infallible operations (e.g., GTK widget downcasts inside factory callbacks where the type is guaranteed). Never on I/O, parsing, or network operations.37 - `.ok()` that discards a result is a code smell — justify it with a comment or handle the error38 - For async dialog callbacks (`FileDialog`, `AlertDialog`), check for `GtkDialogError::Dismissed` — that's the user choosing to cancel, not an error worth reporting3940## Tech Stack4142- **Language**: Rust (edition 2024)43- **UI toolkit**: GTK4 via `gtk4` crate (v0.10) with `v4_12` feature44- **Terminal**: VTE4 via `vte4` crate (v0.9)45- **Highlighting**: `syntect` for code, `pulldown-cmark` for markdown46- **Serialization**: `serde` + `serde_json` for config persistence and CLI protocol47- **Platform**: `dirs` crate for XDG-compliant config paths48- **System deps**: `libgtk-4-dev`, `libvte-2.91-gtk4-dev`49- **Target OS**: Linux only (Debian/Ubuntu, Fedora, Arch)5051## Architecture5253### Layered Architecture (MUST follow)5455The codebase is organized into four layers. **Each layer may only depend on layers below it. Never the reverse.**5657```58Layer 3 — UI (src/ui/, src/workspace.rs, src/main.rs, src/agent_widgets.rs, ...)59 | GTK widgets, presentation, user interaction60 v61Layer 2 — Services (src/services/)62 | Business logic, I/O, CLI backends, platform ops (NO GTK imports)63 v64Layer 1 — Models (src/models/)65 | Pure data structures (no I/O, no GTK, no business logic)66 v67Layer 0 — Config (src/config/)68 Constants, domain enums, theme definitions (no imports from above)69```7071**Dependency rules:**72- `config/` imports nothing from the project (only std and serde)73- `models/` imports only from `config/`74- `services/` imports from `models/` and `config/` (NEVER from `ui/` or GTK)75- `ui/` and top-level UI modules import from any layer below7677### Module Map7879```80src/81 config/ LAYER 0: Constants, types, theme82 constants.rs All magic numbers, known editors/browsers, file type maps,83 quick commands — THE source of truth for tunable values84 types.rs Domain enums: Theme, ViewMode, DiffMode, NotificationLevel,85 AgentOutcome, TreeItemKind86 theme.rs CSS generation from Theme enum8788 models/ LAYER 1: Pure data structures89 app_config.rs AppConfig (window state, theme, notifications)90 workspace_config.rs WorkspaceConfig (pane sizes, view mode, agent sessions),91 RunTabConfig, RunTabType (run panel tab persistence)92 agent_config.rs AgentConfig (name, system prompt, tools, model)93 chat.rs ChatMessage enum (user, assistant, tool, system)9495 services/ LAYER 2: Business logic and I/O96 storage.rs Config/session/agent persistence (load, save, delete, list)97 platform.rs OS interaction: xdg-open, editor/browser detection, default shell98 git.rs Git CLI operations: status, diff, GitFileStatus enum99 cli/100 mod.rs AgentBackend trait, AgentDomainEvent enum, AgentSpawnConfig,101 ImageAttachment — the CLI-agnostic abstraction102 claude.rs Claude CLI implementation: process spawning, wire types (private),103 stream-json parsing, event translation to domain events104105 ui/ LAYER 3: GTK UI (sub-modules)106 agent_panel/107 mod.rs create_agent_panel() — panel construction and wiring108 state.rs PanelState decomposed: AgentProcessState, TokenState,109 ChatState, PanelConfig110 event_handler.rs handle_domain_event() — AgentDomainEvent -> UI updates111112 session.rs Thin re-export layer (models + storage) for convenience113 main.rs App entry point, window setup, tab management, settings popover114 workspace.rs Workspace container: paned layout, file tree, editor, run panel, agent115 run_panel.rs Tabbed Run Panel: multi-terminal tabs + background task tracking116 agent_widgets.rs Chat message widget builders (user, assistant, tool, system)117 agent_config_dialog.rs Agent profile CRUD dialog118 git_panel.rs Git status/diff UI panel (calls services/git.rs)119 textview.rs File viewer with source/preview modes120 highlight.rs Syntax highlighting via syntect (data-driven extension map)121 markdown.rs Markdown to Pango markup converter122 terminal.rs VTE4 terminal utilities (colors, spawn, scrollback save/restore)123 tree.rs File tree (ListView + TreeListModel)124 file_entry.rs GObject subclass for tree model items125 watcher.rs File system change watcher126```127128### CLI Abstraction129130The UI layer communicates with agent CLIs through **domain events only**:131132```133UI (agent_panel) <--- AgentDomainEvent <--- AgentBackend trait <--- ClaudeBackend134 (services/cli/claude.rs)135```136137- `AgentDomainEvent` is CLI-agnostic: `TextDelta`, `ToolStarted`, `TokenUsage`, `TaskNotification`, `Finished`, etc.138- Claude wire types (`ClaudeEvent`, `StreamEventData`, `ContentBlock`, `Delta`) are **private** to `claude.rs`139- UI code MUST NOT match on Claude-specific strings (`"content_block_delta"`, `"message_start"`, etc.)140- The `AgentBackend` trait defines: `spawn`, `send_message`, `pause`, `resume`, `stop`, `is_alive`141142### Key Patterns143144- **GObject model**: `FileEntry` uses `mod imp` pattern with `ObjectSubclass`, `Properties` derive, `glib::wrapper!`145- **TreeListModel lazy loading**: `create_func` returns `Some(child_ListStore)` for directories, `None` for files146- **Agent subprocess I/O**: Reader thread + `std::sync::mpsc::channel` + `glib::timeout_add_local(16ms)` polling for GTK main loop integration. Backend translates wire events to domain events in the reader thread.147- **Streaming markdown**: Track current `gtk::Label`, accumulate text deltas, re-render full markdown->Pango on each delta148- **Signal closures**: `glib::clone!` with `#[weak]`/`#[strong]`. Shared mutable state via `Rc<RefCell<>>`149- **Drag-and-drop**: `DragSource` on ListView provides path as `glib::Value`. `DropTarget` on agent input accepts string drops.150151## Coding Rules (MUST follow)152153### No Magic Numbers154155Every numeric literal, timeout, dimension, threshold, or buffer size MUST be a named constant in `config/constants.rs`. The only exceptions are:156- Trivial values: `0`, `1`, `-1` as initial/sentinel values157- GTK widget spacing (4, 6, 8, 10, 12) which are standard GTK defaults158- Loop bounds derived from data (`0..len`)159160**Bad:**161```rust162terminal.set_scrollback_lines(10000);163glib::timeout_add_local(Duration::from_secs(5), move || { ... });164container.set_width_request(420);165```166167**Good:**168```rust169terminal.set_scrollback_lines(TERMINAL_SCROLLBACK_LINES);170glib::timeout_add_local(Duration::from_secs(AUTOSAVE_INTERVAL_SECS), move || { ... });171container.set_width_request(AGENT_PANEL_MIN_WIDTH);172```173174### No Hardcoded Lists175176Lists of known values (editors, browsers, file extensions, MIME types, quick commands) MUST live in `config/constants.rs` as static arrays. Code iterates over them — never inline the items.177178### Enums Over Booleans179180When a field represents a mode, preference, or status — use an enum, not a bool. Booleans are only for genuinely binary states (like "is the widget visible right now" in transient UI logic).181182**Bad:** `is_dark: bool`, `preview_mode: bool`, `show_diff: bool`183**Good:** `theme: Theme`, `view_mode: ViewMode`, `diff_mode: DiffMode`184185Domain enums live in `config/types.rs`. They must derive `Copy` (for use in `Cell<T>`), `Serialize`, `Deserialize`, and have helper methods (e.g., `Theme::is_dark()`, `Theme::toggle()`).186187### Data-Driven Extensibility188189When you add support for a new file type, editor, browser, quick command, or similar — add it to the corresponding constant array. Never add a new arm to a match statement that hardcodes specific values.190191### Layer Discipline192193- **New CLI-specific code** goes in `services/cli/claude.rs` (or a new backend module)194- **New OS/desktop interaction** goes in `services/platform.rs`195- **New git operations** go in `services/git.rs`196- **New persistence logic** goes in `services/storage.rs`197- **New data types** go in `models/`198- **New UI widgets** go in `ui/` or top-level UI modules199- Never import GTK types in `config/`, `models/`, or `services/`200201### Struct Decomposition202203Large state structs should be decomposed into focused sub-structs grouped by responsibility. See `ui/agent_panel/state.rs` for the pattern: `AgentProcessState`, `TokenState`, `ChatState`, `PanelConfig` composed into `PanelState`.204205### Function Size206207Keep functions under ~100 lines. If a function grows larger, extract helper functions. The event handler pattern in `ui/agent_panel/event_handler.rs` is the example: one top-level match, each arm delegates to focused logic.208209## Build Commands210211```bash212cargo build # debug build213cargo run # run debug214cargo build --release # release build215cargo test # run all tests216cargo fmt # format code217cargo fmt -- --check # check formatting without modifying218cargo clippy # lint219```220221## Git Hooks222223Pre-commit hook runs `cargo fmt --check`, `cargo clippy -- -D warnings`, and `cargo test`.224The hook lives in `hooks/pre-commit` (tracked in the repo).225226```bash227# Enable hooks (once per clone):228git config core.hooksPath hooks229```230231## Conventions232233- Use `gtk4 as gtk` alias throughout234- Import `gtk::prelude::*` for extension traits235- Use `glib::clone!` macro with `#[weak]`/`#[strong]` for signal closures236- GObject property names use kebab-case (e.g., `"icon-name"`, `"is-dir"`)237- Minimize `unsafe` — only for `libc::kill` in process management238- Use `pub(crate)` for internal items; only `pub` when needed across crate boundary239- Serde structs: use `#[serde(default)]` and `Option<>` liberally, `#[serde(other)]` catch-all for unknown variants240241## When Making Changes242243- Run `cargo check` after every structural change — GTK binding errors can be cryptic244- Run `cargo fmt` before committing245- If adding new GTK4 features, check if they require a version feature flag (e.g., `v4_14`)246- New tuneable values go in `config/constants.rs` with descriptive names247- New domain types/enums go in `config/types.rs`248- New file type support: add to `SYNTAX_ALIASES` and/or `HIGHLIGHTABLE_EXTENSIONS` in constants249- Agent event handling: ONLY match on `AgentDomainEvent` variants, never Claude wire types250- When creating new list/tree widgets, use ListView/ColumnView + gio::ListStore + SignalListItemFactory251- Agent event deserialization must be lenient — unknown fields/types should be skipped gracefully252
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| trick77/agents-md-syncAGENTS.md · 2 | AGENTS.md | setupbuildteststyle+5 | 100/100 | 3 days ago | |
| SkeneTechnologies/skene-cookbookAGENTS.md · 51 | AGENTS.md | setupbuildtestlint-format+7 | 100/100 | 2 days ago | |
| duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70 | AGENTS.md | buildteststylearch+3 | 100/100 | 3 days ago | |
| wpscanteam/wpscanAGENTS.md · 9.7k | AGENTS.md | setupbuildteststyle+6 | 100/100 | 2 days ago | |
| OnlyTerp/prompt-cache-skillsAGENTS.md · 112 | AGENTS.md | setupbuildtestlint-format+5 | 100/100 | 3 days ago | |
| mui/material-uiAGENTS.md · 99k | AGENTS.md | setupbuildtestlint-format+9 | 100/100 | 3 days ago | |
| n8n-io/n8npackages/@n8n/agents/AGENTS.md · 199k | AGENTS.md | buildteststylearch+3 | 100/100 | 3 days ago | |
| aaif-goose/gooseAGENTS.md · 52k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 3 days ago |
