This repository has no description
1# 🔎 loup
2
3**Turn user session recordings into cited pull requests & issues — on [Tangled](https://tangled.org), over the AT Protocol.**
4
5loup is a webhook harness you drop into your repo. Point your session-recording
6provider at it; when a recording comes in, loup finds where the user struggled,
7locates it in your code, and opens a **PR** (with a fix) or an **issue** (for the
8fuzzy stuff) on Tangled — each citing the **exact moment of confusion** as a
9screenshot that deep-links into the recording.
10
11Every artifact it creates — the PR, the issue, the screenshot citations — is a
12real AT Protocol record. No bespoke backend; the protocol carries the state.
13
14```
15 recording (mp4/mov) + session context
16 │
17 ▼ ffmpeg cuts citation frames at the hotspots
18 findings ──► localize in your repo ──► PR (fix) | gzipped patch in a round
19 │ └► issue | flag for humans
20 ▼
21 open on Tangled with inline, video-deep-linked screenshot citations
22```
23
24## Install
25
26```bash
27npm install -g loup-cli # installs the `loup` command
28```
29
30## Setup
31
32Run **`loup init`** inside the repo you want loup to operate on. It scaffolds two
33files (and adds the secret one to `.gitignore`):
34
35**`loup.config.json`** — non-secret, safe to commit:
36
37```jsonc
38{
39 "service": "https://tngl.sh",
40 "targetRepo": "your-handle.tngl.sh/your-repo", // repo to open PRs/issues on
41 "targetRepoDid": "did:plc:…", // that repo's DID
42 "targetBranch": "main",
43 "port": 4319
44}
45```
46
47**`.env`** — secrets, never committed:
48
49```bash
50TANGLED_HANDLE=your-handle.tngl.sh
51TANGLED_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx # tngl.sh → Settings → App passwords
52# ANTHROPIC_API_KEY=sk-ant-... # optional — turns on the vision pass
53```
54
55> **No SSH key needed.** loup posts over the AT Protocol (it writes pull/issue
56> *records* to your PDS), not via `git push`. The only credential it needs is a
57> Tangled **app password** — generate one at **tngl.sh → Settings → App
58> passwords** and paste it into `.env`. Use an app password, not your account
59> password.
60
61## Use
62
63```bash
64loup serve # the real pipeline — analyzes & posts to Tangled
65loup serve --demo # offline replay of known-good PRs (no LLM, no network)
66
67# from another terminal, send a recording (a link is optional, for deep-link citations):
68loup send recording.mov --url https://youtu.be/VIDEO
69```
70
71`loup serve` **posts for real** as soon as `.env` has your Tangled credentials —
72it prints the live PR/issue URLs. If those credentials are missing it runs a safe
73**dry-run** (analyzes + drafts, posts nothing) instead of erroring.
74
75`loup serve --demo` replays a fixed set of real, already-open PRs/issue offline —
76handy for a guaranteed demo with no network or API key.
77
78The webhook also accepts a raw POST (`multipart/form-data`: `video`, optional
79`video_url`, optional `text`) — exactly the shape a recording provider would send.
80
81## How it works
82
83When a recording arrives:
84
851. **ffmpeg** cuts it into **one screenshot per second**, each with its timestamp
86 burned in.
872. loup hands those frames **+ your app's source code** (`sourcePaths`) to a
88 Claude model (**`claude-opus-4-8`** by default) under a **single system prompt**.
893. The model watches the session frame by frame, finds every place the user hit a
90 bug or got confused, cross-references the source for the root cause, and — for
91 things it can fix — returns the **corrected file**.
924. loup turns that corrected file into a patch with `git diff` (so it **always
93 applies**), then opens a **PR** with it, or files an **issue** for the fuzzy
94 stuff — each citing the exact frame, deep-linked back into the recording.
95
96Pass optional `--text` notes alongside the recording (a provider's session events,
97your own annotations) to nudge the model — but it works from the recording alone.
98
99## Why Tangled / AT Protocol
100
101A pull request is a `sh.tangled.repo.pull` record (patch as a gzipped blob in a
102round); an issue is `sh.tangled.repo.issue`; a citation is a `sh.tangled.feed.comment`
103whose markdown body embeds the screenshot blob. loup just writes records to its
104own PDS — no clone, no push, no server to run for state. Because contribution is
105federated, it can open PRs/issues on **any** repo, not just its own.
106
107## Config
108
109| File | What |
110|---|---|
111| `loup.config.json` | target repo (`targetRepo`, `targetRepoDid`, `targetBranch`), `repoRoot` (defaults to CWD), **`sourcePaths`** (files/dirs to feed the model), `port`, `model` |
112| `.env` | `TANGLED_HANDLE`, `TANGLED_APP_PASSWORD`, `ANTHROPIC_API_KEY` (the vision pass), optional `LOUP_MODEL` (never commit) |
113
114`sourcePaths` points loup at your UI source (templates, components, pages) so the
115model can locate issues and write fixes. `loup serve` posts for real once `.env`
116has `TANGLED_HANDLE` + `TANGLED_APP_PASSWORD`; without them it dry-runs.