- Created solid.md to document the SOLID principles for clean code practices. - Created tdd.md to outline Test Driven Development principles and practices. - Added kana-rust-backend-best-practice.md as a reference guide for building a Rust backend using Axum and SeaORM. - Established push-flow-convention.md to enforce pre-commit and pre-push hooks with versioning rules. - Introduced AGENTS.md to provide guidance on best practices and available commands for Kilo. - Configured kilo.json to include new skills and agents for enhanced functionality. - Added lefthook.yml for managing git hooks to ensure code quality and adherence to conventions.
157 lines
10 KiB
Markdown
157 lines
10 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
Tests use `#[cfg(test)] mod tests` blocks inline in production files (not a separate `tests/` dir).
|
|
|
|
Tracing output goes to `~/.local/share/zesdex/zesdex.log`. Set `RUST_LOG=debug` for verbose logging.
|
|
|
|
## Architecture Overview
|
|
|
|
Zesdex is an autonomous AI coding agent with a TUI — an OpenAI/Anthropic-compatible LLM client wrapped in a tool-use harness with 37 built-in tools.
|
|
|
|
Detailed architecture documentation is in `docs/CODEMAPS/`:
|
|
|
|
| File | Covers |
|
|
|------|--------|
|
|
| [`docs/CODEMAPS/architecture.md`](docs/CODEMAPS/architecture.md) | System layout, process modes, data flow, key files |
|
|
| [`docs/CODEMAPS/backend.md`](docs/CODEMAPS/backend.md) | Provider, OAuth, IPC, workflow engine, MCP, review, bg bash |
|
|
| [`docs/CODEMAPS/frontend.md`](docs/CODEMAPS/frontend.md) | TUI render pipeline, 16 overlays, toasts, input handling |
|
|
| [`docs/CODEMAPS/data.md`](docs/CODEMAPS/data.md) | Persistence, SQLite msglog, memory files, settings/config |
|
|
| [`docs/CODEMAPS/dependencies.md`](docs/CODEMAPS/dependencies.md) | 23 Rust crates, 5 external services |
|
|
|
|
`docs/runs/` holds an auto-generated audit trail: one markdown file per hive-mind convergence (see below), written deterministically by `app::workflow::docs::write_hive_mind_convergence` — not hand-maintained like `docs/CODEMAPS/`.
|
|
|
|
### Key Patterns
|
|
|
|
- **State mutation** — `AppStateRest` is mutable in-place from `actions/mod.rs` and `controller/input.rs`. No generic update function.
|
|
- **No DI** — modules call `Settings::load()`, `AppConfig::load()`, `all_tools()` directly.
|
|
- **Logging** — `tracing::warn!` to `~/.local/share/zesdex/zesdex.log` (not stderr, avoids TUI corruption).
|
|
- **Error handling** — `anyhow::Result` and `anyhow::bail!` throughout. No custom error types.
|
|
- **Static strings** — MCP tool descriptions use `Box::leak` + `OnceLock` cache.
|
|
- **Tools** — `trait Tool { fn name() -> &str, fn run() -> Result<String> }`, 28 impls, gated by `Harness`.
|
|
- **Shell safety** — `tool/shell_filter/` blocks destructive git commands (`shell_filter::git::check_git_destructive`, called from `tool/shell.rs::Bash::run`). It also contains a `check_credential_read` detector for credential-file reads, but that one is intentionally NOT wired into `Bash::run` today — see the doc comment on `Bash::run` for why.
|
|
|
|
### Hive-Mind Orchestration (Machine Intelligence)
|
|
|
|
- **A single Core Intelligence spawning anonymous processing nodes.** The Core Intelligence (main agent) compiles a cognitive cycle plan per task: an ordered list of cycles, each cycle a set of processing nodes that run in parallel. Each node's sole identity is its directive (what to do) and an access tier. Cycle count and nodes-per-cycle are entirely Core-Intelligence output.
|
|
- **Access tiers** in `src/app/subagent/division.rs` (`tool_scope` module): tool access is granted per node via one of three tiers (`read` / `write` / `full`, see `tool_scope::tools_for`) picked by the Core Intelligence based on what each node's directive actually needs.
|
|
- **Orchestrator** in `src/app/workflow/hive_mind.rs`: `run_hive_mind()` executes a `CognitiveCyclePlan { cycles: Vec<Vec<NodeDirective>> }` cycle-by-cycle. Node IDs are system-assigned coordinates (e.g. `"Node-0-1"`).
|
|
- **Continuous collective state, not phase-boundary sync**: `engine::execute_primitive`'s `ScopedAgent` arm merges each node's complete output into the shared collective-state channel the instant that node finishes — not after its whole parallel cohort completes — so sibling/later nodes see it in real time.
|
|
- **Consensus synthesis, not a per-node summary**: after all cycles complete, `synthesize_consensus()` spawns one final read-only node whose sole directive is to reconcile the entire collective state into a single consensus assessment — a real reasoning pass, not string concatenation, since node outputs can overlap or conflict.
|
|
- **Auto-trigger** in `run_agent_turn()` (`actions/mod.rs`): `is_complex_request()` heuristics decide only whether to ask the Core Intelligence to compile a plan at all — the plan's shape is fully dynamic.
|
|
- **`hive_mind` tool** (`src/tool/workflow.rs`) is the manual entry point: the calling LLM supplies its own `cycles` array of `{directive, access}` directly.
|
|
- **Guaranteed documentation**: after every convergence, `src/app/workflow/docs.rs::write_hive_mind_convergence()` deterministically (not an LLM step, not skippable) writes every node's full output plus the final consensus to `docs/runs/<timestamp>-<slug>.md`.
|
|
- **Live node progress** in TUI panel (`view/workflow.rs`): shows node designation + current tool via `AgentStatus::progress`.
|
|
- **Auto inline review** after each edit: `src/app/subagent/auto.rs` — `spawn_quick_review()` injects verdict back into LLM conversation.
|
|
- **Background subagents** (test-gen, arch-review, security-review) fire asynchronously at turn end via `TurnEvent::SystemNote`, retrying once on failure and escalating to a blocking (`ESCALATED:`-prefixed, `ToastKind::Error`) notice if the retry also fails.
|
|
|
|
---
|
|
|
|
## Best Practices (Kana Engineering Standards)
|
|
|
|
This project follows Kana Engineering Best Practices. The following skills are loaded and enforced:
|
|
|
|
| Skill | Location | Purpose |
|
|
|-------|----------|---------|
|
|
| `clean-code` | `.claude/skills/clean-code/SKILL.md` | Clean Code principles (naming, functions, classes, comments) |
|
|
| `commit-convention` | `.claude/skills/commit-convention/SKILL.md` | Conventional Commits (Bahasa Indonesia) |
|
|
| `push-flow-convention` | `.claude/skills/push-flow-convention/SKILL.md` | Pre-commit/pre-push hooks via lefthook |
|
|
| `kana-rust-backend-best-practice` | `.claude/skills/kana-rust-backend-best-practice/SKILL.md` | Rust clean-architecture patterns (Axum, SeaORM, etc.) |
|
|
|
|
### Layering Rules
|
|
|
|
```
|
|
domain/ → application/ → infrastructure/ → interfaces/ → gateway/
|
|
(inward) (outward)
|
|
```
|
|
|
|
- **Domain** (Layer 0): Pure entities, value objects, repository/service traits. ZERO external framework deps.
|
|
- **Application** (Layer 1): Use-case services (one per file), port traits. Depends ONLY on domain.
|
|
- **Infrastructure** (Layer 2): Concrete implementations of domain traits (SQLite, JSON files, LLM clients, LSP servers, MCP).
|
|
- **Interfaces** (Layer 3): Presentation adapters — TUI (ratatui), API (Axum), WebSocket, daemon, gRPC, web.
|
|
- **Gateway**: Composition root — the only place that wires all layers together.
|
|
|
|
**Critical:** Domain must NEVER import application, infrastructure, or interfaces. Application must NEVER import infrastructure or interfaces.
|
|
|
|
### Commit Convention (Bahasa Indonesia)
|
|
|
|
All commits follow Conventional Commits in Bahasa Indonesia:
|
|
|
|
```
|
|
feat(tool): add batch file delete
|
|
fix(ipc): reconnect loop on socket timeout
|
|
chore: bump reqwest to 0.13
|
|
docs: add architecture diagram to README
|
|
refactor(harness): flatten guard pipeline
|
|
```
|
|
|
|
Types: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `style`, `perf`, `ci`. All types produce a release (patch minimum). Add `BREAKING CHANGE:` for major bumps.
|
|
|
|
### Clean Code Principles
|
|
|
|
- **Functions under ~40 lines**, one level of abstraction, extracted till you drop.
|
|
- **No flag arguments** — split `render(true)` into `renderForSuite()` / `renderForSingleTest()`.
|
|
- **Command-Query Separation** — function either does or answers, never both.
|
|
- **No switch/if-else on type** — replace with factory + polymorphism.
|
|
- **No null returns** — use `Option<T>` or empty collections.
|
|
- **No magic numbers** — extract named constants.
|
|
- **DRY** — no duplication.
|
|
- **Tell, Don't Ask** — don't fetch state then decide; tell the object to work.
|
|
- **Boy Scout Rule** — leave every module cleaner than you found it.
|
|
|
|
### Error Handling
|
|
|
|
- `anyhow::Result` and `anyhow::bail!` throughout (except domain layer typed errors).
|
|
- `tracing::warn!` / `tracing::error!` for logging. NEVER stderr (corrupts TUI).
|
|
- Never `.unwrap()` or `.expect()` in production code — use `?` or proper error handling.
|
|
- Log expected failures at `warn!`, unexpected errors at `error!`.
|
|
|
|
### Testing
|
|
|
|
- `#[cfg(test)] mod tests` blocks inline in production files.
|
|
- Tests are F.I.R.S.T. — Fast, Independent, Repeatable, Self-validating, Timely.
|
|
- Use `Result<()>` as test return type for `?` propagation.
|
|
- Mock at boundaries only; prefer fakes for owned abstractions.
|
|
|
|
### Compiler Bypasses
|
|
|
|
NEVER use `#[allow(...)]`, `#[expect(...)]`, or `#[allow(dead_code)]`. Fix the underlying code instead.
|
|
|
|
## Code Documentation
|
|
|
|
Every function, struct, enum, trait, module, and significant code block must have a doc comment (`///` or `//!`) that explains:
|
|
|
|
- **What** the function/module does (purpose, not how)
|
|
- **Flow** — a brief ASCII or prose description of the code flow / data flow above each non-trivial function
|
|
- **Why** — non-obvious decisions, edge cases, invariants
|
|
- **Return** — what the caller gets back, especially for `Result` types
|
|
|
|
Examples:
|
|
|
|
```rust
|
|
/// Parse an SSE data chunk into one or more StreamEvents.
|
|
///
|
|
/// Flow: buffer → split on '\n' → flush on blank line → JSON parse → match event type
|
|
/// → return Token / ToolCallDelta / Usage / Done.
|
|
///
|
|
/// Edge case: chunk may split mid-line; remaining bytes stay in buffer
|
|
/// for the next feed() call.
|
|
fn feed(&mut self, chunk: &str) -> Vec<StreamEvent> { ... }
|
|
|
|
/// The single source-of-truth state struct for the entire application.
|
|
///
|
|
/// Mutated in-place from two locations: actions/mod.rs (apply_action)
|
|
/// and controller/input.rs (key event handlers). Read-only from
|
|
/// every other module.
|
|
struct AppStateRest { ... }
|
|
```
|
|
|
|
Rules:
|
|
- Every `pub fn` needs a doc comment
|
|
- Every `pub struct` / `pub enum` / `pub trait` needs a doc comment
|
|
- Non-trivial private functions (≥10 lines) need a doc comment
|
|
- Write the comment above the code it documents (not inline in the body)
|
|
- Update comments when code behavior changes — stale docs are worse than no docs
|
|
- NEVER use compiler/linter bypass annotations or attributes (such as `#[allow(clippy::too_many_lines, clippy::too_many_arguments, clippy::ref_option)]`, `#[allow(dead_code)]`, etc.) to silence warnings or skip linter checks. Always fix the underlying code issues instead.
|