zscripts-token-savers/bash
kellymichels 32b9f7fba9
feat(zstart): uvicorn/startApp support + missing-venv warning + fix --detached hang (#12)
* feat(zstart): support uvicorn/ASGI apps via a startApp config field

Python projects could only be started as `python -m <startModule>`, so
FastAPI/ASGI apps that run under uvicorn (like evo-ai:
`uvicorn app.main:app`) couldn't be started by zstart in either port.

Add an optional `startApp` field. When set, zstart runs
`uvicorn <startApp> --host <bind-host> --port <ports.dev> --reload` via
the venv python's -m (no PATH juggling), integrating zstart's existing
bind-host and dev-port handling. startApp takes precedence over
startModule; a python project still needs one or the other. Applied to
bash and PowerShell, documented in the README + example configs.

* feat(zstart): warn when falling back to system python (no project venv)

A python project with no .venv (or only a Windows .venv when on WSL)
silently ran under the system interpreter, which usually lacks the
project's deps - producing a cryptic ModuleNotFoundError far from the
cause. Now zstart prints a clear warning naming the missing venv and the
one-liner to create it, before starting. Bash + PowerShell.

* fix(bash): zstart --detached no longer hangs on the tracking FIFO

Detached mode forked the long-lived server while it still inherited the
ztokens tracking fds (the capture FIFO on 1/2, saved stdout/stderr on
3/4). The parent's EXIT-trap footer runs `tee` on that FIFO and waits for
EOF, which never came while the server held it open - so `zstart
--detached` (and zstartd / zrestart --detached) hung instead of
returning. detach() now redirects stdin<-/dev/null, stdout/stderr->log
and closes fd 3/4 before exec'ing the server. Verified on WSL: detached
returns in 0s and the server still boots.

* fix(zstart): git-pull pre-step can't hang on a credential prompt

start.gitPull ran `git pull --ff-only` before starting the server; in an
environment with no cached git credentials (e.g. WSL against an HTTPS
GitHub remote) git prompted "Username for 'https://github.com':" and the
whole start blocked on stdin. Run the pull with GIT_TERMINAL_PROMPT=0 so
it fails fast, log a clear "auto-pull skipped" note, and start with the
current checkout. Bash + PowerShell.
2026-07-25 22:17:14 -05:00
..
README.md fix(bash): make the port bash-3.2 / BSD-clean so it runs on stock macOS (#8) 2026-07-22 18:41:11 -05:00
setup_backup_schedule fix(bash): zbackup — real pg_dump error reporting + require an explicit target (all) (#9) 2026-07-25 15:17:12 -05:00
zbackup fix(bash): zbackup — real pg_dump error reporting + require an explicit target (all) (#9) 2026-07-25 15:17:12 -05:00
zbackup_and_sync fix(bash): zbackup — real pg_dump error reporting + require an explicit target (all) (#9) 2026-07-25 15:17:12 -05:00
zbackup_ec2 fix: zbackup_ec2/zec2/zec2online require an explicit target (all), matching zbackup (#11) 2026-07-25 15:21:18 -05:00
zconfig.example.json feat(zstart): uvicorn/startApp support + missing-venv warning + fix --detached hang (#12) 2026-07-25 22:17:14 -05:00
zdeploy fix(bash): make the port bash-3.2 / BSD-clean so it runs on stock macOS (#8) 2026-07-22 18:41:11 -05:00
zec2 fix: zbackup_ec2/zec2/zec2online require an explicit target (all), matching zbackup (#11) 2026-07-25 15:21:18 -05:00
zec2online fix: zbackup_ec2/zec2/zec2online require an explicit target (all), matching zbackup (#11) 2026-07-25 15:21:18 -05:00
zhelpers.sh fix: exclude .env secrets from python/vite deploy zips (not backups) (#23) 2026-07-25 15:22:04 -05:00
zkill feat(zkill): support 'all' to stop every project's dev server (#25) 2026-07-24 17:35:14 -05:00
zrepair fix(bash): make the port bash-3.2 / BSD-clean so it runs on stock macOS (#8) 2026-07-22 18:41:11 -05:00
zrestart feat(bash): native bash port of all z-scripts for Linux/macOS/WSL (#5) 2026-07-22 16:35:05 -05:00
zsetup_mail feat(bash): native bash port of all z-scripts for Linux/macOS/WSL (#5) 2026-07-22 16:35:05 -05:00
zstart feat(zstart): uvicorn/startApp support + missing-venv warning + fix --detached hang (#12) 2026-07-25 22:17:14 -05:00
zstart_docker feat(bash): native bash port of all z-scripts for Linux/macOS/WSL (#5) 2026-07-22 16:35:05 -05:00
zstop feat(bash): native bash port of all z-scripts for Linux/macOS/WSL (#5) 2026-07-22 16:35:05 -05:00
zsync fix(bash): translate Windows config paths to WSL/Unix form so the bash port works on WSL (#7) 2026-07-22 18:40:52 -05:00

Token Savers — bash port (Linux / macOS / WSL)

Native bash versions of the z-scripts. Same commands, same zconfig.json schema, same behavior — no PowerShell required.

Requirements

  • bash 3.2+ (the stock macOS bash works), jq, curl, zip, OpenSSH (ssh/scp)
  • lsof (for zkill/zrestart), rsync (for zsync <project> mirror mode)
  • Docker + docker compose on the remote host for the deploy scripts

Debian/Ubuntu: sudo apt install jq curl zip lsof rsync macOS: brew install jq (bash, curl, zip, ssh, lsof and rsync already ship with macOS)

Install

git clone https://github.com/kellymichels/zscripts-token-savers.git ~/zscripts
cd ~/zscripts/bash
cp zconfig.example.json zconfig.json   # then fill in your values
chmod +x z* setup_backup_schedule
echo 'export PATH="$HOME/zscripts/bash:$PATH"' >> ~/.bashrc && source ~/.bashrc

Commands

Same set as the PowerShell versions — the project key in zconfig.json IS the command argument:

Command What it does
zstart <p> [--detached] start local dev server (python/vite/nextjs)
zkill <p> free the project's dev port
zrestart <p> [--detached] kill + start in one command
zstop <p> stop the project's compose stack on the server
zdeploy <p> [--note "msg"] zip → scp → docker compose build/up → verify build version
zec2 [<p>] TCP + HTTP + live-version reachability check
zec2online [<p>] deep health check; auto-starts downed stacks
zrepair <p> audit/repair container + proxy routing, smoke test
zbackup [<p>] [--tag t] local source zip (+ pg_dump when .env has DATABASE_URL)
zbackup_ec2 [<p>] pull db dump + data dirs down from the server
zsync copy new backup files offsite (never overwrites)
zbackup_and_sync both of the above; cron-friendly
setup_backup_schedule --install daily backup cron job
zsetup_mail --domain d provision docker-mailserver mailboxes + print DNS records
zstart_docker bring up the local compose stack in bash/docker/

Flags use GNU style (--port 3000, --detached) instead of PowerShell style (-Port, -Detached). Detached dev servers log to /tmp/zstart-<project>.log.

Usage tracking

Every run prints a footer with its own output volume:

--- 1,048 lines / 69,009 chars / ~19,717 tokens est. (Claude Code) ---

and (when jq is available) appends one JSON line per run to tokens.jsonl, so you can see how much infrastructure output you keep out of an AI agent's context over time. Fields: ts, script, projects, lines, chars, est, model (est ≈ chars / 3.5). Nested runs (the zkill/zstart inside zrestart) are counted once, not double.

Where it's written, first match wins:

  1. $ZTOKENS_DATA
  2. ztokens.dataDir in zconfig.json
  3. the sibling ../ztokens/data directory, if present
  4. ~/.ztokens/data (created on first run)

Set ZTOKENS_MODEL to tag records with a specific model; it defaults to est. chars/3.5.

Differences from the PowerShell versions

  • zsync <viteproject> mirrors dist/ with rsync -a --delete (robocopy /MIR equivalent).
  • Scheduling uses cron (setup_backup_schedule) instead of Windows Task Scheduler.
  • The MOTD banner picks a random motd/*.txt instead of tracking a shuffle rotation.
  • Windows-only helpers (.cmd launchers) don't exist — scripts are directly executable.