mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-08-24 10:05:34 -05: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,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.
|
||||
Reference in New Issue
Block a user