We welcome contributions! This project is part of the larger Muvon ecosystem and follows our open-source contribution guidelines.
- Rust 1.70+ (install from rustup.rs)
- Git for version control
- Basic understanding of Rust, embeddings, and vector databases
# Clone the repository
git clone https://github.com/muvon/octocode.git
cd octocode
# Build the project (MANDATORY: always use --no-default-features)
cargo build --no-default-features
# Run tests
cargo test --no-default-features
# Check code quality
cargo check --no-default-features --message-format=short
cargo clippy --no-default-features
# Run with debug logging
RUST_LOG=debug cargo run -- indexThe project uses several key dependencies:
- Tree-sitter: For parsing multiple programming languages
- Lance: Vector database for embeddings storage
- Tokio: Async runtime
- Clap: Command-line interface
- Serde: Serialization/deserialization
- Reqwest: HTTP client for API calls
octocode/
├── src/
│ ├── main.rs # CLI entry point
│ ├── config/ # Configuration management
│ ├── indexer/ # Code indexing and parsing
│ │ ├── languages/ # Language-specific parsers
│ │ └── embeddings/ # Embedding providers
│ ├── search/ # Search engine implementation
│ ├── graphrag/ # Knowledge graph functionality
│ ├── git/ # Git integration features
│ ├── mcp/ # MCP server implementation
│ └── utils/ # Utility functions
├── tests/ # Integration tests
├── docs/ # Documentation
└── examples/ # Usage examples
Language parsers are located in src/indexer/languages/. Each language needs:
Add the tree-sitter grammar to Cargo.toml:
[dependencies]
tree-sitter-your-language = "0.x.x"Create src/indexer/languages/your_lang.rs:
use tree_sitter::{Language, Query};
use crate::indexer::languages::{LanguageParser, ParsedSymbol, SymbolType};
pub struct YourLanguageParser;
impl LanguageParser for YourLanguageParser {
fn language() -> Language {
tree_sitter_your_language::language()
}
fn file_extensions() -> &'static [&'static str] {
&[".your_ext"]
}
fn extract_symbols(&self, source: &str) -> Vec<ParsedSymbol> {
// Implementation for extracting functions, classes, etc.
vec![]
}
fn extract_imports(&self, source: &str) -> Vec<String> {
// Implementation for extracting import statements
vec![]
}
fn extract_exports(&self, source: &str) -> Vec<String> {
// Implementation for extracting export statements
vec![]
}
}Add to src/indexer/languages/mod.rs:
pub mod your_lang;
// In the get_parser function:
match extension {
// ... existing cases
".your_ext" => Some(Box::new(your_lang::YourLanguageParser)),
_ => None,
}Create tests in tests/languages/test_your_lang.rs:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_your_language_parsing() {
let source = r#"
// Your language sample code
"#;
let parser = YourLanguageParser;
let symbols = parser.extract_symbols(source);
assert!(!symbols.is_empty());
// Add specific assertions
}
}Embedding providers are in src/embedding/provider/. To add a new provider:
- Create provider file (e.g.,
your_provider.rs) - Implement the
EmbeddingProvidertrait - Add to module exports in
mod.rs
Supported providers: FastEmbed, Jina, Voyage, Google, HuggingFace (BERT/JinaBERT), OpenAI
Create src/indexer/embeddings/your_provider.rs:
use async_trait::async_trait;
use crate::indexer::embeddings::{EmbeddingProvider, EmbeddingResult};
pub struct YourProvider {
api_key: String,
model: String,
}
#[async_trait]
impl EmbeddingProvider for YourProvider {
async fn embed_texts(&self, texts: &[String]) -> EmbeddingResult<Vec<Vec<f32>>> {
// Implementation for generating embeddings
Ok(vec![])
}
fn model_name(&self) -> &str {
&self.model
}
fn dimensions(&self) -> usize {
// Return embedding dimensions
768
}
}Add to src/indexer/embeddings/mod.rs:
pub mod your_provider;
// In the create_provider function:
if model.starts_with("yourprovider:") {
let model_name = model.strip_prefix("yourprovider:").unwrap();
return Ok(Box::new(your_provider::YourProvider::new(api_key, model_name)?));
}- Follow standard Rust formatting (
cargo fmt) - Use
cargo clippyfor linting - Write comprehensive tests for new features
- Document public APIs with rustdoc comments
Use the project's error types:
use crate::error::{OctocodeError, Result};
fn your_function() -> Result<String> {
// Use ? operator for error propagation
let result = some_operation()?;
Ok(result)
}Use tokio for async operations:
use tokio::fs;
async fn read_file(path: &str) -> Result<String> {
let content = fs::read_to_string(path).await?;
Ok(content)
}# Run all tests
cargo test
# Run specific test module
cargo test test_rust_parser
# Run with output
cargo test -- --nocapture
# Run integration tests
cargo test --test integration- Unit Tests: Test individual functions and modules
- Integration Tests: Test complete workflows
- Language Tests: Test language parser implementations
- Embedding Tests: Test embedding provider integrations
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[tokio::test]
async fn test_indexing_workflow() {
let temp_dir = TempDir::new().unwrap();
// Test implementation
}
#[test]
fn test_symbol_extraction() {
let source = "fn main() {}";
let symbols = extract_symbols(source);
assert_eq!(symbols.len(), 1);
}
}Use rustdoc comments for public APIs:
/// Extracts symbols from source code using tree-sitter parsing.
///
/// # Arguments
///
/// * `source` - The source code to parse
/// * `language` - The programming language
///
/// # Returns
///
/// A vector of parsed symbols including functions, classes, and variables.
///
/// # Examples
///
/// ```
/// let symbols = extract_symbols("fn main() {}", Language::Rust);
/// assert!(!symbols.is_empty());
/// ```
pub fn extract_symbols(source: &str, language: Language) -> Vec<ParsedSymbol> {
// Implementation
}When adding features, update:
- README.md: If it affects the main workflow
- doc/CONFIGURATION.md: For new configuration options
- doc/ADVANCED_USAGE.md: For new advanced features
- doc/ARCHITECTURE.md: For architectural changes
- Fork the repository and create a feature branch
- Make your changes following the style guidelines
- Add tests for new functionality
- Update documentation as needed
- Run the test suite to ensure everything passes
- Submit a pull request with a clear description
Follow conventional commit format:
feat(indexer): add support for Go language parsing
- Implement Go-specific symbol extraction
- Add import/export detection for Go modules
- Include comprehensive test coverage
Closes #123
## Description
Brief description of the changes.
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing performed
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Tests pass locallyWe follow Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes (backward compatible)
- Update version in
Cargo.toml - Update
CHANGELOG.md - Run full test suite
- Create release tag
- Build and test release binary
- Update documentation
- GitHub Issues: Bug reports and feature requests
- Email: opensource@muvon.io
- Discussions: GitHub Discussions for questions
When reporting bugs, include:
- Environment: OS, Rust version, Octocode version
- Steps to reproduce: Clear reproduction steps
- Expected behavior: What should happen
- Actual behavior: What actually happens
- Logs: Relevant error messages or debug output
For feature requests, provide:
- Use case: Why is this feature needed?
- Proposed solution: How should it work?
- Alternatives: Other approaches considered
- Additional context: Any other relevant information
We are committed to providing a welcoming and inclusive environment. Please:
- Be respectful and constructive in discussions
- Focus on what is best for the community
- Show empathy towards other community members
- Accept constructive criticism gracefully
By contributing to Octocode, you agree that your contributions will be licensed under the Apache License 2.0.