Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ fixbuddy reads open GitHub issues, asks one agent to verify and fix each issue,

The goal is controlled automation: one issue per branch, one issue per PR, explicit labels for every outcome, and full logs for every agent call.

<!--
Demo GIF goes above the fold here. To record one: run `fixbuddy.sh --dry-run`
then a real VERIFY -> FIX -> REVIEW -> PR against a small playground repo with
`asciinema rec demo.cast`, convert with `agg demo.cast docs/demo.gif`, then embed:
![fixbuddy demo](docs/demo.gif)
-->
<p align="center">
<img src="docs/demo.gif" alt="fixbuddy turning a GitHub issue into a reviewed, merged pull request" width="820">
</p>

<sub>The branch, commit, diff, and push above are real; the AI agents and GitHub calls are stubbed for a deterministic, offline recording — see <a href="docs/demo">docs/demo</a>.</sub>

## Why fixbuddy

Expand Down
Binary file added docs/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions docs/demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Demo GIF

`docs/demo.gif` (shown at the top of the main README) is recorded with
[VHS](https://github.com/charmbracelet/vhs).

To keep it reproducible and offline, the recording uses **deterministic demo
doubles**, not real services:

- `bin/agent` — stands in for the AI coding CLIs (`claude` / `codex`). It emits the
`DONE-*` markers fixbuddy expects and, in the FIX stage, makes a **real** small
code change, so the diff fixbuddy reviews and the PR it opens are genuine.
- `bin/gh` — stands in for the GitHub CLI with canned responses.

Everything else is the **real** fixbuddy pipeline: branch creation, the local
commit, the `git diff`, `git push` (to a local bare repo), and the
label / state-machine flow.

## Regenerate

```bash
bash docs/demo/gen.sh
```

Requires `vhs` on `PATH`. Writes `docs/demo.gif`. Edit `demo.tape` to change the
timing/size/theme.
29 changes: 29 additions & 0 deletions docs/demo/bin/agent
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Deterministic demo double for an AI coding CLI (used as claude/codex when recording
# docs/demo.gif). This is NOT a real agent. It emits the DONE-* markers fixbuddy expects
# and, in the FIX stage, makes a REAL small code fix so the diff fixbuddy reviews and the
# PR it opens are genuine. Stage is detected from the prompt fixbuddy pipes on stdin.
# See docs/demo/README.md.
prompt="$(cat)"

case "$prompt" in
*"implementing a fix"*)
sleep 1
( cd "$DEMO_PROJECT" \
&& sed -i.bak 's/a - b/a + b/' src/calc.py && rm -f src/calc.py.bak \
&& git add -A \
&& git commit -q -m "Fix add(): return a + b, not a - b" )
echo "Found the operator bug in add() and corrected it (a - b -> a + b)."
echo "DONE-FIX-APPLIED"
;;
*"independent senior code reviewer"*)
sleep 1
echo "The diff changes 'a - b' to 'a + b' in add(). Correct, minimal, in scope."
echo "DONE-APPROVED"
;;
*)
sleep 1
echo "Reproduced: add(2, 3) returns -1 instead of 5. The bug is real."
echo "DONE-PROCEED"
;;
esac
17 changes: 17 additions & 0 deletions docs/demo/bin/gh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Deterministic demo double for the GitHub CLI, used only when recording docs/demo.gif.
# Returns canned responses so fixbuddy's pipeline runs end-to-end offline. NOT real.
# Everything else (git branch/commit/diff/push to a local bare repo) is genuine.
case "$1 $2" in
"repo view") echo "main" ;;
"issue list")
case "$*" in
*fix:pr-open*) echo "[]" ;; # the "unstick" scan finds nothing
*) echo '[{"number":7,"title":"add() returns the wrong result","labels":[{"name":"bug"},{"name":"severity:medium"}],"url":"https://github.com/acme/calc/issues/7","body":"add(2, 3) should return 5 but returns -1. The operator looks wrong."}]' ;;
esac ;;
"pr create") echo "https://github.com/acme/calc/pull/12" ;;
"pr view") echo "true" ;; # PR reported as merged -> clean "merged" ending
"pr list") echo "" ;;
*) : ;; # label create/list, issue edit/comment, pr merge, auth setup-git -> no-op OK
esac
exit 0
19 changes: 19 additions & 0 deletions docs/demo/demo.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# fixbuddy demo — recorded with VHS (https://github.com/charmbracelet/vhs).
# Run via: bash docs/demo/gen.sh (sets up PATH/env, then runs `vhs` on this file)
Output docs/demo.gif

Require fixbuddy

Set Shell "bash"
Set FontSize 16
Set Width 1320
Set Height 600
Set Padding 24
Set Theme "Dracula"
Set TypingSpeed 35ms

Sleep 600ms
Type "fixbuddy --repo acme/calc --project /tmp/fixbuddy-demo/playground --yes"
Sleep 500ms
Enter
Sleep 9s
43 changes: 43 additions & 0 deletions docs/demo/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Regenerate docs/demo.gif with VHS, using deterministic demo doubles (no real services).
# Usage: bash docs/demo/gen.sh (requires `vhs` on PATH)
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
DEMO_DIR="$REPO_ROOT/docs/demo"
WORK=/tmp/fixbuddy-demo

command -v vhs >/dev/null || { echo "vhs not found — install https://github.com/charmbracelet/vhs"; exit 1; }

rm -rf "$WORK"
mkdir -p "$WORK/bin" "$WORK/home"

# Runtime bin: stub agents + stub gh + fixbuddy, all on PATH.
chmod +x "$DEMO_DIR/bin/agent" "$DEMO_DIR/bin/gh"
ln -sf "$DEMO_DIR/bin/agent" "$WORK/bin/claude"
ln -sf "$DEMO_DIR/bin/agent" "$WORK/bin/codex"
ln -sf "$DEMO_DIR/bin/gh" "$WORK/bin/gh"
ln -sf "$REPO_ROOT/fixbuddy.sh" "$WORK/bin/fixbuddy"

# Playground repo with a real bug and a real origin (so `git push` genuinely works).
git init -q --bare "$WORK/origin.git"
git clone -q "$WORK/origin.git" "$WORK/playground" 2>/dev/null
(
cd "$WORK/playground"
git config user.email demo@example.com
git config user.name "demo"
git checkout -q -b main
mkdir -p src
printf 'def add(a, b):\n return a - b # BUG: should be a + b\n' > src/calc.py
git add -A
git commit -q -m "calc: initial"
git push -q -u origin main
)

export PATH="$WORK/bin:$PATH"
export DEMO_PROJECT="$WORK/playground"
export HOME="$WORK/home" # isolate fixbuddy run logs

cd "$REPO_ROOT"
vhs "$DEMO_DIR/demo.tape"
echo "Wrote $REPO_ROOT/docs/demo.gif"