Development Guide
LibreFang development environment setup and contribution guidelines.
Development Environment
Prerequisites
- Rust 1.75+
- Node.js 18+ (for desktop app)
- pnpm 8+ (for documentation site)
Clone Repository
git clone https://github.com/librefang/librefang.git
cd librefang
Build
# Build entire workspace
cargo build --workspace
# Build CLI
cargo build -p librefang-cli
# Build desktop app
cargo build -p librefang-desktop
Test
# Run all tests
cargo test --workspace
# Run specific crate
cargo test -p librefang-kernel
# Run doc tests
cargo test --doc
Linting
# Must have zero warnings
cargo clippy --workspace --all-targets -- -D warnings
# Format check
cargo fmt --all -- --check
Quick Commands with just
LibreFang provides a justfile for common development commands:
# Install just (if not already installed)
# macOS: brew install just
# Linux: cargo install just
# List all available commands
just
# Common workflows
just build # Build workspace (lib only)
just test # Run all tests
just lint # Clippy with zero warnings
just fmt # Format all code
just ci # Run full local CI (fmt-check + lint + test)
just check # Quick compilation check
just doc # Generate and open docs
just sync-versions # Sync version across all crates
Using just ci before pushing ensures your PR will pass CI checks.
Pre-commit Hooks
LibreFang includes a .pre-commit-config.yaml for automatic code quality checks:
# Install pre-commit
pip install pre-commit
# Install the hooks
pre-commit install
# Now cargo fmt and clippy run automatically on each commit
Project Structure
librefang/
├── Cargo.lock
├── Cargo.toml
├── crates/
│ ├── librefang-cli/ # CLI tool
│ ├── librefang-api/ # REST API server
│ ├── librefang-kernel/ # Core kernel
│ ├── librefang-runtime/ # Agent runtime
│ ├── librefang-memory/ # Memory subsystem
│ ├── librefang-types/ # Shared types
│ ├── librefang-channels/ # Channel adapters
│ ├── librefang-skills/ # Skill system
│ ├── librefang-hands/ # Hands system
│ ├── librefang-wire/ # P2P protocol
│ ├── librefang-desktop/ # Desktop app
│ ├── librefang-migrate/ # Migration tools
│ └── librefang-extensions/ # Extensions
├── xtask/ # Build scripts
├── docs/ # Project documentation
└── scripts/ # Helper scripts
Adding New Channel
1. Create Channel Module
// crates/librefang-channels/src/my_channel.rs
use async_trait::async_trait;
use crate::channel::{Channel, Message, ChannelConfig};
pub struct MyChannel {
config: ChannelConfig,
}
#[async_trait]
impl Channel for MyChannel {
async fn send(&self, message: Message) -> Result<(), Error> {
// Implement send logic
Ok(())
}
async fn receive(&self) -> Result<Message, Error> {
// Implement receive logic
Ok(Message::default())
}
}
2. Register Channel
// crates/librefang-channels/src/lib.rs
pub mod my_channel;
use crate::registry::ChannelRegistry;
pub fn register_channels(registry: &mut ChannelRegistry) {
registry.register("my_channel", |config| {
Ok(Box::new(MyChannel::new(config)))
});
}
3. Add Configuration
// crates/librefang-types/src/config.rs
#[derive(Debug, Clone, Deserialize)]
pub struct MyChannelConfig {
pub api_key_env: String,
pub allowed_users: Option<Vec<String>>,
}
Adding New Skill
1. Create Skill Structure
# skills/my-skill/skill.toml
name = "my-skill"
version = "1.0.0"
description = "My custom skill"
[runtime]
type = "python"
entrypoint = "main.py"
[tools]
provided = ["my_tool"]
[requirements]
packages = ["requests"]
2. Implement Code
# skills/my-skill/main.py
def my_tool(param: str) -> str:
"""My custom tool description"""
# Implement logic
return f"Result: {param}"
# Register tools
TOOLS = [my_tool]
3. Compile Skill
# Skills are automatically compiled into binary
cargo build --workspace
Adding New Tool
1. Define Tool
// crates/librefang-runtime/src/tools/mod.rs
use crate::tool::{Tool, ToolResult};
pub struct MyTool;
impl Tool for MyTool {
fn name(&self) -> &str {
"my_tool"
}
fn description(&self) -> &str {
"My custom tool description"
}
async fn execute(&self, params: Value) -> ToolResult {
// Implement tool logic
Ok(Value::String("result".to_string()))
}
}
2. Register Tool
// crates/librefang-runtime/src/lib.rs
pub fn register_tools(registry: &mut ToolRegistry) {
registry.register(MyTool::new());
}
Adding New LLM Provider
1. Implement Driver
// crates/librefang-runtime/src/llm/my_provider.rs
use crate::llm::{LlmDriver, LlmResponse, LlmError};
pub struct MyProvider {
api_key: String,
base_url: String,
}
#[async_trait]
impl LlmDriver for MyProvider {
async fn complete(&self, prompt: &str) -> Result<LlmResponse, LlmError> {
// Call API
Ok(LlmResponse {
text: "response".to_string(),
tokens: 100,
})
}
}
2. Add to Model Catalog
// crates/librefang-types/src/models.rs
pub fn get_provider(name: &str) -> Option<Box<dyn LlmDriver>> {
match name {
"my_provider" => Some(Box::new(MyProvider::new())),
_ => None,
}
}
Code Style
Rust Standards
- Format with
cargo fmt - Check with
cargo clippy - Follow Rust naming conventions
- Add documentation comments (
///)
Commit Standards
# Format: <type>(<scope>): <description>
git commit -m "feat(kernel): add new scheduling algorithm"
git commit -m "fix(channels): resolve Slack rate limit"
git commit -m "docs(api): update endpoint documentation"
git commit -m "test(runtime): add tool execution tests"
Types
| Type | Description |
|---|---|
| feat | New feature |
| fix | Bug fix |
| docs | Documentation |
| style | Formatting |
| refactor | Refactoring |
| test | Testing |
| chore | Maintenance |
Testing
Unit Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_my_function() {
assert_eq!(my_function(2), 4);
}
}
Integration Tests
#[tokio::test]
async fn test_agent_spawn() {
let kernel = Kernel::new().await;
let agent = kernel.spawn("test-agent").await;
assert!(agent.is_running());
}
Writing Integration Tests
Integration tests live in crates/*/tests/ directories. They test cross-module behavior and real component interaction.
// crates/librefang-kernel/tests/my_feature_test.rs
use librefang_kernel::Kernel;
use librefang_types::AgentManifest;
#[tokio::test]
async fn test_my_feature() {
// 1. Setup — create test fixtures
let kernel = Kernel::new_for_testing().await;
let manifest = AgentManifest::default();
// 2. Action — exercise the feature
let agent_id = kernel.spawn_agent(manifest).await.unwrap();
// 3. Assert — verify expected behavior
assert!(kernel.get_agent(&agent_id).is_some());
}
Tips:
- Use
#[tokio::test]for async tests - Run a specific crate's tests:
cargo test -p librefang-kernel - Run a single test:
cargo test -p librefang-kernel test_my_feature - Integration tests in
tests/directory have access to the crate's public API only
Benchmark Tests
#[tokio::bench]
async fn benchmark_llm_call(b: &mut Bencher) {
b.iter(|| {
runtime.block_on(llm.complete("test prompt"))
})
}
Debugging
Logging
use tracing::{info, warn, error};
info!("Starting agent {}", agent_id);
warn!("Rate limit exceeded for channel {}", channel_id);
error!("Failed to connect to provider: {}", error);
Debug Mode
# Enable verbose logging
RUST_LOG=debug cargo run
# See specific module only
RUST_LOG=librefang_kernel=trace cargo run
Performance Profiling
# CPU profiling
cargo flamegraph --bin librefang-cli -- start
# Memory profiling
cargo leptos --bin librefang-cli -- start
Release
Version Numbers
Follow Semantic Versioning (SemVer):
- Major - Incompatible API changes
- Minor - Backward-compatible new features
- Patch - Backward-compatible bug fixes
Release Process
# 1. Update version
cargo version bump patch
# 2. Update CHANGELOG
vim CHANGELOG.md
# 3. Create tag
git tag v0.1.0
# 4. Build release
cargo build --release
# 5. Publish to crates.io
cargo publish
Contributing
Contribution Process
- Fork the repository
- Create feature branch (
git checkout -b feature/my-feature) - Commit changes (
git commit -m "feat: add my feature") - Push branch (
git push origin feature/my-feature) - Create Pull Request
Code Review
- Ensure
cargo clippyhas no warnings - Ensure
cargo testpasses - Add test coverage for new code
- Update documentation
Code of Conduct
- Respect others
- Welcome newcomers
- Communicate professionally
- Accept constructive criticism