Skip to main content

Personal CLI note-taker for turning meeting audio into cleaned meeting manuscripts.

Project description

Fly on the Wall

PyPI Python Versions License: MIT

Fly on the Wall is a personal CLI note-taker for meeting audio.

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.

The tool is designed for one person running it locally. There is no hosted service, login system, team workspace, or multi-tenant data model.

Project Status

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.

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.

Issues and suggestions are welcome via GitHub Issues, but the project is provided as-is with no support guarantee.

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.

Glossary/keyterm hints are sent to ElevenLabs when processing new recordings. ElevenLabs currently documents this as a billable add-on to speech-to-text usage.

Development Transparency

This project was developed as an agentic coding project using OpenCode with OpenAI GPT-5.5. Code quality checks were supported by CodeScene's CodeHealth analysis.

What It Does

fow process <audio> runs the main pipeline:

  1. Imports the audio into local app storage.
  2. Extracts audio metadata and recording timestamps where possible.
  3. Sends the raw audio to ElevenLabs for transcription and diarization.
  4. Stores the raw provider response for auditability.
  5. Normalizes provider output into internal segments and meeting-local speakers.
  6. Matches meeting-local speakers to known people using local voice embeddings.
  7. Renders a named transcript.
  8. Runs deterministic cleanup.
  9. Optionally runs OpenAI light cleanup, meeting analysis, and title generation.
  10. Exports immutable Markdown artifacts.
  11. Publishes to configured Obsidian targets if auto-publish is enabled.

Final user-facing exports include:

  • transcript.md: cleaned readable manuscript.
  • analysis.md: summary, decisions, action items, open questions, and important details.
  • manifest.json: internal metadata about the export.

Current Provider Setup

The current transcription provider is ElevenLabs Scribe v2.

OpenAI is used for optional transcript cleanup, meeting analysis, and generated meeting titles when an OpenAI API key is available.

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.

Installation

Install the CLI with uv tool:

uv tool install fow-cli
fow setup

Speaker identity matching is optional and adds heavier local ML dependencies:

uv tool install "fow-cli[identity]"

If you already installed the base CLI with uv tool, upgrade it with the optional extra:

uv tool upgrade --reinstall "fow-cli[identity]"

Development from a source checkout also uses uv:

uv sync
uv run fow

Include speaker identity dependencies during local development with:

uv sync --extra identity

You can point fow at uv run fow with a shell alias:

alias fow="uv run fow"

Configuration And Secrets

Configuration lives under:

~/.config/fly-on-the-wall/

Application data lives under:

~/.local/share/fly-on-the-wall/

API keys are read from environment variables first, then from the OS keyring.

Useful secret commands:

fow secrets status
fow secrets set elevenlabs
fow secrets set openai
fow secrets remove openai

Expected environment variables:

ELEVENLABS_API_KEY
OPENAI_API_KEY

Basic Usage

Run the interactive setup wizard:

fow setup

It checks required dependencies, helps store API keys, sets your user identity, and can configure Obsidian publishing and watched folders.

Process one recording:

fow process path/to/meeting.m4a

Optionally provide a manual title and context:

fow process path/to/meeting.m4a --title "Board prep" --description "Monthly board preparation call"

List meetings:

fow meetings list

Show one meeting:

fow meetings show <meeting>

Show pipeline status:

fow meetings status <meeting>

Refresh derived outputs for one meeting without retranscribing:

fow refresh meeting <meeting>

Refresh every meeting with stale derived outputs:

fow refresh stale-meetings

People And Speakers

The CLI uses two related concepts:

  • A person is a stable real-world identity, such as Person A or Person B.
  • A meeting speaker is a local diarization label inside one provider run, such as speaker_0.

Manage known people:

fow people list
fow people create "Person A"
fow people show "Person A"

Review unknown meeting speakers interactively:

fow meetings speakers review
fow meetings speakers review --include-uncertain
fow meetings speakers review --only-uncertain

Review speakers for one meeting:

fow meetings speakers review --meeting <meeting>

List meeting speakers that are not assigned to known people:

fow meetings speakers unknown
fow meetings speakers unknown --meeting <meeting>

Assign a meeting speaker to a known person, creating the person if needed:

fow meetings speakers assign <local-speaker-id> "Person A"

Ignore a meeting speaker so it does not appear in future reviews:

fow meetings speakers ignore <local-speaker-id>

Refresh speaker matching after adding voice samples or changing identities:

fow refresh speakers
fow refresh speakers <meeting>
fow refresh speakers --include-known-speakers

Backfill missing known-person voice embeddings:

fow people embeddings status
fow people embeddings backfill

Glossary

Use the glossary for names, company names, project names, product names, acronyms, and domain-specific phrases that transcription or cleanup models may spell incorrectly.

Add terms with optional context:

fow glossary add "Hejare" --description "Company name"
fow glossary add "Datadrivna" --description "The phrase data driven in Swedish"
fow glossary add "Ants" --description "Company name"
fow glossary add "TT" --description "Company name, short for Theodora Tech"

Manage terms:

fow glossary list
fow glossary show "Hejare"
fow glossary update "TT" --description "Company name, short for Theodora Tech"
fow glossary disable "Ants"
fow glossary enable "Ants"
fow glossary remove "Ants"

During processing, fow combines enabled glossary terms with known people names. The combined list is sent to ElevenLabs as transcription keyterms for new transcriptions, and to OpenAI cleanup, analysis, and title generation as spelling/context guidance. Corrections are model-mediated; fow does not do deterministic search-and-replace from the glossary.

Watched Folders

Fly on the Wall can watch local folders, mounted Dropbox/rclone folders, and removable recorder folders.

Add a folder:

fow watch folders add /path/to/recordings --name recordings

List watched folders:

fow watch folders list

Run one scan:

fow watch scan

Watch continuously:

fow watch run

The watcher tolerates missing/remounted folders and uses periodic scans because cloud/removable mounts may not emit reliable filesystem events.

Publishing To Obsidian

Publishing is separate from internal exports.

Internal exports are immutable. Obsidian notes are mutable and idempotent, so republishing updates the existing note rather than creating duplicate notes.

Add an Obsidian target:

fow publish targets add obsidian "/path/to/Obsidian Vault/Fly on the Wall" --name obsidian --auto-publish

Publish one meeting:

fow publish meeting <meeting> --target obsidian

Publish all exported meetings:

fow publish all --target obsidian

Example Personal Setup

One practical setup is to combine several recording sources with watched folders and Obsidian publishing:

  • A Philips DVT 4110 voice recorder is automounted when connected, exposing recordings as a local folder.
  • A dedicated Dropbox recording folder is synced locally with rclone.
  • On iPhone, RecUp can upload recordings directly to Dropbox. Assigning RecUp to the iPhone Action Button makes quick capture a one-button workflow.
  • fow watch run watches both the recorder mount and the local Dropbox/rclone folder.
  • Processed notes are published into an Obsidian vault. If the vault is already synced with Remotely Save, notes can then appear on other devices through Obsidian sync tooling.

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.

Cost Tracking

The app records estimated external service usage and costs for future live provider calls.

It tracks:

  • ElevenLabs transcription usage via audio_duration_secs.
  • OpenAI cleanup, analysis, and title-generation usage via provider token usage.
  • Pricing snapshots used for each estimate.

Show total estimated costs:

fow costs summary

Show estimated costs for one meeting:

fow costs meeting <meeting>

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.

Local Storage

The app stores operational state in SQLite and large artifacts on disk:

~/.local/share/fly-on-the-wall/
  fly.db
  audio/
  artifacts/
  voice-samples/
  exports/

Raw provider responses are intentionally preserved. They are useful for debugging, normalization changes, speaker review, cost tracking, and future reprocessing.

Uninstalling

Remove the installed CLI:

uv tool uninstall fow-cli

Remove local configuration and app data if you no longer need stored meetings, exports, raw provider responses, voice samples, or settings:

rm -rf ~/.config/fly-on-the-wall ~/.local/share/fly-on-the-wall

This does not remove original recordings that were processed from outside the app storage directory.

Development

Install development dependencies:

uv sync --dev

Install pre-commit hooks:

uv run pre-commit install

Run all pre-commit hooks manually:

uv run pre-commit run --all-files

Run tests:

uv run pytest

Run lint and formatting checks:

uv run ruff check .
uv run ruff format --check .
uv run basedpyright

basedpyright is configured as a pragmatic source-code guardrail. It checks explicit type claims in src/ without requiring every dynamic SQLite, JSON, or third-party boundary to be fully typed.

Build distribution artifacts:

uv build

Test a built wheel locally:

uv tool install dist/fow_cli-0.1.0-py3-none-any.whl
fow setup

Publish to PyPI after verifying the build, package name, and license metadata:

uv publish

Support

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.

Buy Me A Coffee

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fow_cli-0.3.1.tar.gz (59.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fow_cli-0.3.1-py3-none-any.whl (80.7 kB view details)

Uploaded Python 3

File details

Details for the file fow_cli-0.3.1.tar.gz.

File metadata

  • Download URL: fow_cli-0.3.1.tar.gz
  • Upload date:
  • Size: 59.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"KDE neon","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fow_cli-0.3.1.tar.gz
Algorithm Hash digest
SHA256 bba62305285087eee0b65fd729d9312390de0564b0e7f078502dc76a7dd53ac1
MD5 f90f8ba841b76249c84b8e3f01b307f5
BLAKE2b-256 8c2297b2cf74a3f88105a4cd401b12fdb4586039fa9ea5f2e34ee6c04e80b897

See more details on using hashes here.

File details

Details for the file fow_cli-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: fow_cli-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 80.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"KDE neon","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fow_cli-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 416d4c29825052e8b0884330017c446801ee5f038bbb5295cdc8ca4add16eae4
MD5 e660b4383d3d55458df98bbfee13c02b
BLAKE2b-256 12fa4bc593681004e9e8e539f1efab0aa175755d6b84a1855adec13f95e9ce1e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page