🔎 loup#
Turn user session recordings into cited pull requests & issues — on Tangled, over the AT Protocol.
loup is a webhook harness you drop into your repo. Point your session-recording provider at it; when a recording comes in, loup finds where the user struggled, locates it in your code, and opens a PR (with a fix) or an issue (for the fuzzy stuff) on Tangled — each citing the exact moment of confusion as a screenshot that deep-links into the recording.
Every artifact it creates — the PR, the issue, the screenshot citations — is a real AT Protocol record. No bespoke backend; the protocol carries the state.
recording (mp4/mov) + session context
│
▼ ffmpeg cuts citation frames at the hotspots
findings ──► localize in your repo ──► PR (fix) | gzipped patch in a round
│ └► issue | flag for humans
▼
open on Tangled with inline, video-deep-linked screenshot citations
Install#
npm install -g loup-cli # installs the `loup` command
Setup#
Run loup init inside the repo you want loup to operate on. It scaffolds two
files (and adds the secret one to .gitignore):
loup.config.json — non-secret, safe to commit:
{
"service": "https://tngl.sh",
"targetRepo": "your-handle.tngl.sh/your-repo", // repo to open PRs/issues on
"targetRepoDid": "did:plc:…", // that repo's DID
"targetBranch": "main",
"port": 4319
}
.env — secrets, never committed:
TANGLED_HANDLE=your-handle.tngl.sh
TANGLED_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx # tngl.sh → Settings → App passwords
# ANTHROPIC_API_KEY=sk-ant-... # optional — turns on the vision pass
No SSH key needed. loup posts over the AT Protocol (it writes pull/issue records to your PDS), not via
git push. The only credential it needs is a Tangled app password — generate one at tngl.sh → Settings → App passwords and paste it into.env. Use an app password, not your account password.
Use#
loup serve # the real pipeline — analyzes & posts to Tangled
loup serve --demo # offline replay of known-good PRs (no LLM, no network)
# from another terminal, send a recording (a link is optional, for deep-link citations):
loup send recording.mov --url https://youtu.be/VIDEO
loup serve posts for real as soon as .env has your Tangled credentials —
it prints the live PR/issue URLs. If those credentials are missing it runs a safe
dry-run (analyzes + drafts, posts nothing) instead of erroring.
loup serve --demo replays a fixed set of real, already-open PRs/issue offline —
handy for a guaranteed demo with no network or API key.
The webhook also accepts a raw POST (multipart/form-data: video, optional
video_url, optional text) — exactly the shape a recording provider would send.
How it works#
When a recording arrives:
- ffmpeg cuts it into one screenshot per second, each with its timestamp burned in.
- loup hands those frames + your app's source code (
sourcePaths) to a Claude model (claude-opus-4-8by default) under a single system prompt. - The model watches the session frame by frame, finds every place the user hit a bug or got confused, cross-references the source for the root cause, and — for things it can fix — returns the corrected file.
- loup turns that corrected file into a patch with
git diff(so it always applies), then opens a PR with it, or files an issue for the fuzzy stuff — each citing the exact frame, deep-linked back into the recording.
Pass optional --text notes alongside the recording (a provider's session events,
your own annotations) to nudge the model — but it works from the recording alone.
Why Tangled / AT Protocol#
A pull request is a sh.tangled.repo.pull record (patch as a gzipped blob in a
round); an issue is sh.tangled.repo.issue; a citation is a sh.tangled.feed.comment
whose markdown body embeds the screenshot blob. loup just writes records to its
own PDS — no clone, no push, no server to run for state. Because contribution is
federated, it can open PRs/issues on any repo, not just its own.
Config#
| File | What |
|---|---|
loup.config.json |
target repo (targetRepo, targetRepoDid, targetBranch), repoRoot (defaults to CWD), sourcePaths (files/dirs to feed the model), port, model |
.env |
TANGLED_HANDLE, TANGLED_APP_PASSWORD, ANTHROPIC_API_KEY (the vision pass), optional LOUP_MODEL (never commit) |
sourcePaths points loup at your UI source (templates, components, pages) so the
model can locate issues and write fixes. loup serve posts for real once .env
has TANGLED_HANDLE + TANGLED_APP_PASSWORD; without them it dry-runs.