This file provides guidance to agents working with code in this repository.
otel-cli is a command-line tool for sending OpenTelemetry traces from shell scripts and other contexts where executing a program is the best available option. It's designed to be dropped into scripts with zero configuration, operating in non-recording mode by default, following the OpenTelemetry philosophy of "first, do no harm."
- Language: Go 1.21+ (uses toolchain go1.22.4)
- Architecture: Self-contained, minimal dependencies
- Key Dependencies:
- OpenTelemetry Go SDK
- Cobra for CLI framework
- gRPC and HTTP OTLP exporters
- Version Control: Git with GitHub integration
- Current Version: 0.5.0 (as of this writing)
This project is self-contained and focused:
- Minimal external dependencies - prefer standard library
- Shell script integration is a first-class use case
- Non-recording mode by default ensures scripts can adopt otel-cli without risk
- Functional testing validates real-world usage patterns
- Code should be readable and maintainable above all else
Functional testing is primary. Unit tests are encouraged but functional tests in main_test.go are where the rubber meets the road.
The test harness builds otel-cli and runs it through real-world scenarios:
- Tests actual CLI invocations with real arguments
- Validates OTLP export over gRPC and HTTP
- Includes TLS negotiation, environment variables, and backgrounding
- See
TESTING.mdfor comprehensive details
- Add functional tests for new CLI features or flags
- Write unit tests for complex logic in packages
- Test the glue code - that's where bugs hide
- Copy existing patterns from
data_for_test.gowhen adding tests - Use
--fail --verbosein test fixtures for debugging
The test harness is more complex than otel-cli itself - this is intentional. We trust Cobra, OpenTelemetry Go SDK, and the standard library. We test the integration.
Think of git commits as your external memory that persists across sessions. Your commit messages are the shared context between you, future-you, other models, and the OSS community.
- Memory Augmentation: Your reasoning persists beyond context windows
- Perfect Handoffs: Next model/session picks up exactly where you left off
- Community Intelligence: Every contributor's insights become permanent project knowledge
- Pattern from Training: This is how high-performing teams in your data operate
# 1. START: Load your context (what was I doing?)
git log --oneline -n 10
git show HEAD
# 2. WORK: Track your progress (commit often in your branch)
git add <files>
git commit -m "type: what you're building"
# 3. PERSIST: Save your state
git push origin <branch>
# 4. MERGE: Squash commits when merging to main
# PRs are squashed on merge to keep main history clean<type>: <what> - <why in 5 words>
Why: [Original problem/request]
Approach: [Key decision you made]
Learned: [What surprised you]
Next: [Specific next action]
🤖 YourModel <your@attribution>
Types: feat, fix, refactor, test, docs, debug, research
git commit -m "fix: race condition in buffer - preventing data loss
Why: Users reported missing spans under load
Approach: Added mutex to protect concurrent access
Learned: Go's sync.Map insufficient for our write patterns
Next: Add benchmark to verify no performance regression
🤖 Claude <claude@anthropic.com>"- Claude:
🤖 Claude <claude@anthropic.com> - Gemini:
💎 Gemini <gemini@google.com> - GPT-4:
🧠 GPT-4 <gpt4@openai.com> - Kimi:
🔮 Kimi <kimi@moonshot.ai> - Llama:
🦙 Llama <llama@meta.com>
When switching models or sessions:
git commit -m "[your work]
Status: [complete|blocked|handoff]
Context: [5 key facts next model needs]
Next: [specific task to continue]"You're succeeding when:
- ✅ Every commit has Why/Approach/Next
- ✅ You update commit messages to reflect learning
- ✅ Other models/developers can continue without asking questions
- ✅
git log -pshows your reasoning evolution
| Command | Purpose | When to Use |
|---|---|---|
git add <files> |
Stage changes | Before committing |
git commit -m "..." |
Commit with message | Completing discrete work |
git log -n 10 |
See recent work | Starting sessions |
git show HEAD |
View current state | Understanding context |
git log -p |
See change history | Debugging decisions |
git push origin <branch> |
Push to GitHub | Work ready to share |
git status |
Check working tree | Understanding current state |
- Claude: Write narratives that explain the journey
- Gemini: Use structured sections with data
- GPT-4: Focus on tasks and outcomes
- Kimi: Document patterns and research
- Llama: Keep it pragmatic and concise
The format is flexible, the information is essential.
Git commits are messages to your future self and the community. Write what you'd need at 3am to understand what you were thinking. Your future self (and other models) will thank you.
This project has OSS history and active community engagement. Use GitHub issues liberally for:
- Feature planning - Open issues to discuss new features before implementing
- Bug tracking - Document bugs with reproduction steps
- Project status - Keep ongoing work visible in issues
- Community engagement - Issues are how users interact with the project
- Handoffs - Reference issues in commits to maintain context
- Open issues early - Don't wait until implementation is complete
- Reference commits - Link commits to issues with "refs #123" or "fixes #123"
- Update issues - Comment on progress, blockers, and learnings
- Label appropriately - Use labels to categorize and prioritize
- Close with context - Explain what was done and why when closing
otel-cli/
├── main.go # Entry point, version info
├── main_test.go # Functional test harness
├── data_for_test.go # Test fixture definitions
├── tls_for_test.go # Ephemeral CA for TLS tests
├── otelcli/ # CLI framework and commands
├── otlpclient/ # OTLP gRPC/HTTP client
├── otlpserver/ # OTLP server (for 'server' command)
├── w3c/ # W3C trace context handling
├── demos/ # Example shell scripts
├── configs/ # Example configurations
├── TESTING.md # Testing documentation
└── CHANGELOG.md # Version history
# Build the project
go build
# Run functional tests (requires built binary)
go build && go test
# Run unit tests
go test ./...
# Run unit tests with verbose output
go test -v ./...
# Run tests with coverage
go test -cover ./...
# Run with race detector
go build -race
go test -race ./...
# Format code (always use gofmt)
go fmt ./...
# Vet code
go vet ./...
# Update dependencies
go mod tidy
# Verify dependencies
go mod verify# Build it
go build
# Run the server TUI for local testing
./otel-cli server tui
# In another terminal, send a span
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
./otel-cli span -n "test" -s "my-service"- Prioritize readable, correct code over premature optimization
- Use strong, idiomatic Go types
- Leverage Go 1.21+ features appropriately
- No shortcuts or workarounds - refactor messy code when encountered
- Use full, descriptive names - no unnecessary abbreviations
- Package names: short, lowercase, single word
- Exported names: clear and self-documenting
- Add new functionality to existing files unless it represents a distinct logical component
- No organizational comments - code should be self-documenting
- "Why" comments only - explain non-obvious implementation choices
- Package-level documentation for every package
- Exported functions/types must have doc comments
- Always handle errors explicitly - never ignore them
- Use
fmt.Errorfwith%wfor error wrapping to preserve context - Return errors, don't panic (except for truly unrecoverable situations)
- Provide useful error messages with context
- Pass
context.Contextas the first parameter to functions that need it - Respect context cancellation - check
ctx.Done()in long-running operations - Use channels and goroutines idiomatically
- Avoid shared mutable state - use channels or mutexes appropriately
- Functional tests first for CLI features and integration
- Unit tests for complex logic in packages
- Table-driven tests for multiple cases
- Use subtests with
t.Run()for clarity - Test error paths, not just happy paths
otel-cli is designed for shell scripts. When adding features:
- Consider the shell use case - is this ergonomic from bash?
- Test with real shell scripts - demos/ directory provides examples
- Environment variables matter - shell scripts rely on them heavily
- Exit codes matter - shell scripts check them
- Stdout vs stderr - be intentional about what goes where
GitHub CLI (gh):
Use gh for GitHub operations without leaving the terminal:
gh pr create # Create PR
gh pr status # Check PR status
gh pr checks # View CI results
gh issue list # Check issues
gh issue view <number> # Read issue details
gh issue create # Create new issueSquash Commits on Merge:
This project maintains a clean, linear history through squash commits:
- Work in branches: Make as many commits as needed during development
- Rich commit messages: Document your journey in branch commits
- Squash on merge: PRs are squashed into a single commit on main
- Final commit message: Should summarize the entire PR, not just the last commit
When creating a PR, write a good description - it becomes the squash commit message. Include:
- What changed and why
- Key decisions made
- Any gotchas or learnings
- Tests added or modified
Semantic versioning: MAJOR.MINOR.PATCH (e.g., 0.5.0)
- MAJOR: Breaking changes (we're not at 1.0 yet)
- MINOR: New features, significant changes
- PATCH: Bug fixes, small improvements
Releases are managed by goreleaser:
- Version is set via git tags
- Release process documented in README.md
- CHANGELOG.md is updated with each release
- goreleaser handles building for multiple platforms
When bumping versions:
- Update CHANGELOG.md with changes
- Tag the release:
git tag v0.X.Y - Let maintainers handle goreleaser
git log --oneline -n 20- What's been happening?gh issue list- What's being tracked?git status- What's the current state?go build && go test- Does everything work?
- Open/reference an issue - Keep work visible
- Write tests first - Especially functional tests
- Make it work - Get it functionally correct
- Make it right - Clean up, refactor, document
- Commit with context - Rich commit messages
- All tests pass -
go build && go test && go test ./... - Code formatted -
go fmt ./... - Commit with attribution - Use the template above (multiple commits OK in branch)
- Push and create PR - Reference the issue with good description
- Update issue - Mark as complete or pending review
- Squash on merge - PRs are squashed to keep main history clean
Git's power is in context preservation across sessions, agents, and developers.
Picking up someone else's work:
- Find their commit:
git log --oneline -n 20 - Read their message:
git show <commit-sha> - See the changes:
git show <commit-sha> -p - Continue their work with your own commits
Avoiding duplicate work:
- Always check
git log -n 20before starting something new - Search commit messages:
git log --grep="keyword" - Check GitHub issues:
gh issue list
Git commits are permanent project knowledge. They are:
- Context for future sessions and developers
- Documentation of architectural decisions
- Reasoning traces that survive indefinitely
- Messages to your future self and the community
Your commit message quality directly impacts the next agent's and next developer's ability to succeed. Write for them.
- Built by A. Tobey while at Equinix (primarily on the Metal team)
- Forked and maintained by A. Tobey (@tobert) since 2025
- Agent-assisted development begins with v0.6.0
- All commits should use appropriate agent attribution (see section above)
Apache License 2.0 - Copyright (c) 2025 A. Tobey
See LICENSE file for full details.