bug2context 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. bug2context-0.1.0/.github/workflows/ci.yml +50 -0
  2. bug2context-0.1.0/.github/workflows/release.yml +27 -0
  3. bug2context-0.1.0/.gitignore +8 -0
  4. bug2context-0.1.0/LICENSE +21 -0
  5. bug2context-0.1.0/PKG-INFO +232 -0
  6. bug2context-0.1.0/PLAN-bug2context.md +200 -0
  7. bug2context-0.1.0/README.md +202 -0
  8. bug2context-0.1.0/RELEASE.md +111 -0
  9. bug2context-0.1.0/examples/report.md +26 -0
  10. bug2context-0.1.0/pyproject.toml +53 -0
  11. bug2context-0.1.0/src/bug2context/__init__.py +1 -0
  12. bug2context-0.1.0/src/bug2context/android.py +179 -0
  13. bug2context-0.1.0/src/bug2context/cli.py +90 -0
  14. bug2context-0.1.0/src/bug2context/config.py +93 -0
  15. bug2context-0.1.0/src/bug2context/mcp_server.py +119 -0
  16. bug2context-0.1.0/src/bug2context/menu.py +160 -0
  17. bug2context-0.1.0/src/bug2context/pipeline/__init__.py +0 -0
  18. bug2context-0.1.0/src/bug2context/pipeline/assemble.py +216 -0
  19. bug2context-0.1.0/src/bug2context/pipeline/audio.py +94 -0
  20. bug2context-0.1.0/src/bug2context/pipeline/frames.py +185 -0
  21. bug2context-0.1.0/src/bug2context/pipeline/logs.py +187 -0
  22. bug2context-0.1.0/src/bug2context/pipeline/ocr.py +207 -0
  23. bug2context-0.1.0/tests/__init__.py +0 -0
  24. bug2context-0.1.0/tests/conftest.py +116 -0
  25. bug2context-0.1.0/tests/test_android.py +159 -0
  26. bug2context-0.1.0/tests/test_assemble.py +162 -0
  27. bug2context-0.1.0/tests/test_audio.py +106 -0
  28. bug2context-0.1.0/tests/test_frames.py +195 -0
  29. bug2context-0.1.0/tests/test_logs.py +315 -0
  30. bug2context-0.1.0/tests/test_mcp_server.py +95 -0
  31. bug2context-0.1.0/tests/test_menu.py +135 -0
  32. bug2context-0.1.0/tests/test_ocr.py +295 -0
  33. bug2context-0.1.0/uv.lock +1463 -0
@@ -0,0 +1,50 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ # ubuntu proves the tesseract/no-Vision path; macos is the dev platform.
14
+ os: [ubuntu-latest, macos-latest]
15
+ python: ["3.12", "3.13"]
16
+ runs-on: ${{ matrix.os }}
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install ffmpeg (Linux)
21
+ if: runner.os == 'Linux'
22
+ run: sudo apt-get update && sudo apt-get install -y ffmpeg
23
+
24
+ - name: Install ffmpeg (macOS)
25
+ if: runner.os == 'macOS'
26
+ run: brew install ffmpeg
27
+
28
+ - uses: astral-sh/setup-uv@v5
29
+ with:
30
+ enable-cache: true
31
+
32
+ - run: uv sync --python ${{ matrix.python }} --extra ocr
33
+
34
+ - run: uv run pytest -q
35
+
36
+ build:
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: astral-sh/setup-uv@v5
41
+ - run: uv build
42
+ - name: Install the wheel on its own and run it
43
+ run: |
44
+ uv venv /tmp/clean
45
+ uv pip install --python /tmp/clean/bin/python dist/*.whl
46
+ /tmp/clean/bin/bug2context --help
47
+ - uses: actions/upload-artifact@v4
48
+ with:
49
+ name: dist
50
+ path: dist/
@@ -0,0 +1,27 @@
1
+ name: Release
2
+
3
+ # Publishing is deliberately manual: push a tag when you actually mean to ship.
4
+ on:
5
+ push:
6
+ tags: ["v*"]
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write # PyPI trusted publishing — no API token to leak
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v5
17
+
18
+ - name: Fail if the tag and pyproject version disagree
19
+ run: |
20
+ tag="${GITHUB_REF_NAME#v}"
21
+ version=$(grep -m1 '^version' pyproject.toml | cut -d'"' -f2)
22
+ test "$tag" = "$version" || {
23
+ echo "tag $tag != pyproject $version"; exit 1;
24
+ }
25
+
26
+ - run: uv build
27
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ bundle/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Emanuel Duran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.5
2
+ Name: bug2context
3
+ Version: 0.1.0
4
+ Summary: Turns bug screen recordings into structured, chronological context for AI agents.
5
+ Project-URL: Homepage, https://github.com/emanueld92/bug2context
6
+ Project-URL: Issues, https://github.com/emanueld92/bug2context/issues
7
+ Author: Emanuel Duran
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: bug-report,claude,debugging,llm,mcp,ocr,screen-recording
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Multimedia :: Video
17
+ Classifier: Topic :: Software Development :: Bug Tracking
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.12
20
+ Requires-Dist: imagehash>=4.3
21
+ Requires-Dist: mcp>=1.2
22
+ Requires-Dist: pillow>=11.0
23
+ Requires-Dist: typer>=0.15
24
+ Provides-Extra: audio
25
+ Requires-Dist: faster-whisper>=1.1; extra == 'audio'
26
+ Provides-Extra: ocr
27
+ Requires-Dist: pyobjc-framework-vision>=10.3; (sys_platform == 'darwin') and extra == 'ocr'
28
+ Requires-Dist: pytesseract>=0.3.13; extra == 'ocr'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # bug2context
32
+
33
+ Turns a screen recording of a bug into structured, chronological context an AI
34
+ agent can actually read.
35
+
36
+ Screenshots lose the story: bugs happen fast, a still frame has no timeline, and
37
+ the logs and the narration live somewhere else entirely. LLMs don't take video.
38
+ This distills a recording into one markdown chronology — key frames, the text on
39
+ screen, device logs and spoken narration, all on the same clock.
40
+
41
+ - **No SDK.** Works on any video, including one someone sent you over WhatsApp.
42
+ - **Local-first.** Nothing leaves your machine. No account, no API key, no cloud.
43
+ - **MCP-native.** Claude Code calls it as a tool; you just point at the file.
44
+
45
+ ```bash
46
+ uv run bug2context # guided menu
47
+ uv run bug2context process bug.mp4 --out bundle/
48
+ ```
49
+
50
+ Running it with no arguments opens a menu — record an Android device, process a
51
+ video you already have, or list previous bundles — and writes to
52
+ `~/.bug2context/`, which is where the MCP server looks. Everything it does is
53
+ reachable through the flags below; it just stops you having to remember them.
54
+
55
+ ## What comes out
56
+
57
+ ```
58
+ - `[00:00]` 🖼 `frames/frame_001_00-00.png` — start of recording
59
+ OCR: Cart 2 items
60
+ - `[00:04]` 🖼 `frames/frame_002_00-04.png` — screen changed
61
+ OCR: Applying discount..
62
+ - `[00:04]` 📋 W/DiscountEngine: rate field missing
63
+ - `[00:06]` 🖼 `frames/frame_003_00-06.png` — screen changed
64
+ OCR: TypeError: cannot read 'rate'
65
+ - `[00:06]` 📋 E/RideService: NullPointerException at RideRequest.kt:142
66
+ - `[00:06]` 📋 F/AndroidRuntime: FATAL EXCEPTION: main
67
+ - `[00:06]` 🎙 "ahí está, explota al aplicar el descuento"
68
+ ```
69
+
70
+ A full run is in [examples/report.md](examples/report.md).
71
+
72
+ ```
73
+ bundle/
74
+ ├── report.md the chronology above
75
+ ├── frames/ the key frames, named by timestamp
76
+ └── meta.json the same data, structured, for programmatic use
77
+ ```
78
+
79
+ ## Install
80
+
81
+ Requires [`ffmpeg`](https://ffmpeg.org) on PATH (`brew install ffmpeg`).
82
+
83
+ ```bash
84
+ git clone https://github.com/emanueld92/bug2context
85
+ cd bug2context
86
+ uv sync --extra ocr # OCR: Apple Vision on macOS, tesseract elsewhere
87
+ uv sync --extra audio # optional: spoken narration
88
+ ```
89
+
90
+ Every stage is optional and degrades quietly: no OCR backend still gives you
91
+ frames, no audio track still gives you the visual timeline.
92
+
93
+ ## Use it from Claude Code
94
+
95
+ ```bash
96
+ claude mcp add bug2context -- uvx --from /path/to/bug2context bug2context-mcp
97
+ ```
98
+
99
+ Then just ask: *"analiza este video del bug: ~/Desktop/crash.mp4"*.
100
+
101
+ | Tool | Purpose |
102
+ |---|---|
103
+ | `analyze_bug_video` | Video → chronological report (returns the text itself) |
104
+ | `get_frame` | Fetch one frame as an image so the agent can look at it |
105
+ | `list_bundles` | Previously processed bundles, newest first |
106
+
107
+ Bundles default to `~/.bug2context/<video>-<timestamp>/`.
108
+
109
+ ## Android: screen + logcat in one command
110
+
111
+ ```bash
112
+ uv run bug2context record --seconds 60 --package com.example.app --out bundle/
113
+ ```
114
+
115
+ Reproduce the bug while it records. Both streams start from a single host
116
+ timestamp — that anchor is what makes a stack trace land next to the frame
117
+ showing the crash.
118
+
119
+ **Pass `--package`.** Level filtering alone is not enough on a real phone: a
120
+ 2 min capture held 7,634 W/E/F lines and *none* came from the app under test —
121
+ they were all `AppOpsControllerImpl`, `GNSSMGT` and friends. Scoping to the
122
+ app's process left 35. Crash tags (`AndroidRuntime`, `DEBUG`) are kept
123
+ regardless of process, since a crash report is the whole point.
124
+
125
+ Logs are captured unfiltered and scoped afterwards, so an app that crashes and
126
+ restarts still has its death recorded. Consecutive identical entries fold into
127
+ one with a `(×N)` count — framework chatter arrives in bursts of fifteen.
128
+
129
+ Already have a log file? Merge it into any video:
130
+
131
+ ```bash
132
+ uv run bug2context process bug.mp4 --logcat logcat.txt --log-tag RideService
133
+ ```
134
+
135
+ Reads both `-v time` and `-v threadtime`. `--log-levels` defaults to `WEF` and
136
+ `--log-max-lines` to 80, ranked by severity so a cap never trades the crash for
137
+ boot chatter. `--log-pid` scopes to a process when you already know it. If the device clock and the
138
+ recorder disagree, `--log-offset` shifts everything by N seconds; with no
139
+ `--video-started-at`, the first log entry becomes the anchor.
140
+
141
+ `screenrecord` caps at 3 minutes, so `record` refuses longer rather than handing
142
+ back a silently truncated video. Ctrl-C cuts a recording short safely — the file
143
+ is finalised on the device first, because killing the local adb leaves one
144
+ `ffprobe` cannot open.
145
+
146
+ Videos over 10 minutes are refused with the `ffmpeg` command to trim them
147
+ (`--max-duration 0` overrides). Nothing scales badly with length except time,
148
+ but it scales linearly: decoding alone runs at about a third of real time.
149
+
150
+ ## Narration
151
+
152
+ ```bash
153
+ uv run bug2context process bug.mp4 --transcribe --language es
154
+ ```
155
+
156
+ Off by default: it downloads a model on first use and is the slowest stage,
157
+ while most recordings have no voice-over. Skipped automatically when there is no
158
+ audio track, or the track is quieter than −50 dB — whisper invents confident
159
+ sentences out of silence, so that guard is about output quality, not just speed.
160
+
161
+ ## How frames get picked
162
+
163
+ ffmpeg over-produces candidates (scene cuts **and** a fixed interval, so slowly
164
+ changing screens are not skipped), then perceptual hashing collapses the
165
+ near-duplicates. `--max-frames` caps the result, always keeping the opening frame
166
+ and then whichever changed most.
167
+
168
+ | Flag | Default | Notes |
169
+ |---|---|---|
170
+ | `--scene-threshold` | `0.08` | ffmpeg scene score for a cut |
171
+ | `--interval-seconds` | `1.0` | forced sample when no cut fires |
172
+ | `--max-frames` | auto | one frame per 2 s, between 20 and 40 |
173
+ | `--phash-distance` | `4` | below this, frames count as duplicates |
174
+ | `--ocr-upscale` | `2` | enlarge before OCR; skipped above 1600 px wide |
175
+
176
+ Measured end to end, OCR on:
177
+
178
+ | Source | Result | Time |
179
+ |---|---|---|
180
+ | synthetic 3 min, 1080×1920 | 19 frames | 8.5 s |
181
+ | Android capture, 2 min, 720×1612 @ ~12 fps | 118 candidates → 20 frames | 16 s |
182
+ | macOS capture, 90 s, 2880×1864 @ 60 fps | 97 candidates → 18 frames | 86 s |
183
+
184
+ Recognised text is cached by frame content in `~/.cache/bug2context/`, so
185
+ re-running a video with different settings only pays for what actually changed —
186
+ measured 9.0 s cold against 3.3 s warm on a 44 s clip, identical output.
187
+ Extracting the same video twice yields byte-identical frames, which is what
188
+ makes the cache safe. Delete the directory to reset it.
189
+
190
+ Retina desktop recordings are the slow case and there is no trick to remove:
191
+ the cost is decoding 60 fps at 5.4 megapixels (26 s) plus OCR, which Vision
192
+ charges at ~1.6 s per frame regardless of size. Budget roughly real time for
193
+ those; phone captures stay far under it.
194
+
195
+ On `--ocr-upscale`: a stack trace on a recompressed 1080×1920 frame read as
196
+ `Null PointerSxception Ride Requestkt14` at 1× and correctly as
197
+ `NullPointerException Ride Request kt 142` at 3×. Raise it for badly
198
+ recompressed sources — ask for the video as a file, not as a WhatsApp video.
199
+ It is skipped on frames already 1600 px or wider, where it changed nothing
200
+ measurable while building a 5760×3728 image per frame.
201
+
202
+ OCR also drops lines that are debris rather than text — `»`, `.lll (100 4`,
203
+ a misread status-bar clock. On a real capture that removed 41 of 210 report
204
+ lines and no real text. A line that looks like a failure is never dropped.
205
+
206
+ ## Known limitation
207
+
208
+ Perceptual hashing compares visual *structure*. It sees navigation, dialogs and
209
+ error banners, but it cannot detect a small **text-only** change on an otherwise
210
+ identical layout — measured on 1080×1920 those differences fall inside the noise
211
+ floor at every hash size, so no `--phash-distance` value separates them.
212
+
213
+ OCR does not rescue this: it runs on frames that survive deduplication, so a
214
+ screen already collapsed is never read. Reading every candidate instead measured
215
+ 48–59 s for a 3 min video, over the entire time budget. If your bug *is* a small
216
+ text change on an identical screen, record cropped or at lower resolution so the
217
+ text occupies more of the frame.
218
+
219
+ ## Development
220
+
221
+ ```bash
222
+ uv sync --extra ocr
223
+ uv run pytest -q
224
+ ```
225
+
226
+ 183 tests. Device recording is mocked on purpose — a test suite should not record
227
+ anyone's phone. Whisper is opt-in via `BUG2CONTEXT_TEST_WHISPER=1` so the suite
228
+ stays offline.
229
+
230
+ ## License
231
+
232
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,200 @@
1
+ # PLAN — bug2context
2
+
3
+ > Herramienta local-first que transforma grabaciones de video de bugs (especialmente de apps móviles) en contexto estructurado y cronológico listo para consumir por motores de IA, entregada como CLI + servidor MCP.
4
+
5
+ ---
6
+
7
+ ## 1. Visión y problema
8
+
9
+ **Problema:** al reportar o depurar bugs, las capturas de pantalla son ineficientes — los errores suceden muy rápido, no reflejan cronología, y el contexto (logs, narración, causa→efecto) se pierde. Los LLMs (Claude incluido) no aceptan video directamente.
10
+
11
+ **Solución:** un pipeline que destila un video en un "paquete de contexto": frames clave con timestamps + texto extraído por OCR + logs del dispositivo sincronizados + transcripción de narración, ensamblados en un reporte markdown cronológico que un agente de IA puede leer directamente.
12
+
13
+ **Diferenciadores vs. mercado existente (Bugsee, Shake, Instabug):**
14
+ - Sin SDK: funciona con cualquier video, incluso uno recibido por WhatsApp.
15
+ - Local-first: sin cloud, sin suscripción, privacidad total.
16
+ - MCP-first: Claude Code lo consume como una tool nativa.
17
+ - Open source (licencia MIT o Apache 2.0).
18
+
19
+ ## 2. Principios de diseño
20
+
21
+ 1. **Local-first:** todo el procesamiento corre en la máquina del usuario. Ningún dato sale sin acción explícita.
22
+ 2. **MCP como interfaz principal:** el CLI existe, pero el caso de uso estrella es `Claude Code → tool MCP → contexto`.
23
+ 3. **Degradación elegante:** cada etapa del pipeline es opcional. Sin audio → sin transcripción. Sin logcat → solo video. Sin OCR disponible → solo frames.
24
+ 4. **Salida en texto plano primero:** el reporte es markdown legible por humanos y por IA; los frames son adjuntos referenciados.
25
+ 5. **Costo cero por defecto:** ninguna etapa del núcleo requiere API keys. El análisis con vision-LLM es una capa opcional posterior.
26
+
27
+ ## 3. Arquitectura del pipeline
28
+
29
+ ```
30
+ video.mp4 ──► [1] Extracción de frames (ffmpeg, detección de escena + intervalo fijo)
31
+ ──► [2] Deduplicación (hash perceptual)
32
+ ──► [3] OCR por frame (macOS Vision / tesseract fallback)
33
+ audio ──► [4] Transcripción (faster-whisper, opcional)
34
+ logcat.txt ──► [5] Parser de logs con timestamps (opcional)
35
+ ──► [6] Ensamblador: fusiona las 3 líneas de tiempo (frames, narración, logs)
36
+ ──► [7] Salida: bundle/
37
+ ├── report.md (cronología unificada)
38
+ ├── frames/ (PNGs clave, numerados con timestamp)
39
+ └── meta.json (datos estructurados para consumo programático)
40
+ ```
41
+
42
+ ### Detalles por etapa
43
+
44
+ **[1] Extracción de frames**
45
+ - `ffmpeg -i video.mp4 -vf "select='gt(scene,0.08)',showinfo" -vsync vfr` para cambios de escena.
46
+ - Parsear stderr de `showinfo` para obtener `pts_time` (timestamp exacto) de cada frame.
47
+ - Muestreo de respaldo: 1 frame cada N segundos (configurable, default 3 s) para transiciones lentas.
48
+ - Umbral de escena configurable (`--scene-threshold`).
49
+
50
+ **[2] Deduplicación**
51
+ - `imagehash.phash` con distancia Hamming configurable (default ≤ 6 = duplicado).
52
+ - Meta: de un video de 2 min quedar con 8–25 frames.
53
+ - Límite duro configurable (`--max-frames`, default 20) priorizando frames con mayor cambio de escena.
54
+
55
+ **[3] OCR**
56
+ - macOS: framework Vision vía `pyobjc` (calidad superior para texto de pantalla, gratis, offline).
57
+ - Fallback multiplataforma: `pytesseract`.
58
+ - Guardar texto por frame en `meta.json`; incluir en el reporte solo líneas relevantes (heurística: contiene "error", "exception", "fatal", "warning", stack traces, o difiere del frame anterior).
59
+
60
+ **[4] Transcripción (opcional)**
61
+ - `faster-whisper` modelo `small` por defecto (balance velocidad/calidad), con timestamps por segmento.
62
+ - Detectar si el video tiene pista de audio con contenido antes de transcribir (skip si es silencio).
63
+
64
+ **[5] Parser de logs (opcional)**
65
+ - Entrada: archivo logcat (`adb logcat -v time`) o log genérico con timestamps.
66
+ - Normalizar timestamps al reloj del video (offset configurable o auto-alineado por hora de inicio de grabación).
67
+ - Filtro por nivel (default: W/E/F) y por tag/paquete de la app.
68
+
69
+ **[6] Ensamblador**
70
+ - Fusiona los tres streams en una sola cronología ordenada por timestamp.
71
+ - Formato de entrada del reporte:
72
+ ```
73
+ [00:06] 🖼 frame_003.png — cambio de escena
74
+ OCR: "TypeError: cannot read property 'rate'"
75
+ [00:06] 📋 LOG E/RideService: NullPointerException at RideRequest.kt:142
76
+ [00:08] 🎙 "ahí está, explota al aplicar el descuento"
77
+ ```
78
+
79
+ ## 4. Stack técnico
80
+
81
+ | Componente | Elección | Notas |
82
+ |---|---|---|
83
+ | Lenguaje | Python 3.12+ | ecosistema ffmpeg/whisper/MCP maduro |
84
+ | CLI | `typer` | subcomandos, help autogenerado |
85
+ | Video | `ffmpeg` (subprocess) | dependencia de sistema, verificar en arranque |
86
+ | Dedup | `Pillow` + `imagehash` | |
87
+ | OCR | `pyobjc` (Vision) / `pytesseract` | detección de plataforma en runtime |
88
+ | Audio | `faster-whisper` | extra opcional: `pip install bug2context[audio]` |
89
+ | MCP | SDK oficial de Python MCP (`fastmcp`) | servidor stdio |
90
+ | Empaquetado | `uv` + `pyproject.toml` | instalable vía `uvx bug2context` |
91
+ | Tests | `pytest` | fixtures con videos cortos sintéticos generados con ffmpeg |
92
+
93
+ ## 5. Estructura del repo
94
+
95
+ ```
96
+ bug2context/
97
+ ├── pyproject.toml
98
+ ├── README.md
99
+ ├── LICENSE
100
+ ├── src/bug2context/
101
+ │ ├── __init__.py
102
+ │ ├── cli.py # typer app
103
+ │ ├── pipeline/
104
+ │ │ ├── frames.py # etapas 1-2
105
+ │ │ ├── ocr.py # etapa 3
106
+ │ │ ├── audio.py # etapa 4
107
+ │ │ ├── logs.py # etapa 5
108
+ │ │ └── assemble.py # etapas 6-7
109
+ │ ├── mcp_server.py # servidor MCP
110
+ │ └── config.py # dataclass de opciones + defaults
111
+ └── tests/
112
+ ```
113
+
114
+ ## 6. Fases (metodología GSD)
115
+
116
+ ### Fase 1 — Núcleo de video (MVP)
117
+ **Objetivo:** `bug2context process video.mp4` produce `bundle/` con `report.md` + frames.
118
+ - [ ] 1.1 Scaffold del repo (`pyproject.toml`, typer, estructura src/)
119
+ - [ ] 1.2 `frames.py`: extracción por escena + intervalo, parseo de timestamps de showinfo
120
+ - [ ] 1.3 Deduplicación por phash con límite de frames
121
+ - [ ] 1.4 `assemble.py`: report.md cronológico solo con frames + meta.json
122
+ - [ ] 1.5 Tests con video sintético (ffmpeg genera pantallas de color con texto que cambian)
123
+
124
+ **Criterio de aceptación:** un screen recording real de 1–3 min produce ≤ 20 frames relevantes con timestamps correctos, en < 30 s de procesamiento.
125
+
126
+ ### Fase 2 — OCR
127
+ - [ ] 2.1 Backend Vision (macOS) vía pyobjc
128
+ - [ ] 2.2 Backend tesseract como fallback
129
+ - [ ] 2.3 Heurística de relevancia (errores, diffs entre frames)
130
+ - [ ] 2.4 Integración al reporte
131
+
132
+ **Criterio de aceptación:** un video con un stack trace visible en pantalla produce ese texto legible en report.md.
133
+
134
+ ### Fase 3 — Servidor MCP
135
+ - [ ] 3.1 `mcp_server.py` con fastmcp, transporte stdio
136
+ - [ ] 3.2 Tool `analyze_bug_video(path, options)` → devuelve report.md + rutas de frames
137
+ - [ ] 3.3 Tool `get_frame(bundle, index)` → devuelve imagen para que el agente la vea
138
+ - [ ] 3.4 Documentar instalación en Claude Code (`claude mcp add`)
139
+
140
+ **Criterio de aceptación:** desde Claude Code, pedir "analiza este video del bug" ejecuta la tool y el agente razona sobre la cronología sin pasos manuales.
141
+
142
+ ### Fase 4 — Audio (narración)
143
+ - [ ] 4.1 Extracción de pista de audio + detección de silencio
144
+ - [ ] 4.2 faster-whisper con timestamps por segmento
145
+ - [ ] 4.3 Fusión en la cronología
146
+
147
+ ### Fase 5 — Orquestador móvil (Android primero)
148
+ - [ ] 5.1 `bug2context record --android`: lanza `adb shell screenrecord` + `adb logcat -v time` en paralelo
149
+ - [ ] 5.2 Alineación de relojes video↔logcat
150
+ - [ ] 5.3 Parser de logcat con filtros por nivel y paquete
151
+ - [ ] 5.4 Fusión de logs en la cronología
152
+ - [ ] 5.5 (Posterior) iOS: simctl + log stream
153
+
154
+ **Criterio de aceptación:** reproducir un crash en un dispositivo Android conectado produce un reporte donde el stack trace del logcat aparece en el instante del frame del crash.
155
+
156
+ ### Fase 6 — Publicación
157
+ - [ ] 6.1 README con demo GIF, ejemplos de uso con Claude Code
158
+ - [ ] 6.2 Publicar en PyPI + anuncio (X, Reddit r/ClaudeAI, HN)
159
+ - [ ] 6.3 Recoger feedback y definir capa de monetización (cloud, GitHub Issues, equipo)
160
+
161
+ ## 7. Diseño del servidor MCP (contrato)
162
+
163
+ ```python
164
+ @mcp.tool()
165
+ def analyze_bug_video(
166
+ video_path: str,
167
+ logcat_path: str | None = None,
168
+ transcribe_audio: bool = False,
169
+ max_frames: int = 20,
170
+ ) -> str:
171
+ """Procesa un video de un bug y devuelve un reporte cronológico
172
+ en markdown con referencias a frames clave."""
173
+
174
+ @mcp.tool()
175
+ def get_frame(bundle_path: str, frame_index: int) -> Image:
176
+ """Devuelve un frame específico del bundle para inspección visual."""
177
+
178
+ @mcp.tool()
179
+ def list_bundles(workdir: str = "~/.bug2context") -> list[dict]:
180
+ """Lista los bundles procesados con fecha y video de origen."""
181
+ ```
182
+
183
+ ## 8. Riesgos y mitigaciones
184
+
185
+ | Riesgo | Mitigación |
186
+ |---|---|
187
+ | OCR pobre en videos comprimidos (WhatsApp recomprime mucho) | upscaling 2x con ffmpeg antes de OCR; documentar "pide el video como archivo, no como video de WhatsApp" |
188
+ | Detección de escena falla en apps con animaciones constantes | combinar escena + intervalo fijo; umbral configurable |
189
+ | Ventana competitiva (otros ya construyen en el nicho) | priorizar Fases 1–3 para tener el MVP con MCP publicado rápido; el orquestador adb (Fase 5) es el foso |
190
+ | Desalineación de relojes video↔logcat | registrar timestamp de inicio de screenrecord; offset manual como escape |
191
+ | pyobjc/Vision frágil entre versiones de macOS | tesseract siempre disponible como fallback |
192
+
193
+ ## 9. Instrucciones de trabajo para Claude Code
194
+
195
+ - Ejecutar comando por comando, confirmando entre pasos.
196
+ - Trabajar en un worktree de git dedicado.
197
+ - Cada fase = una rama; merge al completar criterio de aceptación.
198
+ - Escribir tests antes de cerrar cada tarea numerada.
199
+ - No agregar dependencias fuera de las listadas sin consultar.
200
+ - Máquina de desarrollo: macOS (Homebrew disponible para ffmpeg/tesseract).