mirror of
https://github.com/instructkr/claw-code.git
synced 2026-08-25 00:00:38 +08:00
docs: add hierarchical AGENTS.md knowledge base
Root knowledge base plus complexity-scored subdirectory files for the rust/ workspace, its five highest-mass crates (runtime, rusty-claude-cli, api, tools, commands, plugins), and the src/ Python porting workspace. Generated via init-deep: 13 parallel explore agents, LSP/ast-grep code map, centrality-scored placement. Snapshot in .omo/init-deep.json (local).
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
# PROJECT KNOWLEDGE BASE
|
||||||
|
|
||||||
|
**Generated:** 2026-08-16
|
||||||
|
**Commit:** b71afdd
|
||||||
|
**Branch:** main
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
Claw Code: public Rust implementation of the `claw` CLI agent harness (Claude-Code-style). Canonical code lives in `rust/`; the repo is an agent-managed exhibit (harnesses plan/execute/verify per README), not a hand-operated product. `src/` is a companion Python porting/parity workspace, not production code.
|
||||||
|
|
||||||
|
## STRUCTURE
|
||||||
|
```
|
||||||
|
claw-code/
|
||||||
|
├── rust/ # canonical Cargo workspace: 11 crates, `claw` binary
|
||||||
|
├── src/ # Python porting workspace + reference_data/ parity snapshots
|
||||||
|
├── tests/ # Python unittest validation of src/ + scripts/ (stdlib unittest)
|
||||||
|
├── docs/ # g0XX gate verification maps + topic docs
|
||||||
|
├── scripts/ # fmt.sh, dogfood-build.sh, roadmap/board helpers
|
||||||
|
├── assets/ # README images only
|
||||||
|
└── install.sh, Containerfile, docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## WHERE TO LOOK
|
||||||
|
| Task | Location | Notes |
|
||||||
|
|------|----------|-------|
|
||||||
|
| CLI subcommands | rust/crates/rusty-claude-cli/src/main.rs | hand-rolled parser; CliAction enum ~L1162; dispatch in run() L995-1158 |
|
||||||
|
| Sessions/permissions/MCP | rust/crates/runtime/src/ | 47 flat modules |
|
||||||
|
| Provider clients | rust/crates/api/src/providers/ | anthropic.rs + openai_compat.rs |
|
||||||
|
| Tool definitions | rust/crates/tools/src/lib.rs | 55-tool spec table L484-1348 |
|
||||||
|
| Slash commands | rust/crates/commands/src/lib.rs | 120+ spec table L60-1047 |
|
||||||
|
| Plugins/hooks | rust/crates/plugins/src/ | manifest = .claude-plugin/plugin.json |
|
||||||
|
| Lean agent harness | rust/crates/claw-analog/src/lib.rs | lib+bin; tool loop over api+runtime |
|
||||||
|
| RAG HTTP service | rust/crates/claw-rag-service/src/ | axum; SQLite + optional Qdrant |
|
||||||
|
| Test mock server | rust/crates/mock-anthropic-service/ | SCENARIO_PREFIX scripted responses |
|
||||||
|
| Python porting CLI | src/main.py | argparse: manifest, parity-audit, graphs |
|
||||||
|
| Parity reference DB | src/reference_data/subsystems/ | 29 JSON snapshots of TS archive |
|
||||||
|
|
||||||
|
## CODE MAP
|
||||||
|
| Symbol | Type | Location | Refs | Role |
|
||||||
|
|--------|------|----------|------|------|
|
||||||
|
| Session | struct | runtime/src/session.rs:117 | 229 | session persistence/lifecycle |
|
||||||
|
| ConfigLoader | struct | runtime/src/config.rs:409 | 83 | config schema/load |
|
||||||
|
| PluginManager | struct | plugins/src/lib.rs | 48 | plugin install/registry |
|
||||||
|
| PermissionEnforcer | struct | runtime/src/permission_enforcer.rs:27 | 35 | pre-dispatch permission gate |
|
||||||
|
| ConversationRuntime | struct | runtime/src/conversation.rs:130 | 32 | conversation loop driver |
|
||||||
|
| McpServerManager | struct | runtime/src/mcp_stdio.rs:488 | 30 | MCP JSON-RPC processes |
|
||||||
|
| HookRunner | struct | runtime/src/hooks.rs:155 | 25 | shell hook execution |
|
||||||
|
| CliAction | enum | rusty-claude-cli/src/main.rs:1162 | — | 25 subcommand variants |
|
||||||
|
| mvp_tool_specs | fn | tools/src/lib.rs:484 | — | static 55-tool table |
|
||||||
|
| SLASH_COMMAND_SPECS | const | commands/src/lib.rs:60 | — | 120+ slash commands |
|
||||||
|
|
||||||
|
(Refs = rg count across rust/crates; rust-analyzer references timed out during mapping.)
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
- `unsafe_code = "forbid"` workspace-wide; every crate opts in via `[lints] workspace = true`; clippy all=warn, pedantic=allow
|
||||||
|
- Edition 2021, resolver 2, publish=false; no rust-toolchain pin (CI floats stable); no rustfmt.toml/clippy.toml — stock defaults
|
||||||
|
- Giant flat files by design (main.rs 19.8k, tools/lib.rs 10.9k, commands/lib.rs 7.2k): organization is positional — types → spec table → dispatch → handlers → tests at EOF
|
||||||
|
- Dual output paths everywhere: `render_x` + `render_x_json`; JSON errors to **stdout**, text errors to **stderr**
|
||||||
|
- Tests: inline `#[cfg(test)] mod tests` primary; integration tests spawn `CARGO_BIN_EXE_claw` subprocess against mock-anthropic-service; tempfile everywhere; env-mutating tests serialize via env_lock/test_env_lock
|
||||||
|
- Comments carry issue numbers (#824, #146); gate tests named by roadmap gate (g004_conformance.rs)
|
||||||
|
- Python side: stdlib only, `python -m unittest`; src/ mixes camelCase (QueryEngine.py) and snake_case filenames
|
||||||
|
|
||||||
|
## ANTI-PATTERNS (THIS PROJECT)
|
||||||
|
- NEVER `cargo install claw-code` — crates.io stub is deprecated and installs `claw-code-deprecated.exe`; build from source
|
||||||
|
- Forbidden doc strings (CI-enforced by .github/scripts/check_doc_source_of_truth.py): old org links `github.com/Yeachan-Heo/claw-code`, `github.com/code-yeongyu/claw-code`, `discord.gg/6ztZB9jvWq`, `assets/clawd-hero.jpeg`
|
||||||
|
- Deprecated config keys: `permissionMode` → `permissions.defaultMode`; `enabledPlugins` → `plugins.enabled`; env `RUSTY_CLAUDE_PERMISSION_MODE` is dead
|
||||||
|
- Direct push to main is policy-blocked (`main_push_forbidden` approval scope)
|
||||||
|
- Automation lanes must not merge/close remote PRs/issues (docs/anti-slop-triage.md)
|
||||||
|
- `claw init` must not scaffold `dontAsk` permission mode (regression-pinned in output_format_contract.rs)
|
||||||
|
- File-level `#![allow(dead_code)]` blocks (main.rs, session_control.rs) are tolerated legacy — do not extend the pattern
|
||||||
|
|
||||||
|
## UNIQUE STYLES
|
||||||
|
- Dogfood build: scripts/dogfood-build.sh injects GIT_SHA; `claw version` provenance must equal HEAD
|
||||||
|
- Mock parity: rust/mock_parity_scenarios.json drives CLI subprocess vs MockAnthropicService
|
||||||
|
- Dogfooding uses `CLAW_CONFIG_HOME=$(mktemp -d)` for config isolation
|
||||||
|
- Env contracts: GIT_SHA (build), CLAW_CONFIG_HOME (config dir), OLLAMA_HOST (provider override), `*_API_KEY`/`*_BASE_URL` per provider
|
||||||
|
|
||||||
|
## COMMANDS
|
||||||
|
```bash
|
||||||
|
scripts/fmt.sh --check # fmt check (apply: scripts/fmt.sh)
|
||||||
|
cd rust && cargo clippy --workspace --all-targets -- -D warnings
|
||||||
|
cd rust && cargo test --workspace
|
||||||
|
cd rust && cargo build -p rusty-claude-cli # binary: rust/target/debug/claw
|
||||||
|
python -m unittest discover -s tests # Python suite
|
||||||
|
python .github/scripts/check_doc_source_of_truth.py && scripts/roadmap-check-ids.sh # docs/roadmap CI
|
||||||
|
```
|
||||||
|
|
||||||
|
## NOTES
|
||||||
|
- `claw` binary comes from crate `rusty-claude-cli` (package/bin name mismatch)
|
||||||
|
- rust-ci.yml triggers only on rust/**, docs/**, listed meta file changes (path filters)
|
||||||
|
- CI clippy job runs without `-D warnings` — weaker than the documented gate; known pre-existing failures recorded in docs/g002/g003 maps
|
||||||
|
- `claw acp` is a status stub, not a real ACP server
|
||||||
|
- rust/ has committed harness dotdirs (.clawd-agents/, .omc/, .sandbox-home/) — intentional
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# AGENTS.md — rust/ workspace
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
Virtual Cargo workspace (resolver 2, edition 2021) housing 11 crates that compose the `claw` CLI and supporting services.
|
||||||
|
|
||||||
|
## STRUCTURE
|
||||||
|
|
||||||
|
| Crate | Kind | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `rusty-claude-cli` | bin (`claw`) | Main CLI binary. Package name ≠ binary name. |
|
||||||
|
| `claw-analog` | lib+bin | Alternate entry point; depends on api + runtime only. |
|
||||||
|
| `claw-rag-service` | bin | RAG service. Only crate with `[features]` (`qdrant-index`). |
|
||||||
|
| `mock-anthropic-service` | lib+bin | Mock Anthropic Messages API. Prints `MOCK_ANTHROPIC_BASE_URL`. Dev-dep for CLI and analog tests. |
|
||||||
|
| `runtime` | lib | Core: sessions, permissions, MCP, conversation loop. ~47 modules. |
|
||||||
|
| `api` | lib | Provider clients: Anthropic, OpenAI-compat (xAI, OpenAI, DashScope, Ollama). |
|
||||||
|
| `tools` | lib | 55-tool surface area. Depends on `commands` (not vice versa). |
|
||||||
|
| `commands` | lib | 120+ slash commands. |
|
||||||
|
| `plugins` | lib | Plugin manifest and lifecycle. |
|
||||||
|
| `telemetry` | lib | Request identity + analytics sinks. |
|
||||||
|
| `compat-harness` | lib | Extracts upstream TS claude-code manifest/commands/tools for parity comparison. |
|
||||||
|
|
||||||
|
Dependency direction: `rusty-claude-cli` → tools/commands/runtime/api/plugins. `tools` → `commands`.
|
||||||
|
|
||||||
|
## WHERE TO LOOK
|
||||||
|
|
||||||
|
- **Parity testing**: `mock_parity_scenarios.json` at workspace root, loaded via `CARGO_MANIFEST_DIR/../../mock_parity_scenarios.json`. Scripts in `scripts/` (`run_mock_parity_harness.sh`, `run_mock_parity_diff.py`).
|
||||||
|
- **CI**: `.github/workflows/rust-ci.yml` (fmt, clippy, test, docs, Windows smoke) and `release.yml` (v* tag builds for linux-x64/macos-arm64/windows-x64).
|
||||||
|
- **Committed test fixtures**: `.clawd-agents/`, `.omc/`, `.sandbox-home/` are checked-in harness dotdirs.
|
||||||
|
- **Docs**: `PARITY.md`, `TUI-ENHANCEMENT-PLAN.md`, `README.md` alongside this file.
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
Workspace lints (all crates opt in via `[lints] workspace = true`):
|
||||||
|
- `unsafe_code` = **forbid**. No exceptions.
|
||||||
|
- clippy `all` = warn, `pedantic` = allow. Explicitly allowed: `module_name_repetitions`, `missing_panics_doc`, `missing_errors_doc`.
|
||||||
|
|
||||||
|
No `rustfmt.toml` or `clippy.toml`. Stock defaults only.
|
||||||
|
|
||||||
|
TUI rule: formatting fns take `&mut impl Write`, never stdout directly. Never mix raw ANSI escapes with crossterm.
|
||||||
|
|
||||||
|
Library crates don't carry the `claw-` prefix. Binary crates do (except legacy `rusty-claude-cli`).
|
||||||
|
|
||||||
|
Workspace version is `0.1.3`, `publish = false`, MIT license.
|
||||||
|
|
||||||
|
No rust-toolchain file, no MSRV. CI pins `dtolnay/rust-toolchain@stable`.
|
||||||
|
|
||||||
|
## ANTI-PATTERNS
|
||||||
|
|
||||||
|
- Don't run `cargo fmt --manifest-path rust/Cargo.toml` from the repo root. Use `../scripts/fmt.sh` instead.
|
||||||
|
- Don't add `unsafe` code. The lint is set to `forbid`, not `deny`. You can't `#[allow]` it.
|
||||||
|
- Don't create dependencies from `commands` → `tools`. The arrow goes `tools` → `commands`.
|
||||||
|
- Don't write TUI output directly to stdout or use raw ANSI escape sequences.
|
||||||
|
- Don't add features to crates other than `claw-rag-service` without good reason; the workspace is feature-lean by design.
|
||||||
|
|
||||||
|
## COMMANDS
|
||||||
|
|
||||||
|
All run from `rust/`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Format (check only)
|
||||||
|
../scripts/fmt.sh --check
|
||||||
|
|
||||||
|
# Format (apply)
|
||||||
|
../scripts/fmt.sh
|
||||||
|
|
||||||
|
# Lint (strict, matches what you should pass before pushing)
|
||||||
|
cargo clippy --workspace --all-targets -- -D warnings
|
||||||
|
|
||||||
|
# Test
|
||||||
|
cargo test --workspace
|
||||||
|
|
||||||
|
# Build specific binary
|
||||||
|
cargo build -p rusty-claude-cli
|
||||||
|
cargo build -p claw-analog
|
||||||
|
cargo build -p claw-rag-service
|
||||||
|
cargo build -p mock-anthropic-service
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: CI clippy runs without `-D warnings`, so the local check above is stricter than the gate.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# AGENTS.md — api crate
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
LLM provider client layer: dispatches Anthropic, xAI, OpenAI, DashScope, and Ollama behind two wire protocols (Anthropic Messages native, OpenAI Chat Completions compat).
|
||||||
|
|
||||||
|
## WHERE TO LOOK
|
||||||
|
|
||||||
|
| Module | What lives here |
|
||||||
|
|---|---|
|
||||||
|
| `client.rs` | `ProviderClient` enum (facade). `from_model(model)` resolves alias, picks `ProviderKind`, handles OLLAMA_HOST and DashScope qwen-prefix cases. `send_message`/`stream_message`. |
|
||||||
|
| `providers/mod.rs` | `Provider` trait (generic, dead_code-allowed, not used for dispatch). `ProviderKind`, `resolve_model_alias`, `ProviderMetadata` (auth_env, base_url_env, default_base_url), `max_tokens_for_model[_with_override]`, capability/diagnostic reporting, `preflight_message_request` validation. |
|
||||||
|
| `providers/anthropic.rs` | `AnthropicClient` (re-exported as `ApiClient` at crate root). Dual auth: API key env vs saved OAuth (`AuthSource`, `OAuthTokenSet`, token expiry checks). Base-url resolution. SSE `MessageStream`. Prompt-cache hooks. |
|
||||||
|
| `providers/openai_compat.rs` | `OpenAiCompatClient` parameterized by `OpenAiCompatConfig` (presets: `xai()`, `openai()`, `dashscope()`, `OLLAMA_CONFIG`). Heavy translation layer: `build_chat_completion_request`, `translate_message`, `sanitize_tool_message_pairing`, `flatten_tool_result_content`. Model-quirk predicates (`is_reasoning_model`, etc.). Body-size estimation/guards. |
|
||||||
|
| `types.rs` | Provider-agnostic wire types: `MessageRequest`, `InputMessage`, `ContentBlock`, `StreamEvent`, `Usage`, `ToolDefinition`, `ToolChoice`. |
|
||||||
|
| `sse.rs` | `SseParser`, `parse_frame`. |
|
||||||
|
| `http_client.rs` | reqwest builders, `ProxyConfig` from env proxy vars, `TimeoutConfig`. |
|
||||||
|
| `error.rs` | `ApiError`. |
|
||||||
|
| `prompt_cache.rs` | `PromptCache` + `Stats` (Anthropic-only). |
|
||||||
|
| `lib.rs` | Curated `pub use` lists define the public surface. Also re-exports sibling telemetry crate items. |
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
- Module-private by default. `lib.rs` `pub use` lists are the sole public API surface.
|
||||||
|
- `#[must_use]` on pure constructors.
|
||||||
|
- Provider config follows an env-var pair pattern: `*_API_KEY` / `*_BASE_URL`, recorded in `ProviderMetadata`.
|
||||||
|
- Leaf files carry targeted `#![allow(clippy::cast_possible_truncation)]` where needed.
|
||||||
|
- Dispatch goes through the `ProviderClient` enum, not trait objects. The `Provider` trait exists but is dead-code-allowed.
|
||||||
|
- Streams unify into `MessageStream` with `next_event()` yielding `StreamEvent`.
|
||||||
|
|
||||||
|
## TESTS
|
||||||
|
|
||||||
|
- Four integration test files under `tests/`:
|
||||||
|
- `client_integration` — core client behavior
|
||||||
|
- `openai_compat_integration` — OpenAI-compat translation paths
|
||||||
|
- `provider_client_integration` — `ProviderClient` dispatch
|
||||||
|
- `proxy_integration` — proxy config
|
||||||
|
- Tests that touch env vars serialize through a shared `env_lock()` mutex. Don't skip this or you'll get flaky parallel failures.
|
||||||
|
- `benches/request_building.rs` is the workspace's only Criterion bench. Targets hot translation functions. This file bulk-opts out of strict lints.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# commands crate
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
REPL slash-command surface: parsing, spec registry, help rendering, and a handful of in-crate handlers. Single flat `src/lib.rs` (~7k lines). Deps: `plugins`, `runtime`, `serde_json` only. Note: `tools` depends on `commands`, not the reverse.
|
||||||
|
|
||||||
|
## lib.rs MAP
|
||||||
|
|
||||||
|
| Lines | Landmark |
|
||||||
|
|-------------|----------|
|
||||||
|
| 16–58 | Registry types: `CommandManifestEntry`, `CommandSource` (Builtin / InternalOnly / FeatureGated), `CommandRegistry`, `SlashCommandSpec`, `SkillSlashDispatch` |
|
||||||
|
| 60–1047 | `SLASH_COMMAND_SPECS` static table. 120+ entries (help, status, sandbox, compact, model, permissions, clear, cost, resume, config, mcp, memory, init, diff, version, bughunter, commit, pr, issue, ultraplan, teleport, debug-tool-call, export, session, plugin, agents, skills, doctor, plan, review, tasks, theme, vim, voice, chat, ...) |
|
||||||
|
| 1048–1303 | `SlashCommand` enum (~65 variants + `Unknown(String)`), `SlashCommandParseError`, `SlashCommand::parse` (L1218) |
|
||||||
|
| 1515–1899 | Per-command arg parsers: `parse_mcp_command`, `parse_plugin_command`, `parse_session_command`, etc. |
|
||||||
|
| 1900–2108 | Help/suggestion rendering: `render_slash_command_help*`, `suggest_slash_commands` (Levenshtein), category grouping |
|
||||||
|
| 2109–2682 | Result types + handlers: `handle_plugins_slash_command`, `handle_agents/mcp/skills_slash_command(_json)`, skill dispatch/resolve |
|
||||||
|
| 3160–5293 | Reporting layer: paired text and `_json` renderers for plugins/agents/skills/mcp reports, skill install/uninstall/create-agent logic, frontmatter parsing, root discovery |
|
||||||
|
| 5294 | `handle_slash_command(input, session, compaction)` top dispatch. Only Compact and Help execute here; all other variants return to the REPL caller |
|
||||||
|
| 5403–7183 | `mod tests` (~1780 lines) |
|
||||||
|
|
||||||
|
## ADDING A SLASH COMMAND
|
||||||
|
|
||||||
|
1. **Spec.** Add a `SlashCommandSpec` entry to `SLASH_COMMAND_SPECS`. Set `resume_supported` honestly.
|
||||||
|
2. **Enum + parse.** Add a variant to `SlashCommand`. Wire a match arm in `SlashCommand::parse`. If the command takes arguments, add a dedicated `parse_*_command` function in the arg-parser block.
|
||||||
|
3. **Handler.** Decide where execution lives:
|
||||||
|
- In-crate (like Compact/Help): handle it inside `handle_slash_command`.
|
||||||
|
- Returned to caller: just return the parsed variant. The REPL layer executes it.
|
||||||
|
4. **Help.** Make sure the spec's `summary` and `argument_hint` are set so help rendering and suggestion matching pick it up automatically.
|
||||||
|
5. **Tests.** Cover parsing (valid input, bad input, edge cases) in the inline `mod tests`.
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
- **Dual renderers.** Every report surface has a text variant and a `_json` variant: `handle_x` / `handle_x_json`, `render_*` / `render_*_json`. Keep them in sync.
|
||||||
|
- **Error style.** Handlers return `std::io::Result`. Parse failures use `SlashCommandParseError`.
|
||||||
|
- **Manifest registries.** Pattern is `entries: Vec<_Entry>` backed by the static spec table.
|
||||||
|
- **Dependency direction.** This crate knows nothing about `tools`. Don't import it.
|
||||||
|
- **No execution here.** Almost all commands pass through as parsed data. Only Compact and Help run inside this crate. Respect that boundary.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# AGENTS.md — plugins crate
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
Plugin subsystem: how third-party, builtin, and bundled tools/commands/hooks enter the runtime.
|
||||||
|
|
||||||
|
## WHERE TO LOOK
|
||||||
|
|
||||||
|
- `src/lib.rs` (~3,863 lines): the bulk of the crate. Manifest parsing (`.claude-plugin/plugin.json`), installed-plugin registry, lifecycle model, permission model, install/update management.
|
||||||
|
- `src/hooks.rs`: hook event model (`HookEvent`, `HookRunResult`) and `HookRunner` for shell-hook execution. Re-exported from `lib.rs`. **Caution:** the runtime crate has its own `hooks.rs` with a separate `HookRunner` (execution + abort-signal side, around L155). Know which layer you need before editing.
|
||||||
|
- `src/test_isolation.rs`: test isolation helpers.
|
||||||
|
- `bundled/`: example plugin fixtures. `example-bundled/` and `sample-hooks/` each contain `.claude-plugin/plugin.json` plus `pre.sh`/`post.sh` shell hooks. Treat these as the reference shape when authoring a new plugin.
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
**Key public types** (all in `src/lib.rs` unless noted):
|
||||||
|
|
||||||
|
- Kinds/definitions: `PluginKind`, `PluginDefinition`, `BuiltinPlugin`, `BundledPlugin`, `ExternalPlugin`.
|
||||||
|
- Manifests: `PluginManifest`, `PluginToolManifest`, `PluginToolDefinition`, `PluginToolPermission`, `PluginCommandManifest`.
|
||||||
|
- Hooks: `PluginHooks`, `HookEvent`, `HookRunResult` (from `hooks.rs`).
|
||||||
|
- Lifecycle/permissions: `PluginLifecycle`, `PluginPermission`.
|
||||||
|
- Registry: `InstalledPluginRecord`, `InstalledPluginRegistry`, `RegisteredPlugin`, `PluginRegistry` (+ `Report`, `Summary`, `LoadFailure`).
|
||||||
|
- Management: `PluginManager` (+ `Config`), `InstallOutcome`, `UpdateOutcome`.
|
||||||
|
- Trait: `Plugin`.
|
||||||
|
- Errors: `PluginError`.
|
||||||
|
- Entry points: `builtin_plugins()`, `load_plugin_from_directory()`.
|
||||||
|
|
||||||
|
**Lifecycle spans two crates.** Manifest parsing and registry live here. Health checks, degraded-mode, and `PluginState` live in `runtime/src/plugin_lifecycle.rs`. Changes to plugin lifecycle logic often touch both.
|
||||||
|
|
||||||
|
**Plugin shape.** A plugin directory contains `.claude-plugin/plugin.json` at minimum. Shell hooks (`pre.sh`, `post.sh`) sit alongside. See `bundled/` for working examples.
|
||||||
|
|
||||||
|
**Consumers.** `PluginManager` has ~48 references across the workspace. CLI wires plugins via `RuntimePluginStateBuildOutput` in `rusty-claude-cli`. The tools crate exposes plugin tools through `GlobalToolRegistry`.
|
||||||
|
|
||||||
|
## NOTES
|
||||||
|
|
||||||
|
- Don't confuse the two `HookRunner` implementations. This crate's version handles the event model. The runtime crate's version handles execution and abort signals.
|
||||||
|
- `lib.rs` is large. Most searches for plugin behavior start and end there.
|
||||||
|
- Bundled plugin fixtures under `bundled/` are used in tests. Breaking their structure breaks CI.
|
||||||
|
- Permission model is enforced at install time and checked at runtime. Both paths matter when modifying `PluginPermission`.
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# AGENTS.md — runtime crate
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
Core crate of `claw`: session persistence, permissions, prompt assembly, MCP plumbing, tool-facing file ops, conversation loop. 47 flat modules in src/, ~330 pub symbols, ~170 re-exported flat from lib.rs.
|
||||||
|
|
||||||
|
## WHERE TO LOOK
|
||||||
|
|
||||||
|
| Group | Files | Entry points |
|
||||||
|
|---|---|---|
|
||||||
|
| session/conversation | session.rs, session_control.rs, conversation.rs, compact.rs, summary_compression.rs, usage.rs | `Session` (L117), `SessionStore`, `ConversationRuntime` (L130), `ApiClient`/`ToolExecutor` traits |
|
||||||
|
| config | config.rs, config_validate.rs, bootstrap.rs | `ConfigLoader` (L409), type-export heaviest file; MCP server config enums live here |
|
||||||
|
| MCP (6-file split) | mcp.rs, mcp_client.rs, mcp_stdio.rs, mcp_server.rs, mcp_tool_bridge.rs, mcp_lifecycle_hardened.rs | `McpServerManager` (L488 in mcp_stdio.rs), JSON-RPC spawn |
|
||||||
|
| hooks/plugins | hooks.rs, plugin_lifecycle.rs | `HookRunner` (L155), abort signal, healthcheck, degraded mode |
|
||||||
|
| permissions/safety | permissions.rs, permission_enforcer.rs, policy_engine.rs, approval_tokens.rs, sandbox.rs, bash_validation.rs, trust_resolver.rs | `PermissionEnforcer` (L27), `GreenLevel`, lane decisions |
|
||||||
|
| tools/execution | bash.rs, file_ops.rs, lsp_client.rs | `execute_bash`, `*_in_workspace` file op variants |
|
||||||
|
| lane/worker | lane_events.rs, worker_boot.rs, task_packet.rs, task_registry.rs, team_cron_registry.rs, branch_lock.rs, stale_base.rs, stale_branch.rs | `LaneEvent` dedupe/provenance, `LaneBoard` |
|
||||||
|
| prompt | prompt.rs | `SystemPromptBuilder`, `ContextFile`, dynamic boundary marker |
|
||||||
|
| git/remote/auth | git_context.rs, remote.rs, oauth.rs | Upstream proxy, PKCE flow |
|
||||||
|
| misc | json.rs, sse.rs, g004_conformance.rs, green_contract.rs, recovery_recipes.rs, report_schema.rs, trident.rs | Report v1 + redaction |
|
||||||
|
|
||||||
|
Largest files by line count: config.rs (3894), mcp_stdio.rs (2969), lane_events.rs (2561), worker_boot.rs (2441), session.rs (1961), conversation.rs (1878).
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
- One file per module, flat layout. No subdirectories.
|
||||||
|
- Most modules are private `mod x` with selective `pub use`. 21 modules are `pub mod`, so consumers use both the re-export and the qualified path.
|
||||||
|
- Deps kept minimal: serde, tokio, glob, regex, sha2, walkdir + internal plugins/telemetry. No reqwest, no async-trait. Remote/SSE done by hand.
|
||||||
|
- Inline `#[cfg(test)]` tests per file. session.rs has two test modules.
|
||||||
|
- `pub(crate) test_env_lock()` mutex in lib.rs serializes env-mutating tests. Use it when touching env vars.
|
||||||
|
- trust_resolver.rs is `#[cfg(test)]`-gated yet pub-used: test-only API surface.
|
||||||
|
|
||||||
|
## INVARIANTS (do not break)
|
||||||
|
|
||||||
|
1. **Compaction pairs**: compact.rs must never split assistant(ToolUse)/ToolResult pairs.
|
||||||
|
2. **No side effects on construction**: SessionStore construction must not create `.claw` directories (session_control.rs:1090).
|
||||||
|
3. **Workspace containment**: file_ops.rs workspace ops must not escape the workspace root.
|
||||||
|
4. **Permission ordering**: a leading read-only permission token must not launder a trailing destructive one (permission_enforcer.rs:450).
|
||||||
|
|
||||||
|
## ANTI-PATTERNS
|
||||||
|
|
||||||
|
- Don't extend whole-file `#![allow(...)]` blocks. They exist as legacy tolerance in worker_boot.rs, mcp_tool_bridge.rs, lsp_client.rs, stale_branch.rs, stale_base.rs, recovery_recipes.rs, mcp_lifecycle_hardened.rs, session_control.rs. Adding new ones is not acceptable.
|
||||||
|
- Don't add reqwest or async-trait as deps. Remote calls go through the manual SSE/proxy layer in remote.rs and sse.rs.
|
||||||
|
- Don't create subdirectories under src/. The flat module layout is intentional.
|
||||||
|
- Don't bypass `*_in_workspace` variants for file ops when running inside a workspace context. The unchecked versions exist for bootstrap and out-of-workspace scenarios only.
|
||||||
|
- Don't add new `pub mod` exports without reason. Prefer private mod + selective `pub use` from lib.rs.
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# AGENTS.md — rusty-claude-cli (the `claw` binary)
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
Crate `rusty-claude-cli` produces the `claw` binary (~25 subcommands). Wires together api, runtime, tools, commands, plugins; terminal via crossterm/rustyline/syntect. Hand-rolled arg parser (no clap).
|
||||||
|
|
||||||
|
## main.rs MAP
|
||||||
|
|
||||||
|
src/main.rs is ~19,800 lines. Landmark table:
|
||||||
|
|
||||||
|
| Lines | Section |
|
||||||
|
|---|---|
|
||||||
|
| 73–330 | Provenance/model types (`ModelSource`, `ModelProvenance`, `PermissionModeSource`), build constants |
|
||||||
|
| 330–682 | Error taxonomy (`classify_error_kind`, JSON/text error output) |
|
||||||
|
| 774–994 | Global flags, `--cwd`/`-C` stripping, stdin plumbing |
|
||||||
|
| 995–1158 | `run()`: single flat match dispatching `CliAction` variants |
|
||||||
|
| 1162–1280 | `CliAction` enum, 25 struct variants (each carries `output_format`) |
|
||||||
|
| 1312–1477 | Output-format machinery: `OnceLock` statics, duplicate-flag tracking |
|
||||||
|
| 1478–2272 | `parse_args`: ~800-line manual token loop |
|
||||||
|
| 2542–3389 | Per-subcommand sub-parsers inside `parse_args` |
|
||||||
|
| 2902–3389 | Model/permission/allowed-tools resolution |
|
||||||
|
| 3390–4696 | Doctor subsystem (~13 `check_*_health` fns) |
|
||||||
|
| 4697–6062 | Manifests, bootstrap-plan, system-prompt, version, `resume_session` |
|
||||||
|
| 5459–7047 | `StatusContext`, `BinaryProvenance`, broad-cwd policy, stale-base preflight |
|
||||||
|
| 7048–9423 | `run_repl` interactive loop (`LiveCli` struct, streaming, heartbeat, `HookAbortMonitor`) |
|
||||||
|
| 9424–14264 | Snapshot printers: session-list, status, sandbox, models, help topics, acp, `run_init` (L11027), `run_export` (L11715), `print_help` (L14055) |
|
||||||
|
| 14266–19831 | In-file `mod tests` + 3 smaller test modules (one embeds a Python MCP fixture) |
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
- Every subcommand supports `--output-format text|json`, env-overridable.
|
||||||
|
- `raw_args_request_json_output` pre-scans argv before parsing to suppress stderr config warnings in JSON mode.
|
||||||
|
- Dual renderers for each output path: human text + structured `*_json`.
|
||||||
|
- Unknown flags/typos get levenshtein-distance suggestions.
|
||||||
|
- Comments carry issue numbers (#824, #146, etc.) when tracking known problems.
|
||||||
|
- `main()` (L330) is only the error envelope. All real work happens in `run()`.
|
||||||
|
- Error output always includes `status`, `error_kind`, `action`, `hint`, `exit_code` fields.
|
||||||
|
- `classify_error_kind` maps message prefixes to snake_case kind tokens.
|
||||||
|
- JSON errors go to stdout, text errors to stderr.
|
||||||
|
- Sibling modules: `init.rs`, `input.rs` (rustyline), `render.rs` (MarkdownStreamState, Spinner, syntect), `setup_wizard.rs`.
|
||||||
|
- `build.rs` injects `GIT_SHA`, `RUSTC_VERSION`, `GIT_DIRTY` via `cargo:rustc-env`.
|
||||||
|
|
||||||
|
## ANTI-PATTERNS
|
||||||
|
|
||||||
|
- File opens with crate-wide `#![allow(...)]` suppressing 13 lints including `dead_code` and `unused_imports`. Legacy. Do NOT extend this list.
|
||||||
|
- 9+ functions carry `#[allow(clippy::too_many_lines)]`. Tolerated, not license for more.
|
||||||
|
- Package/bin name mismatch: crate is `rusty-claude-cli`, binary is `claw`. Watch for this in paths and test macros.
|
||||||
|
|
||||||
|
## TESTS
|
||||||
|
|
||||||
|
All tests live in `tests/` (6 files), black-box style. They spawn `env!("CARGO_BIN_EXE_claw")` in unique temp dirs (AtomicU64 counter).
|
||||||
|
|
||||||
|
| File | What it covers |
|
||||||
|
|---|---|
|
||||||
|
| `output_format_contract.rs` | ~105 tests pinning `kind`/`status`/`action` JSON contract for EVERY subcommand. Must update when adding/changing any command output. 5,986 lines. |
|
||||||
|
| `resume_slash_commands.rs` | Resume and slash-command behavior |
|
||||||
|
| `cli_flags_and_config_defaults.rs` | Flag parsing, config file defaults |
|
||||||
|
| `compact_output.rs` | Compact output mode |
|
||||||
|
| `compact_repl_panic.rs` | Nested-runtime panic regression |
|
||||||
|
| `mock_parity_harness.rs` | Scenario-driven tests against mock-anthropic-service, driven by `rust/mock_parity_scenarios.json` |
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# AGENTS.md — rust/crates/tools
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
Single-crate tool surface: registry, 55 tool specs, permission-gated dispatch, all implementations. One flat `src/lib.rs` (10,892 lines, ~37% tests).
|
||||||
|
|
||||||
|
## lib.rs MAP
|
||||||
|
|
||||||
|
| Lines | Landmark |
|
||||||
|
|-------------|----------|
|
||||||
|
| 1–74 | Imports + six `global_*_registry()` OnceLock singletons (Lsp, McpTool, Team, Cron, Task, Worker) |
|
||||||
|
| 75–483 | Registry API: `ToolManifestEntry`, `ToolSource`, `ToolRegistry`, `ToolSpec`, `GlobalToolRegistry`, `RuntimeToolDefinition`, `canonical_allowed_tool_name` |
|
||||||
|
| 484–1348 | `mvp_tool_specs()` static table of 55 tools with inline JSON schemas |
|
||||||
|
| 1349–1524 | `enforce_permission_check` + `execute_tool()` string-match dispatch, permission classification helpers |
|
||||||
|
| 1525–2735 | `run_*` wrappers: deserialize input, call into `execute_*` or runtime fns |
|
||||||
|
| 2736–2763 | `workspace_traversal_guard_tests` mod |
|
||||||
|
| 2764–3354 | ~45 private serde IO structs |
|
||||||
|
| 3355–6824 | Real implementations: web fetch/search, todo store, skill resolution, agent/subagent spawning (`ProviderRuntimeClient` L5182, `SubagentToolExecutor` L5361), notebook edit, sleep, config, plan-mode, structured output, REPL, PowerShell |
|
||||||
|
| 6825–6826 | `pub mod lane_completion; pub mod pdf_extract;` |
|
||||||
|
| 6829–10892 | `mod tests` (~4000 lines) |
|
||||||
|
|
||||||
|
## ADDING A TOOL
|
||||||
|
|
||||||
|
1. Add a `ToolSpec` entry in `mvp_tool_specs()`. Include `name`, `description`, `input_schema` (inline JSON), and `required_permission: PermissionMode`.
|
||||||
|
2. Add a dispatch arm in `execute_tool()` matching the tool name string.
|
||||||
|
3. Write a `run_<tool>()` wrapper. Deserialize input from a dedicated serde struct.
|
||||||
|
4. Implement the actual logic below L3355 (or call into another crate).
|
||||||
|
5. Add inline tests in `mod tests`. Follow BDD naming: `given_x_when_y_then_z`.
|
||||||
|
6. Permission gating is automatic: `GlobalToolRegistry` / `SubagentToolExecutor` hold an optional `PermissionEnforcer` checked pre-dispatch.
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
- **Tool boundary signature**: `Result<String, String>`. Always.
|
||||||
|
- **Tool naming**: snake_case for file/shell tools, PascalCase otherwise. `canonical_allowed_tool_name` normalizes aliases.
|
||||||
|
- **State**: OnceLock registries for global singletons. JSON state files under config dirs for persistence.
|
||||||
|
- **Input validation**: reject empty strings for todos, descriptions, prompts, messages, code. ~12 validation sites between L3806–6167. Keep that contract.
|
||||||
|
- **Test naming**: BDD style (`given_x_when_y_then_z`).
|
||||||
|
- **Env-mutating tests**: acquire `env_lock()` mutex first.
|
||||||
|
- **Dependencies**: runtime, api, plugins, commands, reqwest(blocking), aspect-*, tokio.
|
||||||
|
|
||||||
|
## ANTI-PATTERNS
|
||||||
|
|
||||||
|
- **Don't add more `#[allow(clippy::...)]` suppressions.** ~50 `needless_pass_by_value` and several `too_many_lines` allows exist as legacy debt. Don't extend.
|
||||||
|
- **Don't skip input validation.** Empty-string rejection is a contract across all user-facing text fields.
|
||||||
|
- **Don't scatter implementation across new submodules.** The crate is intentionally flat (one lib.rs + two leaf mods). Only `lane_completion` and `pdf_extract` break out.
|
||||||
|
- **Don't duplicate tool names.** The canonical name mapping already handles aliases.
|
||||||
|
- **Don't bypass `enforce_permission_check`.** Every tool dispatch goes through permission gating. No exceptions.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# src/ AGENTS.md
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
|
||||||
|
This is a **porting workspace**, not production code. Nothing here is imported by,
|
||||||
|
built with, or shipped in the Rust product (`rust/crates/`). The Python tree exists
|
||||||
|
solely to mirror and track parity against the Claude Code TypeScript source.
|
||||||
|
|
||||||
|
## STRUCTURE
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── main.py # argparse CLI entry point (summary, parity-audit, manifest, etc.)
|
||||||
|
├── ~38 top-level .py # mirror TS root files one-to-one
|
||||||
|
├── models.py # frozen dataclasses: Subsystem, PortingModule, PermissionDenial, ...
|
||||||
|
├── parity_audit.py # hard-coded TS→Python filename mapping (main.tsx→main.py, etc.)
|
||||||
|
├── port_manifest.py # builds a manifest of the src/ tree itself
|
||||||
|
├── reference_data/ # tracked JSON snapshots extracted from the TS archive
|
||||||
|
│ ├── archive_surface_snapshot.json
|
||||||
|
│ ├── tools_snapshot.json, commands_snapshot.json
|
||||||
|
│ └── subsystems/*.json (29 per-subsystem records)
|
||||||
|
└── ~30 subdirectories/ # PLACEHOLDER PACKAGES (assistant/, bootstrap/, voice/, vim/, ...)
|
||||||
|
└── each contains only __init__.py loading subsystems/<name>.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Placeholder packages follow an identical template: load
|
||||||
|
`reference_data/subsystems/<name>.json` via `_archive_helper.load_archive_metadata()`,
|
||||||
|
re-export `ARCHIVE_NAME`, `MODULE_COUNT`, `SAMPLE_FILES`, `PORTING_NOTE`. There is no
|
||||||
|
voice, vim, or other real functionality behind them.
|
||||||
|
|
||||||
|
## WHERE TO LOOK
|
||||||
|
|
||||||
|
| Goal | Start here |
|
||||||
|
|---|---|
|
||||||
|
| Understand the CLI subcommands | `main.py` |
|
||||||
|
| See which TS files map to which .py | `parity_audit.py` |
|
||||||
|
| Find shared data structures | `models.py` |
|
||||||
|
| Check TS archive metadata | `reference_data/` |
|
||||||
|
| Real logic (permissions, path scoping) | `permissions.py`, `path_scope.py` |
|
||||||
|
| Query engine shim | `query_engine.py` |
|
||||||
|
| Runtime simulation | `runtime.py` |
|
||||||
|
| Tests | repo-root `tests/` (test_porting_workspace.py, test_security_scope.py) |
|
||||||
|
|
||||||
|
Run tests: `python -m unittest discover -s tests` from repo root.
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
|
||||||
|
- snake_case filenames, preserving TS names. camelCase exceptions exist where the
|
||||||
|
TS original used it: `QueryEngine.py`, `costHook.py`, `replLauncher.py`.
|
||||||
|
- Every mirrored entry carries a `source_hint` pointing back to its original `.ts`/`.tsx` path.
|
||||||
|
- `from __future__ import annotations` throughout. Stdlib only, no third-party deps.
|
||||||
|
- `models.py` dataclasses are frozen. Renderers are `as_markdown()` / `to_markdown()`.
|
||||||
|
- main.py simulates routing, turn-loops, bootstrap over mirrored inventories.
|
||||||
|
Read-only shims return handled/message results. It never calls an LLM.
|
||||||
|
- Thin shim modules (e.g. `ink.py`) exist as backlog metadata, not working code.
|
||||||
|
|
||||||
|
## ANTI-PATTERNS
|
||||||
|
|
||||||
|
**Don't add real agent, tool, voice, or vim functionality here.** That belongs in
|
||||||
|
`rust/crates/`. This tree is a scaffold, not an implementation target.
|
||||||
|
|
||||||
|
**Don't commit anything from `archive/`.** The local TS snapshot
|
||||||
|
(`archive/claude_code_ts_snapshot/src`) is gitignored. `reference_data/` holds the
|
||||||
|
tracked extracts; work from those.
|
||||||
|
|
||||||
|
**Don't let parity_audit.py drift.** When you rename a mirrored module, update the
|
||||||
|
hard-coded mapping in `parity_audit.py` to match.
|
||||||
|
|
||||||
|
**Don't add third-party dependencies.** Everything runs on stdlib.
|
||||||
Reference in New Issue
Block a user