From 892aefa11b6d8326cd8c7adbb32ec2f0f51c1f56 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Martineau Date: Sun, 6 Sep 2026 01:33:38 -0400 Subject: [PATCH] The machine you develop on stops being written into the repo Every device address, login, key path, password and local venv path in the .claude/ skills moves to dev.env at the repo root: gitignored, with a tracked dev.env.example showing the shape. The skills now name $DEV_* keys and say to read dev.env first, so they still run -- against whoever's hardware. Two of the values were not secrets so much as a wrong assumption. The device paths under that home directory are now $HOME-relative in run_instrumented.sh, instrument.py and drive_console.py, and the deploy docs point at $DEPLOY_USER and $KEY_PATH, which those scripts already parameterised. Hardcoding a home directory was only ever correct for one machine. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/skills/add-narrow-emoji/SKILL.md | 7 +++-- .claude/skills/fix-ans-nulls/SKILL.md | 7 +++-- .claude/skills/profile-picocalc/SKILL.md | 29 +++++++++++-------- .../profile-picocalc/scripts/drive_console.py | 2 +- .../profile-picocalc/scripts/instrument.py | 4 +-- .../scripts/run_instrumented.sh | 10 +++++-- .claude/skills/purge-test-data/SKILL.md | 7 +++-- .claude/skills/verify/SKILL.md | 7 +++-- .gitignore | 4 +++ dev.env.example | 28 ++++++++++++++++++ scripts/README.md | 4 ++- scripts/calculinux-setup.sh | 2 +- 12 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 dev.env.example diff --git a/.claude/skills/add-narrow-emoji/SKILL.md b/.claude/skills/add-narrow-emoji/SKILL.md index 2b95478..a4d86df 100644 --- a/.claude/skills/add-narrow-emoji/SKILL.md +++ b/.claude/skills/add-narrow-emoji/SKILL.md @@ -34,8 +34,11 @@ the commonest answer to "please add this one". 1. **Classify the emoji.** Run, from the repo root: - ``` - $DEV_VENV_PYTHON .claude\skills\add-narrow-emoji\classify.py + The interpreter below is `$DEV_VENV_PYTHON` from `dev.env` at the repo root (gitignored; +`dev.env.example` shows the shape) — read it first. + +``` + $DEV_VENV_PYTHON .claude/skills/add-narrow-emoji/classify.py ``` It prints the codepoints, how each authority measures it now (`Rich now`, `pt now`) diff --git a/.claude/skills/fix-ans-nulls/SKILL.md b/.claude/skills/fix-ans-nulls/SKILL.md index 67b25e7..8483928 100644 --- a/.claude/skills/fix-ans-nulls/SKILL.md +++ b/.claude/skills/fix-ans-nulls/SKILL.md @@ -15,9 +15,12 @@ stays exactly where it was, so the file's length never changes. ## Run +The interpreter below is `$DEV_VENV_PYTHON` from `dev.env` at the repo root (gitignored; +`dev.env.example` shows the shape) — read it first. + ``` -$DEV_VENV_PYTHON .claude\skills\fix-ans-nulls\denul.py --dry-run --width 71 meshterm/assets/splash/logo.71.ans -$DEV_VENV_PYTHON .claude\skills\fix-ans-nulls\denul.py --width 71 meshterm/assets/splash/logo.71.ans +$DEV_VENV_PYTHON .claude/skills/fix-ans-nulls/denul.py --dry-run --width 71 meshterm/assets/splash/logo.71.ans +$DEV_VENV_PYTHON .claude/skills/fix-ans-nulls/denul.py --width 71 meshterm/assets/splash/logo.71.ans ``` - Always `--dry-run` first and read the counts. diff --git a/.claude/skills/profile-picocalc/SKILL.md b/.claude/skills/profile-picocalc/SKILL.md index 3245657..8b3a388 100644 --- a/.claude/skills/profile-picocalc/SKILL.md +++ b/.claude/skills/profile-picocalc/SKILL.md @@ -14,18 +14,23 @@ on came from the real app on `/dev/tty1`. ## Reaching the device -`ssh -i $DEV_PICOCALC_SSH_KEY $DEV_PICOCALC_USER@$DEV_PICOCALC_HOST` (key installed; `scp` with the same flags -pushes files byte-exact). Password fallback: `plink -batch -pw $DEV_PICOCALC_PASSWORD`. `sudo` needs the -password piped: `echo $DEV_PICOCALC_PASSWORD | sudo -S …`. +The address, login, key and password are **not in this file**. Read `dev.env` at the +repo root first (gitignored; `dev.env.example` shows the shape) — the `$DEV_*` names +below are its keys, and everything here is written to be run with them substituted. -The repo lives at `$HOME/MeshTerm`, venv at `.venv`. Push changed modules with `scp` +`ssh -i $DEV_PICOCALC_SSH_KEY $DEV_PICOCALC_USER@$DEV_PICOCALC_HOST` (key installed; +`scp` with the same flags pushes files byte-exact). Password fallback: +`plink -batch -pw "$DEV_PICOCALC_PASSWORD"`. `sudo` needs the password piped: +`echo "$DEV_PICOCALC_PASSWORD" | sudo -S …`. + +The repo lives at `$DEV_PICOCALC_REPO`, venv at `.venv`. Push changed modules with `scp` straight into the tree — no reinstall, it is a `pip install -e .`. **One-time**: reading the console back needs the `tty` group. Busybox's `addgroup $DEV_PICOCALC_USER tty` -misparses; edit `/etc/group` instead: +misparses; edit `/etc/group` instead (the trailing name is the deploy user): ```sh -echo $DEV_PICOCALC_PASSWORD | sudo -S sed -i 's/^tty:\(.*\):\(.*\)$/tty:\1:\2,$DEV_PICOCALC_USER/' /etc/group +echo "$DEV_PICOCALC_PASSWORD" | sudo -S sed -i 's/^tty:\(.*\):\(.*\)$/tty:\1:\2,'"$DEV_PICOCALC_USER"'/' /etc/group ``` Re-open the SSH session for it to take effect. Verify: `dd if=/dev/vcs1 bs=1378 count=1 | wc -c` @@ -34,9 +39,9 @@ should print `1378` (53×26). ## Running a tour ```sh -scp -i $DEV_PICOCALC_SSH_KEY scripts/drive_console.py tours/nav_all.txt $DEV_PICOCALC_USER@…:$HOME/MeshTerm/ -ssh -i … $DEV_PICOCALC_USER@… 'cd $HOME/MeshTerm && .venv/bin/python drive_console.py \ - --script nav_all.txt --boot-wait 20 --shots $HOME/tmp/shots' +scp -i $DEV_PICOCALC_SSH_KEY scripts/drive_console.py tours/nav_all.txt "$DEV_PICOCALC_USER@$DEV_PICOCALC_HOST:$DEV_PICOCALC_REPO/" +ssh -i $DEV_PICOCALC_SSH_KEY "$DEV_PICOCALC_USER@$DEV_PICOCALC_HOST" "cd $DEV_PICOCALC_REPO && .venv/bin/python drive_console.py \ + --script nav_all.txt --boot-wait 20 --shots ~/tmp/shots" ``` `drive_console.py` forks a pty sized to the panel, runs MeshTerm inside it, mirrors every @@ -106,12 +111,12 @@ When a page is slow and you need to know *which stage*, run the app under `instr instead of the binary: ```sh -ssh … 'cd $HOME/MeshTerm && .venv/bin/python drive_console.py --script nav_all.txt \ - --exe $HOME/MeshTerm/run_instrumented.sh --boot-wait 20' +ssh … "cd $DEV_PICOCALC_REPO && .venv/bin/python drive_console.py --script nav_all.txt \ + --exe $DEV_PICOCALC_REPO/run_instrumented.sh --boot-wait 20" ``` where `run_instrumented.sh` is a two-line wrapper `exec .venv/bin/python instrument.py "$@"`. -It appends a CSV row per repaint to `$HOME/tmp/keytrace.csv`: handle, header, compose, +It appends a CSV row per repaint to `~/tmp/keytrace.csv` on the device: handle, header, compose, dialog, parse, paint, total, and the `render_to_ansi` call count. Rows tagged `~tick` are idle repaints — what the app burns doing nothing. diff --git a/.claude/skills/profile-picocalc/scripts/drive_console.py b/.claude/skills/profile-picocalc/scripts/drive_console.py index c08d94d..25ea99a 100644 --- a/.claude/skills/profile-picocalc/scripts/drive_console.py +++ b/.claude/skills/profile-picocalc/scripts/drive_console.py @@ -89,7 +89,7 @@ def main(): ap.add_argument("--mock", action="store_true") ap.add_argument("--boot-wait", type=float, default=12.0) ap.add_argument("--out", default="") - ap.add_argument("--exe", default="$HOME/MeshTerm/.venv/bin/meshterm") + ap.add_argument("--exe", default=os.path.expanduser("~/MeshTerm/.venv/bin/meshterm")) ap.add_argument("--quiet-ms", type=float, default=QUIET_S * 1000, help="silence that ends a keystroke; raise it to time screen opens") ap.add_argument("--tee", default="", help="also copy the app's output to this file") diff --git a/.claude/skills/profile-picocalc/scripts/instrument.py b/.claude/skills/profile-picocalc/scripts/instrument.py index 62665c9..d349d80 100644 --- a/.claude/skills/profile-picocalc/scripts/instrument.py +++ b/.claude/skills/profile-picocalc/scripts/instrument.py @@ -1,7 +1,7 @@ """Run the real MeshTerm with per-keystroke stage timing, logged to a CSV. Wraps the stages a keystroke passes through and appends one row per repaint to -$MESHTERM_TRACE (default $HOME/tmp/keytrace.csv): +$MESHTERM_TRACE (default ~/tmp/keytrace.csv): t, screen, action, dispatch_ms, handle_ms, header_ms, compose_ms, dialog_ms, parse_ms, render_ms, flush_ms, total_ms, out_bytes, rta_calls, rta_ms @@ -19,7 +19,7 @@ import os import sys import time -TRACE = os.environ.get("MESHTERM_TRACE", "$HOME/tmp/keytrace.csv") +TRACE = os.environ.get("MESHTERM_TRACE") or os.path.expanduser("~/tmp/keytrace.csv") _acc: dict[str, float] = {} _rows: list[str] = [] diff --git a/.claude/skills/profile-picocalc/scripts/run_instrumented.sh b/.claude/skills/profile-picocalc/scripts/run_instrumented.sh index 5817488..f58986b 100644 --- a/.claude/skills/profile-picocalc/scripts/run_instrumented.sh +++ b/.claude/skills/profile-picocalc/scripts/run_instrumented.sh @@ -1,6 +1,10 @@ #!/bin/sh # Hand this to drive_console.py as --exe to get a per-repaint stage breakdown. # It runs MeshTerm through instrument.py, which appends CSV rows to -# $MESHTERM_TRACE (default $HOME/tmp/keytrace.csv). -cd $HOME/MeshTerm -exec $HOME/MeshTerm/.venv/bin/python $HOME/MeshTerm/instrument.py "$@" +# $MESHTERM_TRACE (default ~/tmp/keytrace.csv). +# +# The checkout is $MESHTERM_REPO, or ~/MeshTerm -- never a hardcoded /home/, +# since this runs as whatever user the device deployed under. +REPO="${MESHTERM_REPO:-$HOME/MeshTerm}" +cd "$REPO" || exit 1 +exec "$REPO/.venv/bin/python" "$REPO/instrument.py" "$@" diff --git a/.claude/skills/purge-test-data/SKILL.md b/.claude/skills/purge-test-data/SKILL.md index c7a7036..023693d 100644 --- a/.claude/skills/purge-test-data/SKILL.md +++ b/.claude/skills/purge-test-data/SKILL.md @@ -13,9 +13,12 @@ carrying the simulator's fingerprints and nothing else. ## Run +The interpreter below is `$DEV_VENV_PYTHON` from `dev.env` at the repo root (gitignored; +`dev.env.example` shows the shape) — read it first. + ``` -$DEV_VENV_PYTHON .claude\skills\purge-test-data\purge.py --dry-run -$DEV_VENV_PYTHON .claude\skills\purge-test-data\purge.py +$DEV_VENV_PYTHON .claude/skills/purge-test-data/purge.py --dry-run +$DEV_VENV_PYTHON .claude/skills/purge-test-data/purge.py ``` - Always `--dry-run` first and sanity-check the counts (real-session tables diff --git a/.claude/skills/verify/SKILL.md b/.claude/skills/verify/SKILL.md index 5debe7c..230214d 100644 --- a/.claude/skills/verify/SKILL.md +++ b/.claude/skills/verify/SKILL.md @@ -8,13 +8,16 @@ model: haiku ## Launch +- Machine-specific values (this venv, the BLE companions below) live in `dev.env` at + the repo root — gitignored, with `dev.env.example` showing the shape. Read it first; + the `$DEV_*` names here are its keys. - Venv interpreter: `$DEV_VENV_PYTHON` - Run the app as a module: `python -m meshterm …` (no install step needed). - `--mock` swaps in the `MockDevice` simulator (four fake nodes streaming adverts/telemetry, packets within a second) — no hardware required. It still writes to the **real default database**, so expect run ids / totals to grow. -- Real hardware is available too: two BLE MeshCore companions - (`$DEV_BLE_COMPANION_PAIRED` is PIN-protected, `$DEV_BLE_COMPANION_OPEN` is open). +- Real hardware is available too: two BLE MeshCore companions — + `$DEV_BLE_COMPANION_PAIRED` is PIN-protected, `$DEV_BLE_COMPANION_OPEN` is open. ## Surfaces diff --git a/.gitignore b/.gitignore index cb8d644..8cecfb3 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,10 @@ venv/ *.db output/ +# Developer environment: the machine-specific half of the .claude/ skills (device +# addresses, logins, the local venv path). dev.env.example is tracked and shows the shape. +dev.env + # Built radio firmware (scripts/xiao-radio/build-firmware.sh writes it here) scripts/xiao-radio/*.uf2 scripts/xiao-radio/_meshcore-build/ diff --git a/dev.env.example b/dev.env.example new file mode 100644 index 0000000..0dca9b2 --- /dev/null +++ b/dev.env.example @@ -0,0 +1,28 @@ +# MeshTerm developer environment — the machine-specific half of the .claude/ skills. +# +# Copy to `dev.env` (gitignored) and fill in your own values. Nothing in here is read by +# the app: `meshterm` is configured by `config.toml` and its own `MESHTERM_*` variables. +# These are only for driving *your* hardware from a dev session, so every name is +# `DEV_`-prefixed and cannot collide with the app's. +# +# set -a; . ./dev.env; set +a # sh/bash +# Get-Content dev.env | ... # or just read the values; nothing sources it for you + +# --- this workstation --------------------------------------------------------------- +# Full path to the venv interpreter, so a skill can run the app without an install step. +DEV_VENV_PYTHON=/path/to/MeshTerm/.venv/bin/python + +# --- the PicoCalc / Luckfox Lyra profiling device ----------------------------------- +# Reachable over the LAN; see the `profile-picocalc` skill for what is done with it. +DEV_PICOCALC_HOST=192.0.2.10 +DEV_PICOCALC_USER=youruser +DEV_PICOCALC_SSH_KEY=~/.ssh/your_key_ed25519 +# Fallback only — key auth is installed. Rotate this if it ever reaches a shared machine. +DEV_PICOCALC_PASSWORD=changeme +# Where the repo is checked out on the device (a `pip install -e .` tree). +DEV_PICOCALC_REPO=/home/youruser/MeshTerm + +# --- BLE test companions ------------------------------------------------------------ +# Two MeshCore companions to verify against: one paired/PIN-protected, one open. +DEV_BLE_COMPANION_PAIRED=MeshCore-Example +DEV_BLE_COMPANION_OPEN=Example-Open diff --git a/scripts/README.md b/scripts/README.md index 3377610..a82039b 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -32,7 +32,9 @@ sh calculinux-setup.sh Two prerequisites the setup script checks for but cannot embed: -- **Deploy key** — place the read-only GitHub deploy key at `$HOME/.ssh/id_ed25519` +- **Deploy key** — place the read-only GitHub deploy key at + `/home/$DEPLOY_USER/.ssh/id_ed25519` (`DEPLOY_USER` defaults to `meshterm`; `KEY_PATH` + overrides the location outright) (mode 600) before running; the clone uses it over SSH. - **Wi-Fi credentials** — the kick only nudges a network iwd already knows. Either connect once by hand (`iwctl station wlan0 connect `) or export `WIFI_SSID` and `WIFI_PSK` diff --git a/scripts/calculinux-setup.sh b/scripts/calculinux-setup.sh index 266fea8..167cd19 100644 --- a/scripts/calculinux-setup.sh +++ b/scripts/calculinux-setup.sh @@ -34,7 +34,7 @@ # rounded frame corners + the list cursor the bare console can't draw). # # Two prerequisites this script cannot safely embed and will check for / guide you through: -# * the read-only deploy key at $HOME/.ssh/id_ed25519 (never commit a private key); +# * the read-only deploy key at $KEY_PATH (never commit a private key); # * Wi-Fi credentials known to iwd. The kick only nudges an ALREADY-known network. Either # provision it once by hand (`iwctl station wlan0 connect `), or export # WIFI_SSID and WIFI_PSK before running and this script writes the iwd config for you.