Contributing
Development setup, build instructions, code style, and how to add tools, channels, and providers
Contributing
Development Setup
Prerequisites
- Rust 1.93+ (edition 2024) -- install via rustup
- Cargo (ships with rustup)
- wasm32-unknown-unknown target (optional, for WASM builds)
Getting Started
git clone https://github.com/weave-logic-ai/clawft.git
cd clawft
scripts/build.sh check
scripts/build.sh testVerify everything passes before making any changes:
scripts/build.sh clippy
cargo fmt --all -- --checkEditor Setup
Any editor with rust-analyzer support works well. Recommended settings:
- Enable
clippyas the check command in rust-analyzer. - Enable format-on-save using
rustfmt.
Project Structure
The workspace is organized into 22 crates with clear dependency boundaries:
clawft/
Cargo.toml # Workspace root
crates/
clawft-types/ # Core types: Config, events, errors
clawft-platform/ # Platform abstraction (fs, http, env, process)
clawft-core/ # Agent engine: loop, bus, pipeline, sessions, memory, security
clawft-kernel/ # Kernel: boot, governance, ECC, mesh, ExoChain, Weaver
clawft-llm/ # LLM provider abstraction and routing
clawft-tools/ # Tool implementations (file, shell, memory, web, spawn)
clawft-channels/ # Channel plugins (Telegram, Slack, Discord)
clawft-services/ # Background services (cron, heartbeat, MCP)
clawft-cli/ # CLI binary (weft)
clawft-wasm/ # WASM entrypoint
clawft-weave/ # Daemon binary (weaver)
clawft-security/ # Security primitives
clawft-plugin/ # Plugin SDK
clawft-plugin-*/ # First-party plugins (git, cargo, browser, etc.)
exo-resource-tree/ # Merkle-hashed resource tree
weftos/ # Facade crate
docs/ # Documentation
scripts/ # Build and utility scriptsKey Architectural Patterns
Platformtrait (clawft-platform): Abstracts filesystem, HTTP, environment variables, and process spawning so that the same codebase runs on native and WASM targets.Tooltrait (clawft-core): Extensible function-calling interface for the agent. Each tool declares its name, description, JSON Schema parameters, and an asyncexecutemethod.Channel/ChannelFactorytraits (clawft-channels): Plugin system for chat platforms.Providertrait (clawft-llm): Unified interface for LLM completions.- Feature gates: The workspace uses Cargo feature flags to gate optional subsystems (
ecc,exochain,mesh,os-patterns,tilezero,governance).
Building and Testing
Unified Build Script (Recommended)
The scripts/build.sh script provides subcommands for all build workflows:
scripts/build.sh native # Release CLI binary
scripts/build.sh native-debug # Debug build (fast iteration)
scripts/build.sh test # Run all tests
scripts/build.sh check # Fast compile check (no codegen)
scripts/build.sh clippy # Clippy with -D warnings
scripts/build.sh gate # Full 11-check phase gate
scripts/build.sh all # Build everything (native + WASI + browser + UI)
scripts/build.sh --help # See all commands and optionsThe gate subcommand runs all checks from the phase gate protocol and reports PASS/FAIL per check. Always run gate before committing.
Running Tests
# All workspace tests
scripts/build.sh test
# Specific crate
cargo test -p clawft-core
# Single test by name
cargo test -p clawft-core -- test_nameLinting and Formatting
Zero warnings are required. All code must be formatted with rustfmt:
# Lint
scripts/build.sh clippy
# Check formatting
cargo fmt --all -- --check
# Apply formatting
cargo fmt --allPre-Commit Checklist
The fastest way to run all checks:
scripts/build.sh gateCode Style Guidelines
General Rules
- Files under 500 lines. Split large files into submodules.
- No hardcoded secrets. API keys come from environment variables.
- All public APIs return
Result. Usethiserrorenums for error types. - Async-first. Use Tokio and
async-traitfor async interfaces. - Use
tracingfor logging. Notprintln!oreprintln!. - Workspace dependencies. Declare shared dependencies once in the root
Cargo.toml[workspace.dependencies]section.
Naming Conventions
- Types:
PascalCase(ToolRegistry,ChannelMetadata) - Functions and methods:
snake_case(register_all,route) - Constants:
SCREAMING_SNAKE_CASE - Crate names:
clawft-{module}(e.g.,clawft-core,clawft-llm)
Error Handling
Define error enums with thiserror in each crate:
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MyError {
#[error("not found: {0}")]
NotFound(String),
#[error("invalid input: {0}")]
InvalidInput(String),
#[error(transparent)]
Io(#[from] std::io::Error),
}Testing
Write tests alongside code in #[cfg(test)] mod tests blocks:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_valid_config() {
let config: Config = serde_json::from_str(r#"{"name": "test"}"#).unwrap();
assert_eq!(config.name, "test");
}
#[tokio::test]
async fn execute_tool() {
let tool = MyTool::new();
let result = tool.execute(serde_json::json!({"key": "value"})).await;
assert!(result.is_ok());
}
}Documentation
Document all public items. Include examples for complex APIs:
/// Route a model name to its provider.
///
/// # Examples
///
/// ```rust,ignore
/// let router = ProviderRouter::with_builtins();
/// let (provider, model) = router.route("openai/gpt-4o").unwrap();
/// assert_eq!(provider.name(), "openai");
/// ```
pub fn route(&self, model: &str) -> Option<(&dyn Provider, String)> {
// ...
}Adding Tools
Tools extend the agent's capabilities through LLM function calling.
Step 1: Create the Tool Module
Create a new file in crates/clawft-tools/src/:
use std::sync::Arc;
use async_trait::async_trait;
use serde_json::{json, Value};
use clawft_core::tools::registry::{Tool, ToolError};
use clawft_platform::Platform;
pub struct MyTool<P: Platform> {
platform: Arc<P>,
}
#[async_trait]
impl<P: Platform + 'static> Tool for MyTool<P> {
fn name(&self) -> &str { "my_tool" }
fn description(&self) -> &str { "A short description of what this tool does." }
fn parameters(&self) -> Value {
json!({
"type": "object",
"properties": {
"input": { "type": "string", "description": "The input to process" }
},
"required": ["input"]
})
}
async fn execute(&self, args: Value) -> Result<Value, ToolError> {
let input = args.get("input").and_then(|v| v.as_str())
.ok_or_else(|| ToolError::InvalidArgs("missing 'input'".into()))?;
Ok(json!({ "result": input }))
}
}Step 2: Export and Register
Add the module to crates/clawft-tools/src/lib.rs and register it in the register_all function.
Step 3: Write Tests
At a minimum, test: name() returns the expected identifier, parameters() returns valid JSON Schema, execute() succeeds with valid arguments, and execute() returns appropriate errors for invalid arguments.
Adding Channels
Channels connect the agent to chat platforms. Implement ChannelFactory (builds a Channel from JSON config) and Channel (handles start/stop lifecycle and bidirectional messaging). Register the factory with the PluginHost.
Adding LLM Providers
Option A (most common): If the provider exposes an OpenAI-compatible endpoint, add a ProviderConfig entry to the builtin_providers() function in crates/clawft-llm/src/config.rs. No new code required.
Option B: Users can add providers at runtime via configuration without modifying the codebase.
Option C: For non-OpenAI-compatible APIs, implement the Provider trait directly.
WASM Target
Install the target and build:
rustup target add wasm32-unknown-unknown
scripts/build.sh browserWhen adding new features, ensure they work behind the Platform trait rather than calling native APIs directly. Use #[cfg(not(target_arch = "wasm32"))] for native-only functionality.
Release Process
Release Build
scripts/build.sh nativeThe release profile is tuned for minimal binary size (opt-level = "z", LTO, strip, single codegen unit, abort on panic).
Release Checklist
- All tests pass:
scripts/build.sh test - No clippy warnings:
scripts/build.sh clippy - Formatting is clean:
cargo fmt --all -- --check - Release build succeeds:
scripts/build.sh native - Full gate passes:
scripts/build.sh gate