|
| 1 | +# flutter-read-logs |
| 2 | + |
| 3 | +LeanCode Flutter plugin that lets Claude read the running app's `flutter run` output as |
| 4 | +on-demand context. Instead of pasting terminal logs, you run the app, then ask: |
| 5 | + |
| 6 | +``` |
| 7 | +/read-logs why is the login screen stuck |
| 8 | +``` |
| 9 | + |
| 10 | +Claude reads the most recent run's logs and works your task against them — it's a context |
| 11 | +loader, not an auto-analyzer. |
| 12 | + |
| 13 | +## Included assets |
| 14 | + |
| 15 | +- `skills/read-logs/SKILL.md` — the `/read-logs` workhorse: resolves the per-project log, |
| 16 | + auto-detects format, reads task-led, flags stale runs, and guides first-run setup. |
| 17 | +- `skills/flutter-read-logs-usage/SKILL.md` — explains the plugin and routes to setup. |
| 18 | + |
| 19 | +## How it works |
| 20 | + |
| 21 | +Each project logs to its own file, `/tmp/flutter-<repo>.log`, where `<repo>` is derived |
| 22 | +from the shared `.git` (`git rev-parse --git-common-dir`) — so the main checkout and every |
| 23 | +git worktree resolve to the **same** file. The file is overwritten on every launch (always |
| 24 | +the latest run) and lives in `/tmp` — outside the repo, nothing to gitignore, cleared on |
| 25 | +reboot. `/read-logs` derives the path at runtime and auto-detects the format. |
| 26 | + |
| 27 | +The only per-developer step is making your editor write that file — **do it once.** If you |
| 28 | +run `/read-logs` before setting it up, the skill walks you through it (and offers to apply |
| 29 | +the change). It only edits **local** config; it never commits anything. |
| 30 | + |
| 31 | +## ⚠️ Security / data handling — read this first |
| 32 | + |
| 33 | +**Reading a run sends its contents to the model. That *is* the leak — it's how the tool |
| 34 | +works, and it can't be prevented.** Run logs routinely contain auth/refresh/push tokens, |
| 35 | +emails, account details, and other customer data. The file stays local in `/tmp`, but the |
| 36 | +moment `/read-logs` reads it, that slice goes to the model. |
| 37 | + |
| 38 | +Treat this as a **conscious decision**: |
| 39 | + |
| 40 | +- **Enabling capture** (the one-time setup below) is your opt-in — the skill confirms it |
| 41 | + with you before wiring anything up. |
| 42 | +- **Each `/read-logs`** announces the file it's about to read before reading it. |
| 43 | +- **Prefer test/staging data** when you'll `/read-logs`; avoid production or real-customer |
| 44 | + runs unless you've accepted the exposure. |
| 45 | + |
| 46 | +If you need to read production-shaped logs, the durable mitigation is a **redaction pass** |
| 47 | +(mask tokens/emails/keys before the model sees them) — not yet built; tracked as a possible |
| 48 | +fast-follow. Raise it if your team wants it before adopting this widely. |
| 49 | + |
| 50 | +In the snippets below, replace `myapp` with your repo's folder name (it must match the path |
| 51 | +`/read-logs` derives — i.e. `/tmp/flutter-<repo>.log`). |
| 52 | + |
| 53 | +## VS Code / Cursor (one-time) — keeps F5 |
| 54 | + |
| 55 | +Add to your `.vscode/settings.json` (gitignored in most projects, so it stays local — |
| 56 | +correct, since the path is machine-specific): |
| 57 | + |
| 58 | +```json |
| 59 | +{ |
| 60 | + "dart.dapLogFile": "/tmp/flutter-myapp.log", |
| 61 | + "dart.maxLogLineLength": 1000000 |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +- Keeps your normal debug workflow — **F5** and **Ctrl+F5** ("Run Without Debugging") both |
| 66 | + capture, because both run through the debug adapter. Breakpoints intact, no terminal hacks. |
| 67 | +- `maxLogLineLength` defaults to 2000 and truncates long lines; bump it so output and stack |
| 68 | + traces aren't cut. |
| 69 | +- Reload the window after adding (`Developer: Reload Window`) so Dart-Code picks it up. |
| 70 | +- **Cursor** is a VS Code fork using the same Dart-Code extension and `.vscode/settings.json` |
| 71 | + — identical setup. |
| 72 | +- Do **not** use `dapLogFile`'s `${workspaceName}` variable — it expands to the workspace |
| 73 | + folder (differs per worktree) and won't match how `/read-logs` derives the path. |
| 74 | + |
| 75 | +**Caveats:** |
| 76 | + |
| 77 | +- `dapLogFile` captures **debug-adapter sessions** (F5 / Ctrl+F5). If you launch via a plain |
| 78 | + terminal Run Task instead, use the Zed/`script` approach below. |
| 79 | +- App logs only reach the debug console (and the file) on devices that forward them: **iOS |
| 80 | + simulator / Android / macOS / the `chrome` device**. A **`-d web-server`** run sends app |
| 81 | + logs to the *browser's* DevTools console, not the editor — so the file will have |
| 82 | + build/launch output but no app logs. |
| 83 | +- Historical note: the old `dart.flutterRunLogFile` was **removed** from Dart-Code with the |
| 84 | + legacy debug adapters; `dapLogFile` is its replacement. |
| 85 | + |
| 86 | +## Zed / terminal run task (one-time) — clean transcript |
| 87 | + |
| 88 | +Wrap your run task's command in `script` so output is teed to the file while keeping the |
| 89 | +interactive console (hot reload). **`script`'s syntax differs by OS.** |
| 90 | + |
| 91 | +**macOS (BSD `script`)** — logfile, then the command as trailing args, in `.zed/tasks.json`: |
| 92 | + |
| 93 | +```json |
| 94 | +{ |
| 95 | + "label": "Run app", |
| 96 | + "command": "script", |
| 97 | + "args": ["-q", "/tmp/flutter-myapp.log", "flutter", "run", "-t", "lib/main.dart"], |
| 98 | + "use_new_terminal": true |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +**Linux (GNU `script`)** — the command goes through `-c` as a single string, logfile **last**: |
| 103 | + |
| 104 | +```json |
| 105 | +{ |
| 106 | + "label": "Run app", |
| 107 | + "command": "script", |
| 108 | + "args": ["-q", "-c", "flutter run -t lib/main.dart", "/tmp/flutter-myapp.log"], |
| 109 | + "use_new_terminal": true |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +(Wrap whatever your existing run command is — including `fvm flutter …`, flavors, and |
| 114 | +`--dart-define`s — don't replace it.) **Windows** has no `script`; use the VS Code/Cursor |
| 115 | +`dapLogFile` path above, which is cross-platform. |
| 116 | + |
| 117 | +`script` runs Flutter under a pseudo-TTY, so hot reload (`r`) / hot restart (`R`) keep |
| 118 | +working — unlike a plain `… | tee` pipe, which makes Flutter drop to non-interactive mode. |
| 119 | +This produces a clean text transcript (no JSON parsing needed). Web logs are captured here |
| 120 | +too, as long as the task uses `-d chrome` (not `-d web-server`). |
| 121 | + |
| 122 | +## Note on duplicated setup |
| 123 | + |
| 124 | +These setup steps also appear **inline** in `skills/read-logs/SKILL.md`, on purpose: that |
| 125 | +skill applies them at runtime, when the developer is in their editor and not reading this |
| 126 | +README. This `README.md` is the canonical copy — keep the two in sync when changing setup. |
| 127 | + |
| 128 | +## Example usage |
| 129 | + |
| 130 | +- `/read-logs <task>` — read the latest run's logs as context for `<task>`. |
| 131 | +- `/flutter-read-logs-usage` — short explanation of what this plugin does and how to set it up. |
| 132 | + |
| 133 | +## Related plugins |
| 134 | + |
| 135 | +- [`flutter-marionette`](../flutter-marionette/) — the **proactive** counterpart: it drives |
| 136 | + a *live* app (taps, navigation, hot reload, query state) via MCP, needing instrumentation |
| 137 | + and a running connection. `flutter-read-logs` is **reactive** — it reads what a run |
| 138 | + *already produced*, including build failures and crashed/exited runs, with no |
| 139 | + instrumentation or live app. Rule of thumb: **marionette to *make* things happen, |
| 140 | + read-logs to *see what happened*.** They're complementary — each catches what the other |
| 141 | + can't (marionette: live state; read-logs: build/crash output before any connection). |
0 commit comments