# CODEBUDDY.md
This file provides guidance to CodeBuddy Code when working with code in this repository.
## Build & Development Commands
```bash
# Build (debug)
cargo build
# Build (release)
cargo build --release
# Build with browser CDP support (optional feature)
cargo build --release --features browser_cdp
# Run (debug, with CDP feature)
cargo run --features browser_cdp
# Install release binary to /usr/local/bin/j
make install
# Format code
cargo fmt
# Lint (clippy, warnings as errors)
cargo clippy -- -D warnings
# Run tests
cargo test
# Run all tests including feature-gated ones
cargo test --all-features
# Type check without building
cargo check
# Pre-commit (format + lint + test)
make pre-commit
# Bump patch version in Cargo.toml
make bump-version
```
## Project Overview
**j-cli** is a fast macOS ARM64 CLI productivity tool written in Rust. Binary name is `j`. User data lives in `~/.jdata/` (configurable via `J_DATA_PATH`).
Core features: alias management, daily reports with git sync, TUI todo system, full TUI AI chat with streaming/tool calls, browser automation (Lite HTTP mode + optional CDP), extensible skill & hook systems, and an interactive REPL mode.
## Rust Edition & Toolchain
- **Edition**: 2024
- **Minimum Rust**: 1.93.1
- **Patched dependency**: `tui-textarea` 0.7.0 lives in `patches/tui-textarea-0.7.0/`
- **Optional feature**: `browser_cdp` — enables Chromium automation via `chromiumoxide`
## Source Architecture
```
src/
├── main.rs # Entry point: load config → interactive mode OR clap dispatch
├── cli.rs # Clap derive definitions (Cli struct, SubCmd enum with 28 variants)
├── constants.rs # VERSION and global constants
├── assets.rs # rust-embed for bundled assets
├── config/
│ └── yaml_config.rs # YamlConfig: 11 BTreeMap sections, YAML serde, file locking
├── interactive/ # REPL mode (rustyline + Tab completion + history)
├── tui/ # Shared TUI editor widget (used by report + scripts)
├── util/ # Shared utilities
└── command/
├── mod.rs # dispatch(SubCmd, &mut YamlConfig) router
├── handler.rs # command_handlers! macro — generates handler structs
├── open.rs # Alias opening logic (path/URL/script/browser/editor dispatch)
├── alias.rs # set/rm/rename/modify alias commands
├── report.rs # Daily report system (TUI editor, git sync, search)
├── todo/ # TUI todo app (app.rs state machine + ui.rs rendering)
└── chat/ # AI chat subsystem (30+ files, see below)
```
### Command Dispatch Flow
```
main() → Cli::try_parse()
├── Ok(Some(SubCmd)) → command::dispatch() → handler for each subcommand
├── Ok(None) / Err(_) → command::open::handle_open() (alias fallback)
└── No args → interactive::run_interactive() (REPL)
```
When clap fails to parse (e.g. `j chrome`), the tool falls back to alias-based open logic — this is intentional.
### Chat Module Architecture (`command/chat/`)
The chat module is the largest subsystem (~30 files). It follows a **Redux-like unidirectional data flow**:
```
KeyEvent → handler() → Action → app.update() → state mutation → render
StreamMsg → poll_stream_actions() → Vec<Action> → app.update() → state mutation → render
```
**Key structs in `app.rs`**:
| `ChatApp` | Top-level container, owns all state |
| `UIState` | ~55 frontend fields (input, cursor, scroll, popups, mode, caches) |
| `ChatState` | ~7 backend fields (agent_config, session messages, streaming content, loaded skills) |
| `ToolExecutor` | 9 fields + 8 methods (active tool calls, confirmation state, execution channels) |
| `AgentHandle` | 2 fields + 3 methods (stream_rx channel, cancellation token) |
**Action enum** (`app.rs`): ~85 variants covering input, popups, streaming, tool execution, ask-tool interaction, navigation, config editing, archiving, and UI management. The central `update(Action)` method dispatches all state mutations.
**Main event loop** (`handler/tui_loop.rs`): 5-phase architecture:
1. **Tick** — toast expiry
2. **Poll backend** — `poll_stream_actions()` from agent + `poll_tool_exec_results()` from tool threads → Actions
3. **Render** — `draw_chat_ui()` (only if `needs_redraw`)
4. **Collect input** — from InputThread channel, batch-process → Actions
5. **Side effects** — full-screen editors for system prompt / style
**ChatMode enum** (9 modes): Chat, SelectModel, Browse, Help, Config, ArchiveConfirm, ArchiveList, ToolConfirm, ToolToggle/SkillToggle.
**Chat submodule layout**:
```
command/chat/
├── app.rs # ChatApp + UIState + ChatState + ToolExecutor + Action enum + update()
├── mod.rs # run_chat() entry point
├── agent.rs # Background async agent loop (tokio, streams LLM responses)
├── api.rs # OpenAI SDK wrapper (async-openai), message format conversion
├── compact.rs # Context window compaction (micro_compact + auto_compact)
├── permission.rs # .jcli permission rule matching (allow/deny/allow_all)
├── skill.rs # Skill loading from ~/.jdata/agent/skills/
├── hook.rs # 3-level hook system (user/project/session), 8 event types
├── storage.rs # Persistence: agent_config.json, chat_history.json, archives
├── theme.rs # 6 built-in themes (Dark, Light, Dracula, Gruvbox, Monokai, Nord)
├── render_cache.rs # Per-message rendering cache with throttled streaming
├── input_thread.rs # Spawns thread for crossterm event reading (non-blocking)
├── autocomplete.rs # @skill and @file: popup completion
├── archive.rs # Chat session archive/restore
├── constants.rs # Chat-specific constants
├── ui_helpers.rs # Shared UI helper functions
├── handler/ # Key event handlers per mode (all emit Actions)
│ ├── tui_loop.rs # Main 5-phase event loop
│ ├── chat_handler.rs # Chat mode key bindings
│ ├── browse_handler.rs
│ ├── config_handler.rs
│ └── ...
├── ui/ # Rendering (all read-only, take &ChatApp)
│ ├── chat.rs # Main chat UI drawing (messages, input, status)
│ ├── config.rs # Config screen rendering
│ ├── archive.rs # Archive list UI
│ └── ...
├── markdown/ # Markdown → ratatui spans (pulldown-cmark + syntax highlighting)
│ └── highlight.rs # Language-specific syntax highlighting
└── tools/ # Tool implementations
├── mod.rs # ToolRegistry + Tool trait definition
├── shell.rs # ShellTool (Bash command execution)
├── file.rs # Read/Write/Edit/Glob tools
├── grep.rs # GrepTool (ripgrep-style search)
├── browser.rs # BrowserTool (CDP + Lite dual mode)
├── web_fetch.rs # WebFetchTool (HTTP + HTML→markdown)
├── web_search.rs # WebSearchTool (search API)
├── ask.rs # AskTool (interactive questionnaire)
├── background.rs # BackgroundRun + CheckBackground
├── task/ # TaskCreate/List/Get/Update tools
├── compact.rs # CompactTool (context compression trigger)
└── ...
```
### Tool System
Tools implement the `Tool` trait:
```rust
pub trait Tool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn parameters_schema(&self) -> Value; // JSON Schema for LLM
fn execute(&self, arguments: &str, cancelled: &Arc<AtomicBool>) -> ToolResult;
fn requires_confirmation(&self) -> bool;
fn confirmation_message(&self, arguments: &str) -> String;
}
```
Tools are registered in `ToolRegistry`. The LLM generates `tool_calls` from the schema; the main thread shows confirmation UI (unless auto-allowed by `.jcli` permissions), then spawns OS threads for execution. Results flow back via channels.
### Concurrency Model
The chat system uses **channels** for cross-thread communication:
- `stream_rx`: Agent thread → Main (streaming chunks, tool call requests, done/error)
- `tool_result_tx`: Main → Agent thread (tool execution results)
- `tool_exec_rx`: Tool OS threads → Main (execution completion)
- `ask_request_rx`: AskTool → Main (interactive question requests)
Shared mutable state uses `Arc<Mutex<T>>` or `Arc<AtomicBool>` for: streaming content, queued tasks, pending user messages, and tool cancellation flags.
### Config Layer
- **`YamlConfig`** (`config/yaml_config.rs`): Main app config with 11 `BTreeMap<String,String>` sections (path, inner_url, outer_url, browser, editor, vpn, script, setting, log, report, version). Uses `serde_yaml` + `fs2` file locking.
- **`AgentConfig`** (`chat/storage.rs`): AI chat config (providers, active model, system prompt, tools_enabled, theme, etc.). Stored as JSON at `~/.jdata/agent/data/agent_config.json`.
- **`.jcli`** (project root): YAML permissions file for tool auto-approval rules. Program searches upward from cwd.
### Permissions (`.jcli`)
Rule format: `ToolName`, `Bash(prefix:*)`, `Write(path:dir/*)`, `WebFetch(domain:x)`. Deny rules take priority over allow rules. `allow_all: true` bypasses all confirmation.
### Skill System
Skills live in `~/.jdata/agent/skills/<name>/SKILL.md` with YAML frontmatter (name, description, argument-hint). The system prompt includes skill summaries; the AI calls `load_skill` to inject full instructions. Skills can have `references/` and `scripts/` subdirectories.
### Hook System
Three levels (user `~/.jdata/agent/hooks.yaml`, project `.jcli` hooks section, session via `register_hook` tool). 8 events: pre/post_send_message, pre/post_llm_request, pre/post_tool_execution, session_start/end. Hooks receive JSON via stdin, return JSON modifications via stdout.
## Key Conventions
- All Chinese UI text is used throughout (error messages, help text, log output) — maintain this convention
- The `debug_log!` macro is used for verbose logging (controlled by `j log mode verbose/concise`)
- The project uses a patched `tui-textarea` — changes to that dependency require modifying `patches/tui-textarea-0.7.0/`
- No unit tests currently exist in `src/`; testing is done via CLI integration
- The `.claude/settings.local.json` contains Claude Code tool permission rules — distinct from the `.jcli` runtime permissions