fow-cli 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 (46) hide show
  1. fow_cli-0.1.0/.gitignore +19 -0
  2. fow_cli-0.1.0/LICENSE +21 -0
  3. fow_cli-0.1.0/PKG-INFO +447 -0
  4. fow_cli-0.1.0/README.md +417 -0
  5. fow_cli-0.1.0/pyproject.toml +76 -0
  6. fow_cli-0.1.0/src/fly_on_the_wall/__init__.py +3 -0
  7. fow_cli-0.1.0/src/fly_on_the_wall/audio.py +164 -0
  8. fow_cli-0.1.0/src/fly_on_the_wall/audio_metadata.py +241 -0
  9. fow_cli-0.1.0/src/fly_on_the_wall/cache.py +26 -0
  10. fow_cli-0.1.0/src/fly_on_the_wall/cleanup.py +29 -0
  11. fow_cli-0.1.0/src/fly_on_the_wall/cli.py +641 -0
  12. fow_cli-0.1.0/src/fly_on_the_wall/cli_costs.py +81 -0
  13. fow_cli-0.1.0/src/fly_on_the_wall/cli_menu.py +163 -0
  14. fow_cli-0.1.0/src/fly_on_the_wall/cli_publish.py +141 -0
  15. fow_cli-0.1.0/src/fly_on_the_wall/cli_speaker_review.py +315 -0
  16. fow_cli-0.1.0/src/fly_on_the_wall/cli_watch.py +209 -0
  17. fow_cli-0.1.0/src/fly_on_the_wall/config.py +92 -0
  18. fow_cli-0.1.0/src/fly_on_the_wall/costs.py +169 -0
  19. fow_cli-0.1.0/src/fly_on_the_wall/db.py +508 -0
  20. fow_cli-0.1.0/src/fly_on_the_wall/doctor.py +142 -0
  21. fow_cli-0.1.0/src/fly_on_the_wall/embeddings.py +142 -0
  22. fow_cli-0.1.0/src/fly_on_the_wall/exporting.py +155 -0
  23. fow_cli-0.1.0/src/fly_on_the_wall/glossary.py +31 -0
  24. fow_cli-0.1.0/src/fly_on_the_wall/meetings.py +382 -0
  25. fow_cli-0.1.0/src/fly_on_the_wall/normalization.py +166 -0
  26. fow_cli-0.1.0/src/fly_on_the_wall/people.py +82 -0
  27. fow_cli-0.1.0/src/fly_on_the_wall/people_embeddings.py +68 -0
  28. fow_cli-0.1.0/src/fly_on_the_wall/pipeline.py +120 -0
  29. fow_cli-0.1.0/src/fly_on_the_wall/processing.py +427 -0
  30. fow_cli-0.1.0/src/fly_on_the_wall/providers/__init__.py +1 -0
  31. fow_cli-0.1.0/src/fly_on_the_wall/providers/elevenlabs.py +145 -0
  32. fow_cli-0.1.0/src/fly_on_the_wall/providers/openai_analysis.py +195 -0
  33. fow_cli-0.1.0/src/fly_on_the_wall/providers/openai_cleanup.py +91 -0
  34. fow_cli-0.1.0/src/fly_on_the_wall/publishing.py +410 -0
  35. fow_cli-0.1.0/src/fly_on_the_wall/reanalysis.py +172 -0
  36. fow_cli-0.1.0/src/fly_on_the_wall/recording_quality.py +141 -0
  37. fow_cli-0.1.0/src/fly_on_the_wall/rendering.py +115 -0
  38. fow_cli-0.1.0/src/fly_on_the_wall/secrets.py +93 -0
  39. fow_cli-0.1.0/src/fly_on_the_wall/service_pricing.py +75 -0
  40. fow_cli-0.1.0/src/fly_on_the_wall/setup.py +221 -0
  41. fow_cli-0.1.0/src/fly_on_the_wall/speaker_identity.py +173 -0
  42. fow_cli-0.1.0/src/fly_on_the_wall/speaker_matching.py +134 -0
  43. fow_cli-0.1.0/src/fly_on_the_wall/speakers.py +221 -0
  44. fow_cli-0.1.0/src/fly_on_the_wall/storage.py +53 -0
  45. fow_cli-0.1.0/src/fly_on_the_wall/voice_samples.py +125 -0
  46. fow_cli-0.1.0/src/fly_on_the_wall/watch.py +347 -0
@@ -0,0 +1,19 @@
1
+ .env
2
+ .envrc
3
+ .venv-pyannote/
4
+ .venv/
5
+ __pycache__/
6
+ *.py[cod]
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ dist/
10
+
11
+ # Local/generated transcription outputs and private meeting data.
12
+ audio-samples/
13
+ speaker-profiles/
14
+ transcript.txt
15
+ transcript.raw.json
16
+
17
+ # Local caches and editor/OS noise.
18
+ .DS_Store
19
+ .cache/
fow_cli-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Person A Svensson
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.
fow_cli-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,447 @@
1
+ Metadata-Version: 2.4
2
+ Name: fow-cli
3
+ Version: 0.1.0
4
+ Summary: Personal CLI note-taker for turning meeting audio into cleaned meeting manuscripts.
5
+ Project-URL: Repository, https://github.com/henriksvensson/fly-on-the-wall
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
15
+ Classifier: Topic :: Office/Business
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: httpx>=0.28.0
18
+ Requires-Dist: keyring>=25.5.0
19
+ Requires-Dist: prompt-toolkit>=3.0.52
20
+ Requires-Dist: pydantic>=2.10.0
21
+ Requires-Dist: pyyaml>=6.0.0
22
+ Requires-Dist: rich>=13.7.0
23
+ Requires-Dist: typer>=0.12.0
24
+ Requires-Dist: watchfiles>=1.0.0
25
+ Provides-Extra: identity
26
+ Requires-Dist: pyannote-audio>=3.3.0; extra == 'identity'
27
+ Requires-Dist: torch>=2.3.0; extra == 'identity'
28
+ Requires-Dist: torchaudio>=2.3.0; extra == 'identity'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Fly on the Wall
32
+
33
+ Fly on the Wall is a personal CLI note-taker for meeting audio.
34
+
35
+ It takes local audio recordings, transcribes them, identifies recurring speakers where possible, cleans the transcript, analyzes the meeting, exports durable Markdown artifacts, and can publish readable notes into an Obsidian vault.
36
+
37
+ The tool is designed for one person running it locally. There is no hosted service, login system, team workspace, or multi-tenant data model.
38
+
39
+ ## Project Status
40
+
41
+ This is early alpha software. It is usable as a local personal CLI, but command behavior, storage schema, and output formats may still change between releases.
42
+
43
+ Until `1.0`, minor releases may include breaking changes. Back up `~/.local/share/fly-on-the-wall/` before upgrading if you depend on stored meeting data.
44
+
45
+ Issues and suggestions are welcome via GitHub Issues, but the project is provided as-is with no support guarantee.
46
+
47
+ Audio is sent to configured transcription/AI providers during processing. Optional speaker identity embeddings run locally when installed with the `identity` extra. External providers may charge usage-based fees depending on your provider account, pricing plan, and processing volume.
48
+
49
+ ## Development Transparency
50
+
51
+ This project was developed as an agentic coding project using [OpenCode](https://opencode.ai/) with [OpenAI](https://openai.com/) GPT-5.5. Code quality checks were supported by CodeScene's [CodeHealth](https://codescene.com/product/code-health) analysis.
52
+
53
+ ## What It Does
54
+
55
+ `fow process <audio>` runs the main pipeline:
56
+
57
+ 1. Imports the audio into local app storage.
58
+ 2. Extracts audio metadata and recording timestamps where possible.
59
+ 3. Sends the raw audio to ElevenLabs for transcription and diarization.
60
+ 4. Stores the raw provider response for auditability.
61
+ 5. Normalizes provider output into internal segments and meeting-local speakers.
62
+ 6. Matches meeting-local speakers to known people using local voice embeddings.
63
+ 7. Renders a named transcript.
64
+ 8. Runs deterministic cleanup.
65
+ 9. Optionally runs OpenAI light cleanup, meeting analysis, and title generation.
66
+ 10. Exports immutable Markdown artifacts.
67
+ 11. Publishes to configured Obsidian targets if auto-publish is enabled.
68
+
69
+ Final user-facing exports include:
70
+
71
+ - `transcript.md`: cleaned readable manuscript.
72
+ - `analysis.md`: summary, decisions, action items, open questions, and important details.
73
+ - `manifest.json`: internal metadata about the export.
74
+
75
+ ## Current Provider Setup
76
+
77
+ The current transcription provider is ElevenLabs Scribe v2.
78
+
79
+ OpenAI is used for optional transcript cleanup, meeting analysis, and generated meeting titles when an OpenAI API key is available.
80
+
81
+ Speaker identity matching uses local embeddings via `pyannote.audio` / `pyannote/wespeaker-voxceleb-resnet34-LM`. Audio used for identity matching is processed locally. The first model load may contact Hugging Face to download model weights unless they are already cached locally.
82
+
83
+ ## Installation
84
+
85
+ Install the CLI with `uv tool`:
86
+
87
+ ```bash
88
+ uv tool install fow-cli
89
+ fow setup
90
+ ```
91
+
92
+ Speaker identity matching is optional and adds heavier local ML dependencies:
93
+
94
+ ```bash
95
+ uv tool install "fow-cli[identity]"
96
+ ```
97
+
98
+ If you already installed the base CLI with `uv tool`, upgrade it with the optional extra:
99
+
100
+ ```bash
101
+ uv tool upgrade --reinstall "fow-cli[identity]"
102
+ ```
103
+
104
+ Development from a source checkout also uses `uv`:
105
+
106
+ ```bash
107
+ uv sync
108
+ uv run fow
109
+ ```
110
+
111
+ Include speaker identity dependencies during local development with:
112
+
113
+ ```bash
114
+ uv sync --extra identity
115
+ ```
116
+
117
+ You can point `fow` at `uv run fow` with a shell alias:
118
+
119
+ ```bash
120
+ alias fow="uv run fow"
121
+ ```
122
+
123
+ ## Configuration And Secrets
124
+
125
+ Configuration lives under:
126
+
127
+ ```text
128
+ ~/.config/fly-on-the-wall/
129
+ ```
130
+
131
+ Application data lives under:
132
+
133
+ ```text
134
+ ~/.local/share/fly-on-the-wall/
135
+ ```
136
+
137
+ API keys are read from environment variables first, then from the OS keyring.
138
+
139
+ Useful secret commands:
140
+
141
+ ```bash
142
+ fow secrets status
143
+ fow secrets set elevenlabs
144
+ fow secrets set openai
145
+ fow secrets remove openai
146
+ ```
147
+
148
+ Expected environment variables:
149
+
150
+ ```text
151
+ ELEVENLABS_API_KEY
152
+ OPENAI_API_KEY
153
+ ```
154
+
155
+ ## Basic Usage
156
+
157
+ Run the interactive setup wizard:
158
+
159
+ ```bash
160
+ fow setup
161
+ ```
162
+
163
+ It checks required dependencies, helps store API keys, sets your user identity, and can configure Obsidian publishing and watched folders.
164
+
165
+ Process one recording:
166
+
167
+ ```bash
168
+ fow process path/to/meeting.m4a
169
+ ```
170
+
171
+ Optionally provide a manual title and context:
172
+
173
+ ```bash
174
+ fow process path/to/meeting.m4a --title "Board prep" --description "Monthly board preparation call"
175
+ ```
176
+
177
+ List meetings:
178
+
179
+ ```bash
180
+ fow meetings list
181
+ ```
182
+
183
+ Show one meeting:
184
+
185
+ ```bash
186
+ fow meetings show <meeting>
187
+ ```
188
+
189
+ Show pipeline status:
190
+
191
+ ```bash
192
+ fow meetings status <meeting>
193
+ ```
194
+
195
+ Refresh derived outputs for one meeting without retranscribing:
196
+
197
+ ```bash
198
+ fow refresh meeting <meeting>
199
+ ```
200
+
201
+ Refresh every meeting with stale derived outputs:
202
+
203
+ ```bash
204
+ fow refresh stale-meetings
205
+ ```
206
+
207
+ ## People And Speakers
208
+
209
+ The CLI uses two related concepts:
210
+
211
+ - A **person** is a stable real-world identity, such as `Person A` or `Person B`.
212
+ - A **meeting speaker** is a local diarization label inside one provider run, such as `speaker_0`.
213
+
214
+ Manage known people:
215
+
216
+ ```bash
217
+ fow people list
218
+ fow people create "Person A"
219
+ fow people show "Person A"
220
+ ```
221
+
222
+ Review unknown meeting speakers interactively:
223
+
224
+ ```bash
225
+ fow meetings speakers review
226
+ fow meetings speakers review --include-uncertain
227
+ fow meetings speakers review --only-uncertain
228
+ ```
229
+
230
+ Review speakers for one meeting:
231
+
232
+ ```bash
233
+ fow meetings speakers review --meeting <meeting>
234
+ ```
235
+
236
+ List meeting speakers that are not assigned to known people:
237
+
238
+ ```bash
239
+ fow meetings speakers unknown
240
+ fow meetings speakers unknown --meeting <meeting>
241
+ ```
242
+
243
+ Assign a meeting speaker to a known person, creating the person if needed:
244
+
245
+ ```bash
246
+ fow meetings speakers assign <local-speaker-id> "Person A"
247
+ ```
248
+
249
+ Ignore a meeting speaker so it does not appear in future reviews:
250
+
251
+ ```bash
252
+ fow meetings speakers ignore <local-speaker-id>
253
+ ```
254
+
255
+ Refresh speaker matching after adding voice samples or changing identities:
256
+
257
+ ```bash
258
+ fow refresh speakers
259
+ fow refresh speakers <meeting>
260
+ fow refresh speakers --include-known-speakers
261
+ ```
262
+
263
+ Backfill missing known-person voice embeddings:
264
+
265
+ ```bash
266
+ fow people embeddings status
267
+ fow people embeddings backfill
268
+ ```
269
+
270
+ ## Watched Folders
271
+
272
+ Fly on the Wall can watch local folders, mounted Dropbox/rclone folders, and removable recorder folders.
273
+
274
+ Add a folder:
275
+
276
+ ```bash
277
+ fow watch folders add /path/to/recordings --name recordings
278
+ ```
279
+
280
+ List watched folders:
281
+
282
+ ```bash
283
+ fow watch folders list
284
+ ```
285
+
286
+ Run one scan:
287
+
288
+ ```bash
289
+ fow watch scan
290
+ ```
291
+
292
+ Watch continuously:
293
+
294
+ ```bash
295
+ fow watch run
296
+ ```
297
+
298
+ The watcher tolerates missing/remounted folders and uses periodic scans because cloud/removable mounts may not emit reliable filesystem events.
299
+
300
+ ## Publishing To Obsidian
301
+
302
+ Publishing is separate from internal exports.
303
+
304
+ Internal exports are immutable. Obsidian notes are mutable and idempotent, so republishing updates the existing note rather than creating duplicate notes.
305
+
306
+ Add an Obsidian target:
307
+
308
+ ```bash
309
+ fow publish targets add obsidian "/path/to/Obsidian Vault/Fly on the Wall" --name obsidian --auto-publish
310
+ ```
311
+
312
+ Publish one meeting:
313
+
314
+ ```bash
315
+ fow publish meeting <meeting> --target obsidian
316
+ ```
317
+
318
+ Publish all exported meetings:
319
+
320
+ ```bash
321
+ fow publish all --target obsidian
322
+ ```
323
+
324
+ ## Example Personal Setup
325
+
326
+ One practical setup is to combine several recording sources with watched folders and Obsidian publishing:
327
+
328
+ - A Philips DVT 4110 voice recorder is automounted when connected, exposing recordings as a local folder.
329
+ - A dedicated Dropbox recording folder is synced locally with [rclone](https://rclone.org/dropbox/).
330
+ - On iPhone, [RecUp](https://apps.apple.com/us/app/recup-record-to-the-cloud/id416288287) can upload recordings directly to Dropbox. Assigning RecUp to the iPhone Action Button makes quick capture a one-button workflow.
331
+ - `fow watch run` watches both the recorder mount and the local Dropbox/rclone folder.
332
+ - Processed notes are published into an Obsidian vault. If the vault is already synced with [Remotely Save](https://github.com/remotely-save/remotely-save), notes can then appear on other devices through Obsidian sync tooling.
333
+
334
+ In that setup, recordings can enter from either the hardware recorder or phone uploads, `fow` processes them locally, and Obsidian becomes the final reading and review surface.
335
+
336
+ ## Cost Tracking
337
+
338
+ The app records estimated external service usage and costs for future live provider calls.
339
+
340
+ It tracks:
341
+
342
+ - ElevenLabs transcription usage via `audio_duration_secs`.
343
+ - OpenAI cleanup, analysis, and title-generation usage via provider token usage.
344
+ - Pricing snapshots used for each estimate.
345
+
346
+ Show total estimated costs:
347
+
348
+ ```bash
349
+ fow costs summary
350
+ ```
351
+
352
+ Show estimated costs for one meeting:
353
+
354
+ ```bash
355
+ fow costs meeting <meeting>
356
+ ```
357
+
358
+ Historical ElevenLabs usage can be backfilled accurately from stored raw responses. Historical OpenAI usage can only be approximated unless raw OpenAI response usage was stored.
359
+
360
+ ## Local Storage
361
+
362
+ The app stores operational state in SQLite and large artifacts on disk:
363
+
364
+ ```text
365
+ ~/.local/share/fly-on-the-wall/
366
+ fly.db
367
+ audio/
368
+ artifacts/
369
+ voice-samples/
370
+ exports/
371
+ ```
372
+
373
+ Raw provider responses are intentionally preserved. They are useful for debugging, normalization changes, speaker review, cost tracking, and future reprocessing.
374
+
375
+ ## Uninstalling
376
+
377
+ Remove the installed CLI:
378
+
379
+ ```bash
380
+ uv tool uninstall fow-cli
381
+ ```
382
+
383
+ Remove local configuration and app data if you no longer need stored meetings, exports, raw provider responses, voice samples, or settings:
384
+
385
+ ```bash
386
+ rm -rf ~/.config/fly-on-the-wall ~/.local/share/fly-on-the-wall
387
+ ```
388
+
389
+ This does not remove original recordings that were processed from outside the app storage directory.
390
+
391
+ ## Development
392
+
393
+ Install development dependencies:
394
+
395
+ ```bash
396
+ uv sync --dev
397
+ ```
398
+
399
+ Install pre-commit hooks:
400
+
401
+ ```bash
402
+ uv run pre-commit install
403
+ ```
404
+
405
+ Run all pre-commit hooks manually:
406
+
407
+ ```bash
408
+ uv run pre-commit run --all-files
409
+ ```
410
+
411
+ Run tests:
412
+
413
+ ```bash
414
+ uv run pytest
415
+ ```
416
+
417
+ Run lint and formatting checks:
418
+
419
+ ```bash
420
+ uv run ruff check .
421
+ uv run ruff format --check .
422
+ ```
423
+
424
+ Build distribution artifacts:
425
+
426
+ ```bash
427
+ uv build
428
+ ```
429
+
430
+ Test a built wheel locally:
431
+
432
+ ```bash
433
+ uv tool install dist/fly_on_the_wall-0.1.0-py3-none-any.whl
434
+ fow setup
435
+ ```
436
+
437
+ Publish to PyPI after verifying the build, package name, and license metadata:
438
+
439
+ ```bash
440
+ uv publish
441
+ ```
442
+
443
+ ## Support
444
+
445
+ If Fly on the Wall is useful to you, and you have the spare cash, buying me a coffee would be lovely. Absolutely no pressure.
446
+
447
+ [![Buy Me A Coffee](https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&emoji=&slug=henriksvensson&button_colour=FFDD00&font_colour=000000&font_family=Lato&outline_colour=000000&coffee_colour=ffffff)](https://buymeacoffee.com/henriksvensson)