Welcome! Whether you're fixing a typo or adding a new feature, every contribution helps make ccboard better for the Claude Code community.
| Type | Examples | Effort |
|---|---|---|
| Report | Bugs, performance issues, broken links | 2-5 min |
| Improve | Fix typos, clarify docs, improve error messages | 5-15 min |
| Add Features | New tabs, parsers, analytics, export formats | 2-8 hours |
| Optimize | Performance, cache strategies, memory usage | 1-4 hours |
| Test | Write tests, improve coverage, benchmarks | 30-120 min |
| Share | Workflows, success stories, blog posts | 5 min |
Not sure where to start? Check issues labeled good first issue.
- Code of Conduct
- Getting Started
- Development Setup
- Code Style
- Testing
- Pull Request Process
- Architecture Guidelines
- Commit Message Guidelines
This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to florian.bruniaux@gmail.com.
- Rust 1.85+ (uses edition 2024)
- Git for version control
- Claude Code installed with
~/.claudedirectory (for testing)
# Fork the repository on GitHub
# Then clone your fork
git clone https://github.com/YOUR_USERNAME/ccboard.git
cd ccboard
# Add upstream remote
git remote add upstream https://github.com/FlorianBruniaux/ccboard.git# Build all crates
cargo build --all
# Build in release mode
cargo build --release --all# Run TUI (default)
cargo run
# Run web interface
cargo run -- web --port 3333
# Run with debug logging
RUST_LOG=ccboard=debug cargo run# Install cargo-watch
cargo install cargo-watch
# Auto-rebuild on changes
cargo watch -x 'run'
# Auto-rebuild web
cargo watch -x 'run -- web'ccboard follows strict Rust code quality standards.
REQUIRED before every commit:
# Format all code
cargo fmt --all
# Check formatting without modifying
cargo fmt --all -- --checkRule: All code must be formatted with rustfmt. CI will reject PRs with formatting issues.
REQUIRED before every commit:
# Run clippy with strict warnings
cargo clippy --all-targets -- -D warningsRules:
- Zero clippy warnings allowed
- Fix issues, don't suppress with
#[allow(...)]unless justified in PR description - Common violations:
- Unused imports
- Nested
if let(use collapsed pattern) - Missing documentation on public items
- Unnecessary
.clone()calls
ccboard follows idiomatic Rust error handling:
Use anyhow for application errors:
use anyhow::{Context, Result};
fn load_session(path: &Path) -> Result<Session> {
let file = File::open(path)
.context("Failed to open session file")?; // ✅ ALWAYS add context
// ...
}Rules:
- Every
?operator MUST have.context("description")or.with_context(|| ...) - Context messages should be actionable and user-friendly
- Never use
.unwrap()in production code (tests are OK)
Use thiserror for custom error types:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ParseError {
#[error("Invalid JSONL format at line {line}")]
InvalidFormat { line: usize },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}Rules:
- Libraries NEVER panic (except for contract violations in debug mode)
- Always return
Result<T, E>for fallible operations - Use
#[from]for automatic error conversion
ccboard displays partial data if some files are corrupted:
pub struct LoadReport {
pub stats_loaded: bool,
pub sessions_scanned: usize,
pub sessions_failed: usize,
pub errors: Vec<LoadError>,
}
// ✅ Good: Collect errors, continue loading
fn load_all() -> LoadReport {
let mut report = LoadReport::default();
for path in paths {
match parse_session(&path) {
Ok(session) => { /* store */ }
Err(e) => {
report.errors.push(e);
report.sessions_failed += 1;
// Continue with other sessions
}
}
}
report
}# Run all tests (139 tests)
cargo test --all
# Run tests for specific crate
cargo test -p ccboard-core
# Run specific test
cargo test test_name
# Run with logging
RUST_LOG=debug cargo test -- --nocaptureAll PRs must include tests for:
- New features (unit tests + integration tests where applicable)
- Bug fixes (regression test demonstrating the fix)
- Public API changes (documentation tests)
// ✅ Good: Tests in same file as implementation
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_valid_session() {
// Arrange
let input = r#"{"id":"123","type":"session"}"#;
// Act
let session = parse_session(input).unwrap();
// Assert
assert_eq!(session.id, "123");
}
#[test]
fn test_parse_handles_malformed_json() {
let input = "invalid json";
assert!(parse_session(input).is_err());
}
}Test Fixtures:
- Located in
crates/ccboard-core/tests/fixtures/ - Use real sanitized data when possible
- Keep fixtures minimal (only necessary fields)
Performance-critical code should include benchmarks:
# Run benchmarks
cargo bench
# Run specific benchmark
cargo bench --bench startup_benchLocation: crates/ccboard-core/benches/
# Update main
git checkout main
git pull upstream main
# Create feature branch
git checkout -b feat/my-feature
# or
git checkout -b fix/issue-123Branch naming:
feat/description- New featuresfix/description- Bug fixesdocs/description- Documentation onlyrefactor/description- Code refactoringtest/description- Test additions/fixeschore/description- Maintenance tasks
Follow the Pre-Commit Checklist for every commit.
- Add unit tests in the same file (
#[cfg(test)] mod tests) - Add integration tests in
tests/if needed - Update documentation tests if API changed
- Update README.md if user-facing changes
- Add/update doc comments (
///) for public APIs - Update CHANGELOG.md with notable changes
REQUIRED before pushing:
# 1. Format code
cargo fmt --all
# 2. Check clippy (MUST pass with 0 warnings)
cargo clippy --all-targets -- -D warnings
# 3. Run all tests
cargo test --all
# 4. Build in release mode (catches some edge cases)
cargo build --release --allAll checks must pass before submitting PR.
Follow Conventional Commits:
# Format: <type>(<scope>): <description>
feat(tui): add search highlighting in Sessions tab
fix(core): handle symlinks in project path validation
docs(readme): update installation instructions
refactor(cache): replace clones with Arc<T>
test(parser): add regression test for #123
chore(deps): update rusqlite to 0.32Types:
feat- New featurefix- Bug fixdocs- Documentation onlyrefactor- Code refactoring (no behavior change)test- Test additions/fixesperf- Performance improvementschore- Maintenance (deps, tooling)ci- CI/CD changes
Scopes (optional):
tui,web,core,clicache,parser,store,watcherdeps,config,readme
# Push to your fork
git push origin feat/my-feature
# Create PR on GitHub
# Fill in the PR template with:
# - Description of changes
# - Related issue (if any)
# - Testing performed
# - Screenshots (if UI changes)- CI checks must pass (formatting, clippy, tests)
- Code review by maintainer
- Address feedback if requested
- Squash commits if needed (maintainer will guide)
- Merge once approved
ccboard is a Cargo workspace with 4 crates:
ccboard/ # Binary CLI entry point
├─ ccboard-core/ # Shared data layer (NO UI)
│ ├─ parsers/ # JSONL, JSON, YAML parsers
│ ├─ models/ # Domain models
│ ├─ store.rs # Thread-safe DataStore
│ └─ cache/ # SQLite metadata cache
├─ ccboard-tui/ # Ratatui frontend
│ ├─ tabs/ # 7 tab implementations
│ ├─ components/ # Reusable UI components
│ └─ ui.rs # Main rendering logic
└─ ccboard-web/ # Leptos + Axum frontend (future)
Dependency Rules:
ccboard-coreNEVER depends ontuiorweb(pure business logic)tuiandwebdepend oncore(UI → logic, not logic → UI)ccboardbinary depends on all crates (entry point)
| Feature Type | Location |
|---|---|
| New parser (JSONL, JSON, etc.) | ccboard-core/src/parsers/ |
| New domain model | ccboard-core/src/models/ |
| Cache logic | ccboard-core/src/cache/ |
| New TUI tab | ccboard-tui/src/tabs/ |
| Reusable UI component | ccboard-tui/src/components/ |
| CLI command | ccboard/src/main.rs |
| Web page | ccboard-web/src/pages/ |
ccboard uses thread-safe concurrency:
use dashmap::DashMap;
use parking_lot::RwLock;
pub struct DataStore {
// ✅ DashMap for high-contention collections (per-key locking)
sessions: DashMap<String, Arc<SessionMetadata>>,
// ✅ RwLock for low-contention data (many readers, rare writers)
stats: RwLock<Option<Stats>>,
settings: RwLock<Option<Settings>>,
}Rules:
- Use
Arc<T>for shared ownership, minimize.clone()of large structs - Use
DashMapfor collections with many concurrent writes - Use
parking_lot::RwLockfor data with many reads, rare writes - NEVER use
std::sync::RwLock(parking_lot is faster and fairer) - Avoid nested locks (deadlock risk)
File changes are detected automatically:
use notify::Watcher;
// Debounced watcher (500ms)
let watcher = notify_debouncer_mini::new_debouncer(
Duration::from_millis(500),
|event| { /* handle change */ }
);Patterns:
- Stats cache: Reload on
stats-cache.jsonchange - Sessions: Update specific session on
.jsonlchange - Config: Reload cascade on any
settings.jsonchange
feat(cache): implement SQLite metadata cache with 89x speedup
- Add MetadataCache with mtime-based invalidation
- Use WAL mode for concurrent reads
- Background cache population during initial load
- Benchmarks show 20s → 224ms improvement
Closes #45
fix(parser): handle symlinks in project path sanitization
Previously, symlinks in project paths could bypass path validation,
allowing directory traversal attacks. Now explicitly reject symlinks
with is_symlink() check.
Fixes #78
docs(readme): add 13 production screenshots
- Dashboard, Sessions, Config, Hooks, Agents, Costs, History, MCP
- Search highlighting and Help modal screenshots
- Collapsible section for additional screenshots
❌ update stuff
❌ fix bug
❌ WIP
❌ changes
Before submitting:
- Code compiles without warnings (
cargo build) - All tests pass (
cargo test --all) - Clippy passes with 0 warnings (
cargo clippy --all-targets) - Code formatted (
cargo fmt --all) - Documentation updated if API changed
- CHANGELOG.md entry added for user-facing changes
The maintainer works on macOS. If you're a Windows user:
- Test Windows-specific code paths
- Verify path handling (backslashes vs forward slashes)
- Report Windows-specific issues
- Test cross-platform compatibility
Your contributions are especially valuable!
- Marketing language or promotional content
- Unverified or speculative performance claims
- Large architectural changes without prior discussion (open an issue first)
- Breaking changes to public APIs without consensus
- Code that fails CI checks
Contributors are recognized through:
- Git history — Your commits are permanently attributed
- GitHub contributors — Visible on repository page
- CHANGELOG.md — Significant contributions highlighted in releases
- General questions: GitHub Discussions
- Bug reports: Issues
- Security issues: florian.bruniaux@gmail.com (private)
Thank you for contributing to ccboard! 🎉