Skip to content

Repository files navigation

Riptide

Riptide — Self-Hosted GitHub App (Three Bots)

https://excalidraw.com/#json=xGk0JJ9K3Fj3w-jrNM1kw,iEepj-mogls0LeSUBi03JA

AI-powered code review that runs in your infrastructure. Three bots, zero bloat.

Bot 1 — Companion: Posts TL;DR comments on PRs with graphify-informed blast radius. Bot 2 — Riptide Review: Deep-think autonomous code review for large, settled PRs. Bot 3 — Proofshotter: Visual verification via Playwright captures on UI changes.


Architecture

GitHub Webhook
  → FastAPI /webhook/github (verify signature, route event)
      → Companion thread (TL;DR + graphify blast radius)
  → GitHub API (post TL;DR comment)

Cron (every 15 min)
  → riptide/deepthink.py (poll open PRs, filter by LOC + staleness)
  → Hermes cron session (deep-think + graphify analysis)
  → GitHub API (post review comment)

Cron (every 10 min)
  → riptide/proofshotter.py (poll open PRs for UI changes)
  → Playwright captures on dev instance
  → GitHub API (post visual evidence comment)

Bot 1: Companion (Webhook-Triggered)

Every PR triggers a friendly TL;DR comment explaining what changed and why it matters.

Trigger: pull_request (opened, reopened, synchronize)
Output:  "## ✨ TL;DR" comment with ELI5 + Blast Radius + ProofShot (if UI)
Model:  qwen2.5-coder:7b (via local Ollama)

Example output:

## ✨ TL;DR

@alice — Adds atomic per-session draft persistence to reduce large-session 
latency. Replaces full-session rewrites with atomic .drafts/ file writes 
via DraftOptimization.save_draft(). 
Also touches: models.py, routes.py.

**📊 Blast Radius**
Analyzed 3 files, 12 related code paths found.

**🧒 ELI5**
This adds a new thing! Like getting a new piece for your LEGO set that 
makes it even cooler.

![sparkles](https://media.giphy.com/media/26tOZ42Mg6pbTUPHW/giphy.gif)

---
<sub>🤖 Generated by Riptide · PR review via local Ollama (qwen2.5-coder:7b) · 
`@riptide-bot companion skip` to opt out</sub>

Commands

Reply to any PR comment with @riptide-bot:

Command Effect Bot
@riptide-bot review Trigger deep-think review now Bot 2
@riptide-bot full review Same (alias) Bot 2
@riptide-bot deepthink Same (alias) Bot 2
@riptide-bot companion skip Stop companion TLDR on this PR Bot 1
@riptide-bot companion resume Re-enable companion TLDR Bot 1

Bot 2: Riptide Review (Cron-Triggered)

Autonomous deep-think code review for large, settled PRs. Runs every 15 minutes.

Trigger: Cron polling (every 15 min)
Filter:  >100 LOC changes + unchanged 30+ min + (our repo OR our PR)
Output:  Hermes cron session with deep-think + graphify analysis
Skills:  github-pr-lifecycle + deep-think
Model:  custom:LongCat (LongCat-2.0) preferred

The deep-think loop:

  1. SURFACE — Fetch and restate what the PR changes
  2. EXPLORE — What code paths are affected? Graphify blast radius?
  3. CHALLENGE — Could the change have side effects? Simpler approach?
  4. SYNTHESIZE — Advise next steps (approve, request changes, follow-ups)
  5. VALIDATE — Run tests if claims are testable

Bot 3: Proofshotter (Cron-Triggered)

Visual verification for UI changes. Runs every 10 minutes.

Trigger: Cron polling (every 10 min)
Filter:  UI files changed + not draft + stale 5+ min
Output:  PR comment with GIF + screenshots of UI changes
Config:  proofshot.config.json (optional — defaults to localhost:8788)

Proofshotter captures screenshots of the dev instance (default http://localhost:8788) and posts them as visual evidence on the PR. Requires Playwright installed on the host.


Repo Structure

riptide/
├── riptide/
│   ├── github_app.py      # JWT auth, GitHub API client
│   ├── companion.py       # Bot 1: TL;DR + ELI5 + ProofShot comment generator
│   ├── deepthink.py       # Bot 2: Cron polling + Hermes session spawner
│   ├── proofshotter.py    # Bot 3: Cron-polled visual verification
│   ├── webhook.py         # FastAPI server (companion trigger, installation sync)
│   └── __init__.py
├── server.py              # Uvicorn entry point
├── requirements.txt       # fastapi, uvicorn, pydantic, cryptography, requests, graphifyy
├── Dockerfile
├── docker-compose.yml
├── SKILL.md               # AI agent skill definition
└── start.sh               # Dev start script

Quick Start

Prerequisites

  • Python 3.11+
  • Ollama with qwen2.5-coder:7b and nomic-embed-text
  • GitHub App registered with webhook permissions

Install

git clone <repo>
cd riptide

# Python deps
pip install -r requirements.txt

# Ollama models
ollama pull qwen2.5-coder:7b
ollama pull nomic-embed-text

# Graphify (for blast radius analysis)
pip install graphifyy

Configure

# .env
GITHUB_APP_ID=4262983
GITHUB_PRIVATE_KEY_PATH=/home/sc/workspace/riptide/github-private-key.pem
GITHUB_WEBHOOK_SECRET=your_secret
GITHUB_APP_SLUG=riptide-review
RIPTIDE_DATA_DIR=/tmp/riptide-data

# Companion (Bot 1)
RIPTIDE_COMPANION_REPOS=owner/repo1,owner/repo2
RIPTIDE_COMPANION_MODEL=qwen2.5-coder:7b
COMPANION_ENABLE_GRAPHIFY=1
OLLAMA_BASE_URL=http://localhost:43311

# Riptide Review (Bot 2)
RIPTIDE_WATCHED_REPOS=ChonSong/riptide,ChonSong/hermes-webui
RIPTIDE_STALENESS_MINUTES=30
RIPTIDE_MIN_LOC_CHANGED=100

Run

# Development
bash start.sh

# Docker
docker compose up -d --build

# Production (Cloudflare Tunnel)
cloudflared tunnel --config ~/.cloudflared/config.yml run

Set Up Bot 2 Cron

# Add to Hermes cron (every 15 minutes)
hermes cron create "*/15 * * * *" \
  --name "riptide-review-poll" \
  --script /home/sc/workspace/riptide/riptide/deepthink.py

Companion Configuration

Variable Description Default
RIPTIDE_COMPANION_REPOS Comma-separated owner/repo list (disabled)
RIPTIDE_COMPANION_MODEL Ollama model for TLDR generation qwen2.5-coder:7b
COMPANION_ENABLE_GRAPHIFY Enable blast radius analysis 1

Riptide Review Configuration

Variable Description Default
RIPTIDE_WATCHED_REPOS Comma-separated owner/repo list (all ChonSong repos)
RIPTIDE_OUR_USERNAME GitHub username for authorship check ChonSong
RIPTIDE_OUR_ORG GitHub org for ownership check ChonSong
RIPTIDE_STALENESS_MINUTES Minutes since last update to qualify 30
RIPTIDE_MIN_LOC_CHANGED Minimum LOC changes to trigger 100

API Reference

Webhook Endpoint

POST /webhook/github
Headers:
  X-Hub-Signature-256: sha256=...
  X-GitHub-Event: pull_request
  X-GitHub-Delivery: uuid

Health Check

GET /health
→ {"status": "ok", "app": "riptide"}

GitHub Events Handled

Event Action Output
pull_request opened/reopened/synchronize Companion TLDR TL;DR comment
issue_comment @riptide-bot companion skip/resume Control companion Confirmation reply
issue_comment @riptide-bot review / full review / deepthink Trigger Bot 2 deep-think Confirmation reply
installation created/deleted Sync repo list Metadata DB updated

Comparison with Alternatives

Riptide Octopus CodeRabbit
Stack Python / FastAPI Next.js + tRPC SaaS
Vector store None (graphify for blast radius) Qdrant (Docker) Proprietary
Deployment Single container Multi-service Cloud-only
Auth GitHub App JWT JWT + Prisma OAuth
TLDR Comments ✅ (Companion)
ELI5 Explanations
Deep Thinking ✅ (Bot 2)
Graphify Blast Radius
Self-Hosted

Development

Run Tests

# Syntax check
python -m py_compile riptide/companion.py riptide/deepthink.py riptide/webhook.py riptide/github_app.py

Simulate Webhook

# Send test payload
python3 -c "
import requests, json, hmac, hashlib
payload = {'action': 'opened', 'number': 1, 'pull_request': {'title': 'test', 'user': {'login': 'test'}, 'head': {'sha': 'abc'}},'repository': {'full_name': 'owner/repo', 'name': 'repo'}, 'installation': {'id': 123}}
body = json.dumps(payload)
sig = 'sha256=' + hmac.new(b'secret', body.encode(), hashlib.sha256).hexdigest()
requests.post('http://localhost:8477/webhook/github', data=body, headers={'X-Hub-Signature-256': sig, 'X-GitHub-Event': 'pull_request'})
"

Local Test

# Run unit tests
source .env 2>/dev/null  # optional — tests with no-op fallback
python3 -m pytest riptide/tests/ -v

# Manual webhook smoke-test
python3 -c "
import requests, json, hmac, hashlib
payload = {'action': 'opened', 'number': 1, 'pull_request': {'title': 'test', 'user': {'login': 'test'}, 'head': {'sha': 'abc'}},'repository': {'full_name': 'owner/repo', 'name': 'repo'}, 'installation': {'id': 123}}
body = json.dumps(payload)
sig = 'sha256=' + hmac.new(b'secret', body.encode(), hashlib.sha256).hexdigest()
requests.post('http://localhost:8477/webhook/github', data=body, headers={'X-Hub-Signature-256': sig, 'X-GitHub-Event': 'pull_request'})
"

# Server health check (requires running service)
curl -s http://localhost:8477/health

License

Modified MIT — see LICENSE.md.

Test PR

Test commit for webhook GIF verification.

test commit

final test

About

No description, website, or topics provided.

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages