Contributing to Git-Iris
Ready to contribute your extension back to Git-Iris? This guide covers development setup, coding standards, testing requirements, and the PR process.
Quick Start
# Fork the repository on GitHub
git clone https://github.com/YOUR_USERNAME/git-iris.git
cd git-iris
# Create a feature branch
git checkout -b feature/my-extension
# Make your changes
# ...
# Test your changes
cargo test
cargo clippy
cargo fmt
# Commit and push
git add .
git commit -m "Add feature: my extension"
git push origin feature/my-extension
# Open a pull request on GitHubDevelopment Setup
Prerequisites
- Rust: 1.75 or later (
rustup update) - Git: 2.30 or later
- LLM Provider: At least one API key (OpenAI, Anthropic, or Google)
Environment Setup
# Install Rust (if needed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Clone your fork
git clone https://github.com/YOUR_USERNAME/git-iris.git
cd git-iris
# Build
cargo build
# Set up API key for testing
export ANTHROPIC_API_KEY=sk-ant-...
# or
export OPENAI_API_KEY=sk-...
# Run tests
cargo test
# Try it out
cargo run -- gen
cargo run -- studioDevelopment Workflow
# Create feature branch from main
git checkout main
git pull upstream main
git checkout -b feature/my-feature
# Make changes, test frequently
cargo build
cargo test
cargo run -- gen --debug
# Check code quality
cargo clippy
cargo fmt
# Commit with descriptive messages
git add .
git commit -m "Add X: brief description
Detailed explanation of what changed and why."
# Push and create PR
git push origin feature/my-featureCoding Standards
Rust Style
Follow standard Rust conventions:
# Format code
cargo fmt
# Lint code
cargo clippy
# Fix common issues
cargo clippy --fixCode Organization
Keep modules focused:
// Good - clear separation
mod tools;
mod capabilities;
mod state;
// Bad - mixed concerns
mod stuff;
mod utils;Use clear names:
// Good
pub struct GitDiff;
pub fn handle_commit_key(...) -> Vec<SideEffect>;
// Bad
pub struct Helper;
pub fn process(...) -> Vec<Thing>;Document public APIs:
/// Analyzes project dependencies from package manifests.
///
/// Supports Cargo.toml, package.json, and requirements.txt.
/// Auto-detects manifest type if not specified.
pub struct DependencyAnalyzer;Error Handling
Use descriptive errors:
// Good
return Err(anyhow::anyhow!(
"Failed to read Cargo.toml: file not found in {}",
path.display()
));
// Bad
return Err(anyhow::anyhow!("error"));Use error context:
use anyhow::Context;
let content = fs::read_to_string(&path)
.with_context(|| format!("Failed to read file: {}", path.display()))?;Tool Development Standards
Clear tool descriptions:
async fn definition(&self, _: String) -> ToolDefinition {
ToolDefinition {
name: "my_tool".to_string(),
// Good - actionable, specific
description: "Analyze project dependencies from package manifests (Cargo.toml, package.json, requirements.txt)".to_string(),
parameters: parameters_schema::<MyToolArgs>(),
}
}Structured output:
// Good - organized, parseable
Ok(format!(
"## Dependencies\n{}\n\n## Dev Dependencies\n{}\n",
deps, dev_deps
))
// Bad - unstructured
Ok(format!("{} {}", deps, dev_deps))Reasonable defaults:
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct MyToolArgs {
pub query: String, // Required
#[serde(default = "default_limit")]
pub limit: usize, // Optional with default
#[serde(default)]
pub verbose: bool, // Optional, defaults to false
}
fn default_limit() -> usize { 10 }Capability Development Standards
Explicit workflow steps:
## Workflow
1. Call `project_docs(doc_type="context")` to understand the project
2. Get changes with `git_diff(detail="summary")`
3. Analyze key files with `file_analyzer()`
4. Synthesize findings into structured outputClear output requirements:
## Output Requirements
- **Field1**: Description, constraints
- **Field2**: Description, format
- Use definitive language, not "probably" or "might"Context strategies:
## Context Strategy by Size
- **Small**: Consider all files
- **Medium**: Focus on high-relevance files
- **Large**: Use top 5-7 files, summarize rest
- **Very Large**: Use `parallel_analyze` to distribute workStudio Mode Standards
Pure reducer pattern:
// Good - pure function
pub fn reduce(state: StudioState, event: StudioEvent) -> (StudioState, Vec<SideEffect>) {
let mut state = state;
let effects = match event {
StudioEvent::KeyPress(key) => handle_key(&mut state, key),
// ...
};
(state, effects)
}
// Bad - side effects in reducer
pub fn reduce(state: &mut StudioState, event: StudioEvent) {
tokio::spawn(async { ... }); // Don't do this!
}Focused handlers:
// Good - clear responsibility
fn handle_file_list_key(state: &mut StudioState, key: KeyEvent) -> Vec<SideEffect>
fn handle_content_key(state: &mut StudioState, key: KeyEvent) -> Vec<SideEffect>
// Bad - monolithic
fn handle_key(state: &mut StudioState, key: KeyEvent) -> Vec<SideEffect> {
// 500 lines of match arms...
}Testing Requirements
Unit Tests
Every tool and capability should have tests:
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_dependency_analyzer_cargo() {
let tool = DependencyAnalyzer;
let args = DependencyAnalyzerArgs {
manifest_type: Some("cargo".to_string()),
include_dev: false,
};
let result = tool.call(args).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_dependency_analyzer_auto_detect() {
let tool = DependencyAnalyzer;
let args = DependencyAnalyzerArgs {
manifest_type: None,
include_dev: true,
};
let result = tool.call(args).await;
// Should auto-detect and succeed
assert!(result.is_ok());
}
#[test]
fn test_detect_manifest_type() {
let path = PathBuf::from("./");
let result = detect_manifest_type(&path);
// Project has Cargo.toml
assert_eq!(result.unwrap(), "cargo");
}
}Integration Tests
For modes and end-to-end flows:
#[cfg(test)]
mod integration_tests {
use super::*;
#[tokio::test]
async fn test_commit_generation_flow() {
// Set up test repo
let temp_dir = tempdir::TempDir::new("test_repo").unwrap();
// ... create test commits ...
// Generate commit message
let service = IrisAgentService::new(test_config())?;
let response = service.execute_capability("commit", &[]).await?;
// Verify output
assert!(matches!(response, StructuredResponse::CommitMessage(_)));
}
}Manual Testing
Before submitting PR:
- [ ] Build succeeds:
cargo build - [ ] Tests pass:
cargo test - [ ] Clippy passes:
cargo clippy - [ ] Format applied:
cargo fmt - [ ] Feature works in CLI:
cargo run -- gen - [ ] Feature works in Studio:
cargo run -- studio - [ ] Debug mode works:
cargo run -- gen --debug
Pull Request Process
Before Opening PR
Rebase on latest main:
bashgit fetch upstream git rebase upstream/mainEnsure tests pass:
bashcargo test cargo clippyClean commit history:
bash# Squash commits if needed git rebase -i HEAD~3
PR Title and Description
Good PR title:
Add dependency analyzer toolBad PR title:
feat: add some new stuffGood PR description:
## Summary
Adds a new tool that analyzes project dependencies from package manifests.
## Changes
- New `DependencyAnalyzer` tool in `src/agents/tools/dependency_analyzer.rs`
- Supports Cargo.toml, package.json, and requirements.txt
- Auto-detects manifest type
- Unit tests for all supported formats
## Testing
- [x] Tested with Rust project (Cargo.toml)
- [x] Tested with Node.js project (package.json)
- [x] Tested with Python project (requirements.txt)
- [x] Auto-detection works correctly
- [x] All tests pass
## Documentation
- Added tool documentation in extending/tools.md example
## Related Issues
Closes #123PR Checklist
Before requesting review:
- [ ] Code follows style guidelines
- [ ] Tests added for new functionality
- [ ] Documentation updated (if needed)
- [ ] No breaking changes (or clearly documented)
- [ ] Commit messages are descriptive
- [ ] PR description explains what and why
Review Process
- Automated checks: CI will run tests and linting
- Code review: Maintainers review your code
- Feedback: Address review comments
- Approval: Once approved, maintainers will merge
Responding to feedback:
# Make requested changes
git add .
git commit -m "Address review feedback: improve error messages"
# Push updates
git push origin feature/my-featureCommit Message Guidelines
Format
<type>: <subject>
<body>
<footer>Types
- feat: New feature
- fix: Bug fix
- refactor: Code refactoring
- docs: Documentation changes
- test: Test additions/changes
- chore: Build/tooling changes
Examples
Good commit message:
feat: Add dependency analyzer tool
Implements a new tool that analyzes project dependencies from
package manifests (Cargo.toml, package.json, requirements.txt).
The tool auto-detects the manifest type and supports filtering
dev dependencies.Concise commit:
fix: Handle empty git status correctly
Fixes panic when running in repository with no changes.Breaking change:
refactor: Update Tool trait to async
BREAKING CHANGE: All tools must now implement async `call()` method.
Existing tool implementations need to be updated.Documentation Requirements
When to Update Docs
Update documentation when you:
- Add a new capability
- Add a new tool
- Add a new Studio mode
- Change public APIs
- Add new configuration options
Where to Document
- User-facing features:
README.md - Developer features:
CLAUDE.md(Developer Guide) - Extension guides:
docs/extending/*.md - API docs: Inline doc comments (
///)
Documentation Style
Good API docs:
/// Analyzes project dependencies from package manifests.
///
/// Supports Cargo.toml, package.json, and requirements.txt.
/// Auto-detects manifest type if not specified.
///
/// # Examples
///
/// ```
/// let tool = DependencyAnalyzer;
/// let args = DependencyAnalyzerArgs {
/// manifest_type: None, // Auto-detect
/// include_dev: true,
/// };
/// let result = tool.call(args).await?;
/// ```
pub struct DependencyAnalyzer;Good guide content:
- Start with what the feature does
- Show step-by-step examples
- Link to real code
- Explain the why, not just the what
Common Issues
Build Fails
# Clean and rebuild
cargo clean
cargo build
# Update dependencies
cargo updateTests Fail
# Run specific test
cargo test test_name
# Run with output
cargo test -- --nocapture
# Run with logging
RUST_LOG=debug cargo testClippy Warnings
# Auto-fix where possible
cargo clippy --fix
# See all warnings
cargo clippy -- -W clippy::pedanticGetting Help
- Questions about architecture: Read
CLAUDE.md - Extension guides: Check
docs/extending/ - API questions: Check inline docs and examples
- Stuck on implementation: Open a draft PR and ask for guidance
- Found a bug: Open an issue with reproduction steps
Recognition
Contributors are recognized in:
- GitHub Contributors page
- Release notes for features you contribute
CONTRIBUTORS.md(coming soon)
Code of Conduct
- Be respectful and constructive
- Focus on the code, not the person
- Accept feedback gracefully
- Help others learn
License
By contributing, you agree that your contributions will be licensed under the same license as the project (check LICENSE file).
Next Steps
Ready to contribute?
- Pick an issue labeled
good first issueorhelp wanted - Comment that you're working on it
- Follow this guide to implement and test
- Open a PR
- Respond to feedback
- Celebrate when it's merged
Let's build something powerful together. ⚡
Additional Resources
- Rust Book: https://doc.rust-lang.org/book/
- Rig Framework: https://docs.rs/rig-core
- Ratatui TUI: https://ratatui.rs/
- Git-Iris Discussions: GitHub Discussions tab
Welcome to the Git-Iris community.
