# scripts/ Operational scripts for the PKB. Everything here is side-effectful — writing files, running webhooks, ingesting voice notes. Keep it small; anything analytical belongs in `.claude/commands/` or in the `mql` CLI. ## Current contents | Path | Language | Purpose | |---|---|---| | `audiopen-ingest.sh` | bash | Normalises raw AudioPen drops (`inbox/raw/*.md`) into properly-frontmattered fleeting notes in `inbox/`. Idempotent. | | `audiopen-webhook/main.py` | Python (stdlib) | Tiny HTTP server that receives AudioPen webhook POSTs and writes to `inbox/raw/`. Designed to sit behind Tailscale Funnel. | | `audiopen-webhook/*.example` | systemd | User-unit examples for the receiver + the path/service that runs `audiopen-ingest.sh` on file change. | Setup guide for the full pipeline: [`docs/setup/audiopen.md`](../docs/setup/audiopen.md). ## Python venv convention Python code in this repo is **stdlib-only as of now**. No pip-installable dependencies. If/when a real dependency arrives (e.g. `watchdog`, `imap-tools`, anything heavier than stdlib), the convention is: - Venv lives at **`scripts/.venv/`** (gitignored — every user materialises locally). - Dependencies tracked in **`scripts/pyproject.toml`** (committed). - Bootstrap on a fresh machine: ```sh cd scripts python3 -m venv .venv .venv/bin/pip install -e . ``` Or with `uv` (recommended if available): ```sh cd scripts uv venv uv pip install -e . ``` - Scripts that need the venv should either: - Invoke `scripts/.venv/bin/python3` explicitly in their shebang or systemd `ExecStart`. - Or document activation (`source scripts/.venv/bin/activate`) before running. Keeping the venv under `scripts/` rather than the repo root keeps the Obsidian file tree uncluttered — `scripts/` is already excluded from Obsidian's index (`userIgnoreFilters` in `.obsidian/app.json`) so nothing Python-related will ever show in the graph or search. **Don't create the venv preemptively.** When the first dependency lands, that's when you create it. Keeps the repo lighter and avoids stale venv directories. The `pyproject.toml` uses `[project.optional-dependencies]` to split deps by tool — e.g. `pip install -e .[imap]` installs only the IMAP fetcher's deps. This lets the webhook receiver stay dep-free while heavier tools can pull in what they need. ## Shell scripts Kept in plain bash. Portable to any Linux running GNU coreutils (which Bazzite does). No zsh-isms, no bashisms beyond `set -euo pipefail` and arrays. If a script grows past ~200 lines or needs real argument parsing, rewrite it in Python (and you're now back in the venv convention above).