Contributing to agentdiff
April 30, 2026 · View on GitHub
Thanks for your interest in contributing. This guide covers bug reports, development setup, adding agent support, and the PR process.
Quick links
Reporting Bugs
Open an issue at github.com/codeprakhar25/agentdiff/issues.
Include:
agentdiff --version- OS, Rust version (
rustc --version), Python version (python3 --version) - Which agent you were using
- Steps to reproduce
- What you expected vs. what happened
- Debug logs (see below)
Capture debug logs:
export AGENTDIFF_DEBUG=1
# reproduce the issue (make an AI edit, commit)
cat ~/.agentdiff/logs/capture-<agent>.log
cat .git/agentdiff/session.jsonl
Development Setup
Prerequisites: Rust 1.85+, Python 3.7+, Git 2.20+
git clone https://github.com/codeprakhar25/agentdiff.git
cd agentdiff
cargo build
Running tests
# Rust tests
cargo test
# Python unit tests (capture script logic)
python3 -m unittest discover -s scripts/tests -v
# MCP server smoke test
python3 scripts/mcp-smoke-test.py
# Full end-to-end test (all 7 agents, simulated payloads)
bash scripts/e2e-test.sh
Installing locally for manual testing
cargo build --release
mkdir -p ~/.local/bin
install -m 0755 target/release/agentdiff ~/.local/bin/agentdiff
install -m 0755 target/release/agentdiff-mcp ~/.local/bin/agentdiff-mcp
# Run configure + init in a throwaway repo
agentdiff configure
mkdir /tmp/test-repo && cd /tmp/test-repo
git init && git config user.email "test@test.com" && git config user.name "Test"
agentdiff init
Project Structure
src/
├── main.rs ← CLI entry point and command routing
├── cli.rs ← Argument definitions (clap)
├── init.rs ← agentdiff configure + agentdiff init logic
├── config.rs ← Global config (load, save, paths)
├── store.rs ← Ledger + session reading
├── data.rs ← Data structures (Entry, LedgerRecord)
├── util.rs ← Shared helpers
├── bin/
│ └── agentdiff-mcp.rs ← MCP stdio server
└── commands/ ← One file per CLI command
scripts/
├── capture-<agent>.py ← Agent-specific capture scripts (stdin JSON → session.jsonl)
├── prepare-ledger.py ← Pre-commit: match staged diff to captured events
├── finalize-ledger.py ← Post-commit: write ledger entry
├── record-context.py ← MCP record_context tool handler
├── opencode-agentdiff.ts ← OpenCode TypeScript plugin template
├── vscode-extension/ ← Copilot VS Code extension (plain JS)
└── tests/ ← Python unit tests
Adding a New Agent
Supporting a new AI agent means two things: a capture script and a hook installation step.
1. Write the capture script
Create scripts/capture-<agentname>.py. The script:
- Reads a JSON payload from stdin
- Resolves the repo root with
git rev-parse --show-toplevel - Writes one JSON entry to
<repo>/.git/agentdiff/session.jsonl
Minimum entry schema:
{
"timestamp": "2026-03-28T10:00:00Z",
"agent": "my-agent",
"model": "model-name-or-unknown",
"session_id": "session-id-or-unknown",
"tool": "Edit",
"file": "src/main.rs",
"abs_file": "/home/user/project/src/main.rs",
"lines": [10, 11, 12],
"prompt": "prompt text or null"
}
Look at scripts/capture-claude.py as the reference implementation — it handles path normalization, repo resolution, and debug logging.
Debug logging — use the shared pattern:
import os, sys
def debug_log(msg):
if os.environ.get("AGENTDIFF_DEBUG"):
log_dir = Path.home() / ".agentdiff" / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
with open(log_dir / "capture-myagent.log", "a") as f:
f.write(f"{datetime.utcnow().isoformat()} {msg}\n")
2. Embed the script in the binary
In src/init.rs, add:
const MYAGENT_CAPTURE_SCRIPT: &str = include_str!("../scripts/capture-myagent.py");
Add it to step_install_scripts():
("capture-myagent.py", MYAGENT_CAPTURE_SCRIPT),
3. Add the hook installation step
Add a step_configure_myagent() function that writes to the agent's global config file (home directory preferred over per-repo). Call it from run_configure().
4. Add CLI flags
In src/cli.rs, add to ConfigureArgs:
/// Skip MyAgent hook setup
#[arg(long)]
pub no_myagent: bool,
Wire it through main.rs and run_configure().
5. Add tests
Add a Python unit test in scripts/tests/test_capture_myagent.py that:
- Pipes a minimal valid JSON payload to the script
- Asserts the resulting
session.jsonlentry has the correctagent,tool, andfilefields
Add an entry to the e2e-test.sh script simulating a real payload.
Code Conventions
Rust
- Follow existing patterns — match the style of adjacent code
- Use
anyhow::Resultfor error propagation; add.context("...")on file I/O - Prefer
dirs::home_dir()over hardcoding~ - All user-visible output uses
coloredcrate:"ok".green(),"!".yellow(),"--".dimmed() - No
unwrap()in non-test code except afterdirs::home_dir()(it won't fail on supported platforms)
Python (capture scripts)
- Target Python 3.7+ — no f-string
=syntax, nomatchstatements - Read from
sys.stdin; resolve paths withpathlib.Path - Write to
session.jsonlwith a file lock (fcntl.flock/msvcrt.locking) - Fail silently on errors that shouldn't break git workflows — log to debug file,
sys.exit(0) - Always resolve repo root with
git rev-parse --show-toplevelfrom the file's directory
Commit messages
Use Conventional Commits:
feat: add MyAgent capture support
fix: handle missing cwd in windsurf payload
docs: add CI integration example
chore: bump serde to 1.0.200
Pull Request Process
-
Fork the repo and branch from
main:git checkout -b feat/my-agent-support -
Make focused changes — one feature or fix per PR.
-
Build and test:
cargo build cargo test python3 -m unittest discover -s scripts/tests python3 scripts/mcp-smoke-test.py bash scripts/e2e-test.sh -
Open a PR with:
- What changed and why
- How to test it
Closes #<issue>if applicable
-
Address review feedback — push updates and reply to comments.
CI checks
All PRs must pass:
cargo build --locked— no compile errorscargo test --locked— all Rust tests greenpython3 -m unittest discover -s scripts/tests— all Python tests greenpython3 scripts/mcp-smoke-test.py— MCP server smoke test
License
By contributing, you agree that your contributions will be licensed under the MIT License and Apache-2.0 License.