Thanks for your interest in contributing. This guide covers bug reports, development setup, adding agent support, and the PR process.
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.jsonlPrerequisites: Rust 1.85+, Python 3.7+, Git 2.20+
git clone https://github.com/codeprakhar25/agentdiff.git
cd agentdiff
cargo build# 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.shcargo 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 initsrc/
├── 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
Supporting a new AI agent means two things: a capture script and a hook installation step.
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")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),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().
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().
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.
- 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)
- 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
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
-
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.
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
By contributing, you agree that your contributions will be licensed under the MIT License and Apache-2.0 License.