Contributing Guide¶
Thank you for your interest in contributing to clinvoker! This guide covers development environment setup, project structure, coding standards, testing requirements, and the contribution process.
Development Environment Setup¶
Prerequisites¶
- Go 1.24 or later
- Git
- (Optional) Nix for reproducible environment
- (Optional) golangci-lint for linting
- (Optional) pre-commit for git hooks
Fork and Clone¶
# Fork the repository on GitHub, then:
git clone https://github.com/YOUR_USERNAME/clinvoker.git
cd clinvoker
git remote add upstream https://github.com/signalridge/clinvoker.git
Using Nix (Recommended)¶
This provides all required tools in a reproducible environment.
Manual Setup¶
Pre-commit Hooks¶
Project Structure¶
clinvoker/
├── cmd/
│ └── clinvk/ # Main application entry point
│ └── main.go
├── internal/
│ ├── app/ # CLI command implementations
│ ├── backend/ # Backend abstraction layer
│ ├── config/ # Configuration management
│ ├── executor/ # Command execution
│ ├── output/ # Output formatting
│ ├── server/ # HTTP API server
│ ├── session/ # Session management
│ ├── auth/ # API key management
│ ├── metrics/ # Prometheus metrics
│ └── resilience/ # Circuit breaker
├── docs/ # Documentation
├── scripts/ # Build and utility scripts
└── test/ # Integration tests
Package Responsibilities¶
| Package | Purpose | Key Files |
|---|---|---|
app/ |
CLI commands using Cobra | app.go, cmd_*.go |
backend/ |
Backend abstraction | backend.go, registry.go, claude.go, codex.go, gemini.go |
config/ |
Viper-based configuration | config.go, validate.go |
executor/ |
Subprocess execution | executor.go, signal.go |
server/ |
HTTP server | server.go, routes.go, handlers/, middleware/ |
session/ |
Session persistence | session.go, store.go, filelock.go |
Coding Standards¶
Go Guidelines¶
Follow Effective Go and Google Go Style Guide:
- Formatting: Use
gofmtorgoimports - Linting: Run
golangci-lint runbefore committing - Naming: Use descriptive, idiomatic names
- Comments: Document all exported types and functions
- Error Handling: Wrap errors with context, avoid naked returns
Code Style Examples¶
// Good: Clear function name, proper documentation, error handling
// ExecuteCommand runs the given command and returns the output.
func ExecuteCommand(ctx context.Context, cfg *Config, cmd *exec.Cmd) (*Result, error) {
if cfg == nil {
return nil, fmt.Errorf("config is required")
}
// Implementation
result, err := runWithTimeout(ctx, cmd, cfg.Timeout)
if err != nil {
return nil, fmt.Errorf("failed to execute command: %w", err)
}
return result, nil
}
// Bad: Unclear name, missing documentation, poor error handling
func exec(cfg *Config, c *exec.Cmd) (*Result, error) {
res, _ := runWithTimeout(context.Background(), c, cfg.Timeout)
return res, nil
}
Error Handling¶
Use the project's error package for consistent error handling:
import apperrors "github.com/signalridge/clinvoker/internal/errors"
// Create error with context
return apperrors.BackendError("claude", err)
// Check error type
if apperrors.IsCode(err, apperrors.ErrCodeBackendUnavailable) {
// Handle specific error
}
Testing Standards¶
All code must have tests. Follow these guidelines:
- File Naming:
*_test.goalongside source files - Table-Driven Tests: Use for multiple test cases
- Parallel Tests: Use
t.Parallel()for independent tests - Coverage: Aim for >80% coverage for new code
- Mocking: Use interfaces for testability
func TestExecuteCommand(t *testing.T) {
t.Parallel()
tests := []struct {
name string
cfg *Config
cmd *exec.Cmd
wantErr bool
}{
{
name: "valid command",
cfg: &Config{Timeout: 30 * time.Second},
cmd: exec.Command("echo", "hello"),
wantErr: false,
},
{
name: "nil config",
cfg: nil,
cmd: exec.Command("echo", "hello"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
_, err := ExecuteCommand(ctx, tt.cfg, tt.cmd)
if (err != nil) != tt.wantErr {
t.Errorf("ExecuteCommand() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
Testing Requirements¶
Running Tests¶
# All tests
go test ./...
# With race detection
go test -race ./...
# With coverage
go test -coverprofile=coverage.txt ./...
go tool cover -html=coverage.txt -o coverage.html
# Short tests only
go test -short ./...
# Specific package
go test ./internal/backend/...
Integration Tests¶
Integration tests require actual backend CLIs to be installed:
# Run integration tests
CLINVK_TEST_INTEGRATION=1 go test ./test/...
# Run with specific backend
CLINVK_TEST_BACKEND=claude go test ./test/...
Benchmarks¶
# Run benchmarks
go test -bench=. ./...
# Run with memory profiling
go test -bench=. -benchmem ./...
Documentation Requirements¶
Code Comments¶
Document all exported types and functions:
// Backend represents an AI CLI backend.
type Backend interface {
// Name returns the backend identifier.
Name() string
// IsAvailable checks if the backend CLI is installed.
IsAvailable() bool
}
// NewStore creates a new session store at the given directory.
// The directory is created if it doesn't exist.
func NewStore(dir string) (*Store, error) {
// ...
}
User Documentation¶
Update documentation for user-facing changes:
- Concepts: Update architecture docs if design changes
- Guides: Add/update how-to guides for new features
- Reference: Update API/CLI reference for changes
- Changelog: Add entry to CHANGELOG.md
Documentation Style¶
- Use clear, concise language
- Include code examples
- Add diagrams for complex concepts (Mermaid)
- Keep Chinese and English versions in sync
PR Process¶
Branch Naming¶
Use conventional branch names:
| Prefix | Purpose | Example |
|---|---|---|
feat/ |
New features | feat/add-gemini-backend |
fix/ |
Bug fixes | fix/session-locking |
docs/ |
Documentation | docs/api-examples |
refactor/ |
Code refactoring | refactor/executor |
test/ |
Test additions | test/backend-coverage |
chore/ |
Maintenance | chore/update-deps |
Commit Message Conventions¶
Follow Conventional Commits:
Types: feat, fix, docs, style, refactor, test, chore
Examples:
feat(backend): add support for new AI provider
Add support for the new XYZ AI CLI tool. This includes:
- Backend implementation
- Flag mapping
- Documentation updates
fix(session): handle concurrent access correctly
Fix race condition in session store when multiple processes
attempt to update the same session simultaneously.
docs(readme): update installation instructions
test(backend): add unit tests for registry
chore(deps): update golang.org/x packages
Pull Request Template¶
## Summary
Brief description of changes.
## Changes
- Change 1
- Change 2
## Test Plan
How changes were tested:
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing performed
## Related Issues
Fixes #123
Closes #456
Code Review Checklist¶
Before submitting PR:
- [ ] All tests pass locally
- [ ] Code follows style guidelines
- [ ] Documentation updated
- [ ] Commit messages follow conventions
- [ ] No unnecessary files included
- [ ] CHANGELOG.md updated (if applicable)
Review Process¶
- PRs require at least one approval
- CI must pass before merging
- Address review comments promptly
- Keep PRs focused and reasonably sized (<500 lines preferred)
- Rebase on main if there are conflicts
Release Process¶
Versioning¶
clinvoker follows Semantic Versioning:
MAJOR: Breaking changesMINOR: New features, backwards compatiblePATCH: Bug fixes, backwards compatible
Automated Releases with release-please¶
We use release-please for automated versioning and changelog generation.
How It Works¶
feat: add new feature → push to main → Release PR created automatically
↓
merge Release PR
↓
tag created (v1.1.0)
↓
GoReleaser builds binaries
- Write code using Conventional Commits
- Push to main - release-please automatically creates/updates a Release PR
- Merge Release PR - this creates a git tag and GitHub Release
- GoReleaser triggers - builds binaries for all platforms
Conventional Commits and Versioning¶
| Commit Type | Version Bump | Changelog Section |
|---|---|---|
feat: |
Minor (0.1.0 → 0.2.0) | Features |
fix: |
Patch (0.1.0 → 0.1.1) | Bug Fixes |
feat!: or BREAKING CHANGE: |
Major (0.1.0 → 1.0.0) | Breaking Changes |
perf: |
Patch | Performance |
refactor: |
Patch | Refactoring |
deps: |
Patch | Dependencies |
security: |
Patch | Security |
docs:, chore:, test:, ci: |
No release | Hidden |
Example Workflow¶
# 1. Create feature branch
git checkout -b feat/new-feature
# 2. Make changes and commit with conventional format
git commit -m "feat(backend): add support for new AI provider"
# 3. Create PR and merge to main
gh pr create && gh pr merge
# 4. release-please automatically creates Release PR
# Title: "chore: release v0.2.0"
# Contains: Updated CHANGELOG.md and version bumps
# 5. Review and merge Release PR
# → Tag v0.2.0 created
# → GoReleaser builds and publishes
# 6. Done! Binaries available via:
# - GitHub Releases
# - Homebrew: brew install signalridge/tap/clinvk
# - go install: go install github.com/signalridge/clinvoker/cmd/clinvk@latest
Manual Release (Emergency Only)¶
For emergency releases when automation is unavailable:
# 1. Update CHANGELOG.md manually
# 2. Create and push tag
git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3
# 3. GoReleaser workflow triggers automatically
Questions?¶
- Open an issue for bugs or features
- Start a discussion for questions
- Check existing issues before creating new ones
Code of Conduct¶
By participating, you agree to:
- Be respectful and inclusive
- Accept constructive criticism gracefully
- Focus on what's best for the community
- Show empathy towards others
Related Documentation¶
- Architecture Overview - System architecture
- Design Decisions - Architectural ADRs
- Troubleshooting - Common issues