# Contributing to Ralphloop
Thank you for your interest in contributing to Ralphloop! This document provides guidelines and instructions for contributing.
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Getting Started](#getting-started)
- [Development Setup](#development-setup)
- [Making Changes](#making-changes)
- [Testing](#testing)
- [Submitting Changes](#submitting-changes)
- [Style Guide](#style-guide)
- [Community](#community)
## Code of Conduct
### Our Pledge
We are committed to providing a welcoming and inclusive environment for all contributors.
### Expected Behavior
- Be respectful and considerate
- Welcome newcomers and help them get started
- Accept constructive criticism gracefully
- Focus on what's best for the community
- Show empathy towards others
### Unacceptable Behavior
- Harassment or discrimination
- Trolling or insulting comments
- Publishing others' private information
- Other unprofessional conduct
## Getting Started
### Prerequisites
- Rust 1.70 or later
- Git
- Basic understanding of async Rust
- Familiarity with LLM APIs (optional)
### Fork and Clone
```bash
# Fork the repository on GitHub
# Then clone your fork
git clone https://github.com/YOUR_USERNAME/ralphiloop.git
cd ralphiloop
# Add upstream remote
git remote add upstream https://github.com/ORIGINAL_OWNER/ralphiloop.git
```
## Development Setup
### Install Dependencies
```bash
# Build the project
cargo build
# Run tests
cargo test
# Run with logging
RUST_LOG=debug cargo run -- --help
```
### IDE Setup
**VS Code:**
```json
{
"rust-analyzer.checkOnSave.command": "clippy",
"editor.formatOnSave": true
}
```
**Recommended Extensions:**
- rust-analyzer
- CodeLLDB (for debugging)
- Better TOML
## Making Changes
### Branch Naming
Use descriptive branch names:
- `feature/add-new-provider` - New features
- `fix/rate-limit-handling` - Bug fixes
- `docs/api-documentation` - Documentation
- `refactor/config-module` - Code refactoring
- `test/integration-tests` - Test additions
### Commit Messages
Follow conventional commits:
```
type(scope): subject
body (optional)
footer (optional)
```
**Types:**
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation
- `style`: Formatting
- `refactor`: Code restructuring
- `test`: Adding tests
- `chore`: Maintenance
**Examples:**
```
feat(llm): add support for Cohere provider
Implements the Cohere API integration with streaming support.
Closes #123
```
```
fix(config): handle missing profile gracefully
Previously crashed when profile didn't exist.
Now returns helpful error message.
```
### Code Organization
**Module Structure:**
```
src/
├── main.rs # CLI entry point
├── llm.rs # LLM provider implementations
├── config.rs # Configuration management
├── ralph_loop.rs # Loop execution engine
├── error.rs # Error types
├── templates.rs # Loop templates
├── repl.rs # REPL mode
├── analytics.rs # Analytics & monitoring
├── audit.rs # Audit trail
└── tests/ # Test modules
```
### Adding a New LLM Provider
1. **Implement the trait:**
```rust
pub struct MyProvider {
api_key: String,
model: String,
client: Client,
}
#[async_trait]
impl LLMProvider for MyProvider {
async fn complete(&self, prompt: &str) -> Result<LLMResponse> {
}
fn provider_name(&self) -> &'static str {
"myprovider"
}
}
```
2. **Add to config.rs:**
```rust
"myprovider" => {
let api_key = profile.api_key.clone()
.ok_or_else(|| RalphError::MissingConfig("API key not configured".to_string()))?;
Ok(Box::new(MyProvider::new(api_key, profile.model.clone())))
}
```
3. **Add tests:**
```rust
#[tokio::test]
async fn test_myprovider() {
let provider = MyProvider::new("test-key".to_string(), "model".to_string());
}
```
4. **Update documentation:**
- Add to README.md
- Add to API.md
- Add example configuration
## Testing
### Running Tests
```bash
# All tests
cargo test
# Specific test
cargo test test_name
# With output
cargo test -- --nocapture
# Integration tests
cargo test --test '*'
# Benchmarks
cargo bench
```
### Writing Tests
**Unit Tests:**
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_feature() {
// Arrange
let input = "test";
// Act
let result = function(input);
// Assert
assert_eq!(result, expected);
}
}
```
**Integration Tests:**
```rust
#[tokio::test]
async fn test_integration() {
let provider = MockLLMProvider::new(vec!["response".to_string()]);
let result = provider.complete("prompt").await;
assert!(result.is_ok());
}
```
### Test Coverage
```bash
# Install cargo-llvm-cov
cargo install cargo-llvm-cov
# Generate coverage
cargo llvm-cov --html
# Open report
open target/llvm-cov/html/index.html
```
## Submitting Changes
### Before Submitting
- [ ] Code compiles without errors
- [ ] All tests pass
- [ ] Code follows style guide
- [ ] Documentation updated
- [ ] Commit messages follow convention
- [ ] Branch is up to date with main
### Pull Request Process
1. **Update your branch:**
```bash
git fetch upstream
git rebase upstream/main
```
2. **Push to your fork:**
```bash
git push origin feature/my-feature
```
3. **Create Pull Request:**
- Use descriptive title
- Reference related issues
- Describe changes made
- Include testing done
- Add screenshots if UI changes
4. **PR Template:**
```markdown
## Description
Brief description of changes
## Related Issues
Closes #123
## Changes Made
- Added feature X
- Fixed bug Y
- Updated documentation
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests pass
- [ ] Manual testing completed
## Screenshots (if applicable)
```
### Review Process
- Maintainers will review within 48 hours
- Address feedback promptly
- Keep discussions professional
- Be patient and respectful
## Style Guide
### Rust Style
Follow official Rust style guide:
```bash
# Format code
cargo fmt
# Check lints
cargo clippy
```
### Code Conventions
**Naming:**
- `snake_case` for functions and variables
- `PascalCase` for types and traits
- `SCREAMING_SNAKE_CASE` for constants
**Error Handling:**
```rust
// Use Result types
pub fn function() -> Result<T> {
// Implementation
}
// Provide context
.map_err(|e| RalphError::Custom(format!("Failed to X: {}", e)))?
```
**Documentation:**
```rust
/// Brief description
///
/// # Arguments
/// * `param` - Description
///
/// # Returns
/// Description of return value
///
/// # Errors
/// When this function errors
///
/// # Example
/// ```
/// let result = function(param);
/// ```
pub fn function(param: Type) -> Result<ReturnType> {
// Implementation
}
```
### Documentation Style
- Use clear, concise language
- Include code examples
- Document error cases
- Keep examples up to date
## Community
### Getting Help
- **GitHub Issues:** Bug reports and feature requests
- **Discussions:** Questions and general discussion
- **Discord:** Real-time chat (if available)
### Reporting Bugs
**Bug Report Template:**
```markdown
## Bug Description
Clear description of the bug
## Steps to Reproduce
1. Step 1
2. Step 2
3. See error
## Expected Behavior
What should happen
## Actual Behavior
What actually happens
## Environment
- OS: macOS 13.0
- Rust: 1.70
- Ralph CLI: 0.1.0
## Additional Context
Any other relevant information
```
### Feature Requests
**Feature Request Template:**
```markdown
## Feature Description
Clear description of the feature
## Use Case
Why is this feature needed?
## Proposed Solution
How should it work?
## Alternatives Considered
Other approaches considered
## Additional Context
Any other relevant information
```
## Recognition
Contributors will be:
- Listed in CONTRIBUTORS.md
- Mentioned in release notes
- Credited in documentation
## License
By contributing, you agree that your contributions will be licensed under the same license as the project.
## Questions?
Don't hesitate to ask! Open an issue or discussion if you need help.
---
Thank you for contributing to Ralph CLI! 🎉