--- phase: 02-env-audit-and-cli-polish plan: 01 type: execute wave: 1 depends_on: [] files_modified: - claudebox.sh autonomous: true requirements: - UX-03 - UX-04 - UX-05 must_haves: truths: - "claudebox --check reports pass/fail for bwrap, claude, git, nix, ~/.claudebox, and ANTHROPIC_API_KEY" - "claudebox --dry-run prints the full bwrap command with all flags and exits without executing" - "claudebox --yes -y flags are recognized and stored for Plan 02 audit skip" - "Unknown flags are passed through to claude in CLAUDE_ARGS array" - "Flag parsing handles multiple claudebox flags in any order" artifacts: - path: "claudebox.sh" provides: "Refactored flag parsing, --check, --dry-run" contains: "CHECK_MODE|DRY_RUN|SKIP_AUDIT|CLAUDE_ARGS" key_links: - from: "claudebox.sh flag parsing" to: "SANDBOX_CMD construction" via: "CLAUDE_ARGS array replaces raw $@" pattern: "CLAUDE_ARGS" --- Refactor claudebox.sh flag parsing to support multiple flags, then add --check and --dry-run early-exit modes. Purpose: Foundation for all Phase 2 CLI flags. The current single-flag parser (for/shift/break) cannot handle multiple claudebox flags. This plan refactors it to a while/shift loop that collects claudebox flags and accumulates remaining args in CLAUDE_ARGS. Then adds two diagnostic modes that exit before sandbox launch. Output: claudebox.sh with working --check, --dry-run, and flag scaffolding for --yes/-y. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/02-env-audit-and-cli-polish/02-CONTEXT.md @.planning/phases/02-env-audit-and-cli-polish/02-RESEARCH.md @claudebox.sh @flake.nix Task 1: Refactor flag parsing to while/shift with CLAUDE_ARGS accumulator claudebox.sh claudebox.sh Replace the current flag parsing block (lines 1-9) with a while/shift loop that handles multiple claudebox flags. Define these variables at the top of the script: ```bash SKIP_AUDIT=false DRY_RUN=false CHECK_MODE=false SHELL_MODE=false CLAUDE_ARGS=() while (( $# > 0 )); do case "$1" in --yes|-y) SKIP_AUDIT=true ;; --dry-run) DRY_RUN=true ;; --check) CHECK_MODE=true ;; --shell) SHELL_MODE=true ;; --) shift; CLAUDE_ARGS+=("$@"); break ;; *) CLAUDE_ARGS+=("$1") ;; esac shift done ``` Per D-08, --yes/-y sets SKIP_AUDIT=true (consumed by Plan 02's audit display). Then update the SANDBOX_CMD construction (currently lines 70-74) to use CLAUDE_ARGS instead of $@: ```bash if [[ "$SHELL_MODE" == true ]]; then SANDBOX_CMD=("$SANDBOX_BASH" "${CLAUDE_ARGS[@]}") else SANDBOX_CMD=("$CLAUDE_BIN" --dangerously-skip-permissions "${CLAUDE_ARGS[@]}") fi ``` This ensures unknown flags like `--model sonnet` pass through to claude correctly. cd /home/toph/code/tools/claudebox && grep -q 'SKIP_AUDIT=false' claudebox.sh && grep -q 'DRY_RUN=false' claudebox.sh && grep -q 'CHECK_MODE=false' claudebox.sh && grep -q 'CLAUDE_ARGS' claudebox.sh && grep -q 'while (( \$# > 0 ))' claudebox.sh && echo "PASS: flag parsing refactored" - claudebox.sh contains `SKIP_AUDIT=false` variable declaration - claudebox.sh contains `DRY_RUN=false` variable declaration - claudebox.sh contains `CHECK_MODE=false` variable declaration - claudebox.sh contains `SHELL_MODE=false` variable declaration - claudebox.sh contains `CLAUDE_ARGS=()` array declaration - claudebox.sh contains `while (( $# > 0 ))` loop (not `for arg in`) - claudebox.sh contains `CLAUDE_ARGS+=("$1")` for unknown flag accumulation - SANDBOX_CMD uses `"${CLAUDE_ARGS[@]}"` not `"$@"` - The old `for arg in "$@"` parsing block is removed Flag parsing handles --yes, -y, --dry-run, --check, --shell, and passes unknown flags to CLAUDE_ARGS. SANDBOX_CMD uses CLAUDE_ARGS. Task 2: Add --dry-run mode that prints full bwrap command and exits claudebox.sh claudebox.sh Per D-09 (UX-04), add a --dry-run handler after the SANDBOX_CMD construction and before the `exec bwrap` line. When DRY_RUN=true, print the complete bwrap invocation in a readable multiline format to stderr, then exit 0. Do NOT prompt for confirmation (--dry-run implies --yes per research open question 1). Insert this block right before `exec bwrap`: ```bash # --dry-run: print the bwrap command without executing (D-09, UX-04) if [[ "$DRY_RUN" == true ]]; then { echo "bwrap \\" echo " --clearenv \\" # Print each ENV_ARGS entry local_i=0 while (( local_i < ${#ENV_ARGS[@]} )); do printf ' %s %s %q \\\n' "${ENV_ARGS[$local_i]}" "${ENV_ARGS[$((local_i+1))]}" "${ENV_ARGS[$((local_i+2))]}" (( local_i += 3 )) done echo " --tmpfs / \\" echo " --proc /proc \\" echo " --dev /dev \\" echo " --tmpfs /tmp \\" echo " --ro-bind /nix/store /nix/store \\" echo " --bind /nix/var/nix /nix/var/nix \\" echo " --ro-bind /etc/resolv.conf /etc/resolv.conf \\" echo " --ro-bind /etc/ssl /etc/ssl \\" echo " --ro-bind /etc/static /etc/static \\" echo " --ro-bind /etc/passwd /etc/passwd \\" echo " --ro-bind /etc/group /etc/group \\" echo " --ro-bind /etc/hosts /etc/hosts \\" echo " --ro-bind /etc/nsswitch.conf /etc/nsswitch.conf \\" echo " --ro-bind /etc/nix /etc/nix \\" printf ' --symlink %q /usr/bin/env \\\n' "$(readlink -f "$(command -v env)")" echo " --tmpfs $HOME \\" echo " --bind $HOME/.claudebox $HOME/.claude \\" printf ' --ro-bind %q %s/.gitconfig \\\n' "$GITCONFIG_TMP" "$HOME" echo " --bind $CWD $CWD \\" echo " --chdir $CWD \\" printf ' -- %s\n' "${SANDBOX_CMD[*]}" } >&2 exit 0 fi ``` Note: The ENV_ARGS array is structured as triplets (--setenv key value), so iterate in steps of 3. Use `printf %q` for values that might contain special chars. The filesystem mount flags mirror the actual `exec bwrap` call exactly -- if the bwrap call changes, this must be updated too. Important: Since writeShellApplication uses `set -euo pipefail` and shellcheck, avoid using `local` outside functions. Use a unique variable name like `dry_run_i` instead of `local_i` for the loop counter. cd /home/toph/code/tools/claudebox && grep -q 'DRY_RUN.*true' claudebox.sh && grep -q 'dry.run' claudebox.sh && grep -c 'exit 0' claudebox.sh | grep -q '[1-9]' && echo "PASS: dry-run handler present" - claudebox.sh contains an `if [[ "$DRY_RUN" == true ]]` block - The dry-run block prints `bwrap \` as first line to stderr - The dry-run block prints `--clearenv \` on its own line - The dry-run block iterates ENV_ARGS in steps of 3 to print --setenv triplets - The dry-run block prints all filesystem mount flags matching the actual exec bwrap call - The dry-run block ends with `exit 0` - All dry-run output goes to stderr (>&2) - `nix build` succeeds (shellcheck passes) Running `claudebox --dry-run` prints the full bwrap command in multiline format to stderr and exits 0 without launching the sandbox. Task 3: Add --check mode that verifies prerequisites and exits claudebox.sh claudebox.sh Per D-10 (UX-05), add a --check handler as an early exit right after flag parsing and before any binary resolution or env construction. When CHECK_MODE=true, run diagnostic checks and exit. Insert this block immediately after the flag parsing while/shift loop: ```bash # --check: verify prerequisites and exit (D-10, UX-05) if [[ "$CHECK_MODE" == true ]]; then pass=true green=$'\033[32m' red=$'\033[31m' yellow=$'\033[33m' reset=$'\033[0m' check_cmd() { if command -v "$1" &>/dev/null; then echo "${green}OK${reset} $1" >&2 else echo "${red}FAIL${reset} $1 -- not found" >&2 pass=false fi } echo "claudebox prerequisites:" >&2 echo "" >&2 check_cmd bwrap check_cmd claude check_cmd git check_cmd curl check_cmd nix if [[ -d "$HOME/.claudebox" ]]; then echo "${green}OK${reset} ~/.claudebox exists" >&2 else echo "${red}FAIL${reset} ~/.claudebox -- not found (will be created on first run)" >&2 fi if [[ -v ANTHROPIC_API_KEY ]]; then echo "${green}OK${reset} ANTHROPIC_API_KEY is set" >&2 else echo "${yellow}WARN${reset} ANTHROPIC_API_KEY is not set" >&2 fi echo "" >&2 if [[ "$pass" == true ]]; then echo "${green}All checks passed.${reset}" >&2 exit 0 else echo "${red}Some checks failed.${reset}" >&2 exit 1 fi fi ``` The check verifies: bwrap, claude, git, curl, nix (required binaries), ~/.claudebox directory, and ANTHROPIC_API_KEY (warn only, not a hard failure). Exit 0 if all required checks pass, exit 1 if any fail. All output to stderr per D-07. cd /home/toph/code/tools/claudebox && grep -q 'CHECK_MODE.*true' claudebox.sh && grep -q 'check_cmd' claudebox.sh && grep -q 'prerequisites' claudebox.sh && echo "PASS: check handler present" - claudebox.sh contains an `if [[ "$CHECK_MODE" == true ]]` block - The check block is positioned BEFORE binary resolution (`command -v bash`, `command -v claude`) - The check block tests for: bwrap, claude, git, curl, nix via `command -v` - The check block tests for `$HOME/.claudebox` directory existence - The check block tests for ANTHROPIC_API_KEY with WARN (not FAIL) - The check block prints colored OK/FAIL/WARN indicators to stderr - The check block exits 0 on all-pass, exits 1 on any FAIL - `nix build` succeeds (shellcheck passes) Running `claudebox --check` prints pass/fail for each prerequisite to stderr and exits with appropriate code. ## Trust Boundaries | Boundary | Description | |----------|-------------| | host env -> --dry-run output | Env var values printed to stderr may contain secrets | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-02-01 | Information Disclosure | --dry-run printing env values | accept | --dry-run shows env values unmasked because the user explicitly asked to see the full command. Plan 02 adds masking for the audit display. The dry-run user is debugging, not reviewing for leaks. | | T-02-02 | Information Disclosure | --check printing ANTHROPIC_API_KEY status | mitigate | Only print presence/absence, never the value. Use `[[ -v ANTHROPIC_API_KEY ]]` not echo of value. | 1. `grep -c 'CLAUDE_ARGS' claudebox.sh` returns >= 3 (declaration + accumulation + usage) 2. `grep 'DRY_RUN\|CHECK_MODE\|SKIP_AUDIT' claudebox.sh` shows all three flag variables 3. `nix build` succeeds (shellcheck validation) - Flag parsing supports --yes, -y, --dry-run, --check, --shell, and -- separator - Unknown flags accumulate in CLAUDE_ARGS and pass through to claude - --check exits early with colored diagnostic output - --dry-run prints full bwrap command and exits - shellcheck passes via nix build After completion, create `.planning/phases/02-env-audit-and-cli-polish/02-01-SUMMARY.md`