# Coding Standards — modal-shell ## Shell - **Default shell:** bash. Shebang is always `#!/usr/bin/env bash`. - **Strict mode:** Every file starts with `set -euo pipefail`. No exceptions. - **No bashisms in base.sh:** `base.sh` must also be compatible with zsh (users may source it from their zsh config). - **Functions over repetition:** If the same logic appears in two modes, move it to `base.sh`. - **No global side effects in sourced files:** `base.sh` sets env vars; it does not print output, start processes, or modify files. ## Naming - Mode files: lowercase, hyphen-separated, `.sh` suffix. e.g. `db.sh`, `build.sh`, `reset.sh`. - Variables in `base.sh`: `UPPER_SNAKE_CASE`. Exported. - Local variables in mode files: `lower_snake_case`. Use `local` inside functions. - The dispatcher: `ms` (no extension, executable). ## Mode File Structure ```sh #!/usr/bin/env bash # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/base.sh" # mode logic here ``` ## Confirmation Pattern for Destructive Modes ```sh echo "This will destroy and rebuild the environment." read -rp "Continue? [y/N] " confirm [[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 1; } ``` ## Error Handling - Let `set -e` do the work. Do not swallow errors with `|| true` unless intentional and commented. - On expected failure paths, print a helpful message to stderr before exiting: `echo "error: " >&2` - Do not use `exit 0` at the end of scripts — implicit success is fine. ## Testing - Syntax check: `bash -n modes/.sh` before committing any mode file. - Dry-run: modes that exec into an environment can be tested with `bash -c 'source modes/base.sh && echo $PROJECT_ROOT'`. - No automated test framework required for this project's scope. Manual verification is acceptable. ## The Dispatcher (`ms`) - Must stay under 40 lines. - No logic beyond: find the modes directory, match argument to filename, exec. - If no argument: list available modes (strip `.sh`, sort). - If unknown mode: print error to stderr, exit 1. - Never source the mode file — always exec it (so the mode can itself exec into a shell without forking). ## What Not to Do - No `eval`. - No `set +e` blocks. - No hardcoded paths outside of `base.sh`. - No secrets in any file tracked by git. - No modes that call other modes.