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.


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.jsonl entry has the correct agent, tool, and file fields

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::Result for error propagation; add .context("...") on file I/O
  • Prefer dirs::home_dir() over hardcoding ~
  • All user-visible output uses colored crate: "ok".green(), "!".yellow(), "--".dimmed()
  • No unwrap() in non-test code except after dirs::home_dir() (it won't fail on supported platforms)

Python (capture scripts)

  • Target Python 3.7+ — no f-string = syntax, no match statements
  • Read from sys.stdin; resolve paths with pathlib.Path
  • Write to session.jsonl with 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-toplevel from 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

  1. Fork the repo and branch from main:

    git checkout -b feat/my-agent-support
    
  2. Make focused changes — one feature or fix per PR.

  3. 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
    
  4. Open a PR with:

    • What changed and why
    • How to test it
    • Closes #<issue> if applicable
  5. Address review feedback — push updates and reply to comments.

CI checks

All PRs must pass:

  • cargo build --locked — no compile errors
  • cargo test --locked — all Rust tests green
  • python3 -m unittest discover -s scripts/tests — all Python tests green
  • python3 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.