diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 20f34c9..e3fcc9e 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -43,10 +43,11 @@ Plans:
2. Running `claudebox --yes` or `claudebox -y` skips the env audit and launches immediately
3. Running `claudebox --dry-run` prints the full bwrap command without executing it
4. Running `claudebox --check` reports whether bwrap exists, required Nix packages are available, and ~/.claudebox exists
-**Plans**: TBD
+**Plans:** 2 plans
Plans:
-- [ ] 02-01: TBD
+- [ ] 02-01-PLAN.md -- Refactor flag parsing, add --check and --dry-run modes
+- [ ] 02-02-PLAN.md -- Env audit display with grouping, masking, and confirmation prompt
### Phase 3: Sandbox-Aware Prompting
**Goal**: Claude inside the sandbox knows it is sandboxed, how to install tools, and what is unavailable
@@ -67,6 +68,6 @@ Phases execute in numeric order: 1 -> 2 -> 3
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
-| 1. Minimal Viable Sandbox | 0/2 | Planned | - |
-| 2. Env Audit and CLI Polish | 0/1 | Not started | - |
+| 1. Minimal Viable Sandbox | 2/2 | Complete | - |
+| 2. Env Audit and CLI Polish | 0/2 | Planned | - |
| 3. Sandbox-Aware Prompting | 0/1 | Not started | - |
diff --git a/.planning/phases/02-env-audit-and-cli-polish/02-01-PLAN.md b/.planning/phases/02-env-audit-and-cli-polish/02-01-PLAN.md
new file mode 100644
index 0000000..581338f
--- /dev/null
+++ b/.planning/phases/02-env-audit-and-cli-polish/02-01-PLAN.md
@@ -0,0 +1,296 @@
+---
+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
+
+
+
diff --git a/.planning/phases/02-env-audit-and-cli-polish/02-02-PLAN.md b/.planning/phases/02-env-audit-and-cli-polish/02-02-PLAN.md
new file mode 100644
index 0000000..6883028
--- /dev/null
+++ b/.planning/phases/02-env-audit-and-cli-polish/02-02-PLAN.md
@@ -0,0 +1,325 @@
+---
+phase: 02-env-audit-and-cli-polish
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - "02-01"
+files_modified:
+ - claudebox.sh
+autonomous: true
+requirements:
+ - UX-01
+ - UX-02
+must_haves:
+ truths:
+ - "Running claudebox without --yes shows all env vars grouped by source before launching"
+ - "Env vars are grouped into Sandbox-generated, Host (allowlisted), and Extra (CLAUDEBOX_EXTRA_ENV) sections"
+ - "PATH is displayed split by colon with one entry per line"
+ - "Values matching *KEY*, *TOKEN*, *SECRET*, *PASSWORD*, *CREDENTIAL* are auto-masked"
+ - "User sees a Proceed? [Y/n] prompt and can abort by typing n"
+ - "Non-interactive stdin (piped, CI) aborts with error telling user to pass --yes/-y"
+ - "All audit output goes to stderr, stdout stays clean"
+ artifacts:
+ - path: "claudebox.sh"
+ provides: "Env audit display, masking, confirmation prompt"
+ contains: "mask_value|print_audit|Proceed"
+ key_links:
+ - from: "claudebox.sh env audit"
+ to: "claudebox.sh SKIP_AUDIT flag"
+ via: "if [[ $SKIP_AUDIT != true ]]"
+ pattern: "SKIP_AUDIT"
+ - from: "claudebox.sh audit display"
+ to: "ENV_ARGS array"
+ via: "parallel display arrays populated during env construction"
+ pattern: "AUDIT_SANDBOX\\|AUDIT_HOST\\|AUDIT_EXTRA"
+---
+
+
+Add pre-launch env audit display with grouped sections, value masking, and confirmation prompt to claudebox.sh.
+
+Purpose: Transparency before sandbox launch. The user sees exactly what environment enters the sandbox, with sensitive values masked, and can abort if something looks wrong. Non-interactive environments are forced to use --yes.
+
+Output: claudebox.sh with full env audit display and interactive confirmation, skippable via --yes/-y (flag already parsed by Plan 01).
+
+
+
+@$HOME/.claude/get-shit-done/workflows/execute-plan.md
+@$HOME/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/phases/02-env-audit-and-cli-polish/02-CONTEXT.md
+@.planning/phases/02-env-audit-and-cli-polish/02-RESEARCH.md
+@.planning/phases/02-env-audit-and-cli-polish/02-01-SUMMARY.md
+
+@claudebox.sh
+@flake.nix
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Task 1: Add parallel display arrays and env audit display function
+ claudebox.sh
+ claudebox.sh
+
+Add parallel display data structures alongside the existing ENV_ARGS construction, then add a display function. This task implements D-01, D-02, D-03, D-04, D-07.
+
+**Step 1: Add ANSI color constants and mask_value function near the top of the script (after flag parsing, before binary resolution):**
+
+```bash
+# ANSI formatting (D-03)
+if [[ -t 2 ]] && [[ "${NO_COLOR:-}" == "" ]]; then
+ BOLD=$'\033[1m'
+ RESET=$'\033[0m'
+ DIM=$'\033[2m'
+ CYAN=$'\033[36m'
+ YELLOW=$'\033[33m'
+ GREEN=$'\033[32m'
+ RED=$'\033[31m'
+else
+ BOLD="" RESET="" DIM="" CYAN="" YELLOW="" GREEN="" RED=""
+fi
+
+# Mask sensitive values (D-04)
+mask_value() {
+ local name="$1" value="$2"
+ local upper="${name^^}"
+ if [[ "$upper" == *KEY* || "$upper" == *TOKEN* || "$upper" == *SECRET* || "$upper" == *PASSWORD* || "$upper" == *CREDENTIAL* ]]; then
+ if (( ${#value} > 11 )); then
+ echo "${value:0:7}...${value: -4}"
+ else
+ echo "***"
+ fi
+ else
+ echo "$value"
+ fi
+}
+```
+
+**Step 2: Add display tracking arrays.** Declare these right before the ENV_ARGS construction block:
+
+```bash
+# Parallel display data for env audit (D-01)
+declare -a AUDIT_SANDBOX_KEYS=()
+declare -A AUDIT_SANDBOX_VALS=()
+declare -a AUDIT_HOST_KEYS=()
+declare -A AUDIT_HOST_VALS=()
+declare -a AUDIT_EXTRA_KEYS=()
+declare -A AUDIT_EXTRA_VALS=()
+```
+
+**Step 3: Populate display arrays alongside ENV_ARGS.** After each --setenv addition to ENV_ARGS, also record in the audit arrays.
+
+For sandbox-generated vars, after the ENV_ARGS=(...) block, add:
+```bash
+AUDIT_SANDBOX_KEYS=(HOME USER PATH SHELL TMPDIR XDG_RUNTIME_DIR NIX_SSL_CERT_FILE SSL_CERT_FILE)
+AUDIT_SANDBOX_VALS[HOME]="$HOME"
+AUDIT_SANDBOX_VALS[USER]="$USER"
+AUDIT_SANDBOX_VALS[PATH]="$SANDBOX_PATH"
+AUDIT_SANDBOX_VALS[SHELL]="$SANDBOX_BASH"
+AUDIT_SANDBOX_VALS[TMPDIR]="/tmp"
+AUDIT_SANDBOX_VALS[XDG_RUNTIME_DIR]="/tmp"
+AUDIT_SANDBOX_VALS[NIX_SSL_CERT_FILE]="/etc/ssl/certs/ca-certificates.crt"
+AUDIT_SANDBOX_VALS[SSL_CERT_FILE]="/etc/ssl/certs/ca-certificates.crt"
+```
+
+For host allowlisted vars, inside the existing HOST_ALLOWLIST loop, add after each `ENV_ARGS+=`:
+```bash
+AUDIT_HOST_KEYS+=("$var")
+AUDIT_HOST_VALS[$var]="${!var}"
+```
+
+For CLAUDEBOX_EXTRA_ENV vars, inside the existing extras loop, add after each `ENV_ARGS+=`:
+```bash
+AUDIT_EXTRA_KEYS+=("$var")
+AUDIT_EXTRA_VALS[$var]="${!var}"
+```
+
+**Step 4: Add the print_audit function** (after the display arrays are populated, before the dry-run check):
+
+```bash
+# Env audit display (D-01, D-02, D-03, D-04, D-07, UX-01)
+print_audit() {
+ echo "${BOLD}${CYAN}=== Sandbox Environment ===${RESET}" >&2
+ echo "" >&2
+
+ # Sandbox-generated (D-01)
+ echo "${BOLD}Sandbox-generated:${RESET}" >&2
+ for var in "${AUDIT_SANDBOX_KEYS[@]}"; do
+ if [[ "$var" == "PATH" ]]; then
+ echo " ${GREEN}PATH=${RESET}" >&2
+ IFS=':' read -ra path_entries <<< "${AUDIT_SANDBOX_VALS[PATH]}"
+ for entry in "${path_entries[@]}"; do
+ echo " ${DIM}${entry}${RESET}" >&2
+ done
+ else
+ echo " ${GREEN}${var}=${RESET}$(mask_value "$var" "${AUDIT_SANDBOX_VALS[$var]}")" >&2
+ fi
+ done
+ echo "" >&2
+
+ # Host allowlisted (D-01)
+ if (( ${#AUDIT_HOST_KEYS[@]} > 0 )); then
+ echo "${BOLD}Host (allowlisted):${RESET}" >&2
+ for var in "${AUDIT_HOST_KEYS[@]}"; do
+ echo " ${YELLOW}${var}=${RESET}$(mask_value "$var" "${AUDIT_HOST_VALS[$var]}")" >&2
+ done
+ echo "" >&2
+ fi
+
+ # Extra from CLAUDEBOX_EXTRA_ENV (D-01)
+ if (( ${#AUDIT_EXTRA_KEYS[@]} > 0 )); then
+ echo "${BOLD}Extra (CLAUDEBOX_EXTRA_ENV):${RESET}" >&2
+ for var in "${AUDIT_EXTRA_KEYS[@]}"; do
+ echo " ${YELLOW}${var}=${RESET}$(mask_value "$var" "${AUDIT_EXTRA_VALS[$var]}")" >&2
+ done
+ echo "" >&2
+ fi
+}
+```
+
+All output goes to stderr per D-07.
+
+
+ cd /home/toph/code/tools/claudebox && grep -q 'mask_value' claudebox.sh && grep -q 'print_audit' claudebox.sh && grep -q 'AUDIT_SANDBOX_KEYS' claudebox.sh && grep -q 'AUDIT_HOST_KEYS' claudebox.sh && grep -q 'AUDIT_EXTRA_KEYS' claudebox.sh && grep -q 'NO_COLOR' claudebox.sh && echo "PASS: audit display infrastructure present"
+
+
+ - claudebox.sh contains `mask_value()` function
+ - mask_value checks for KEY, TOKEN, SECRET, PASSWORD, CREDENTIAL (case-insensitive via `${name^^}`)
+ - mask_value shows first 7 + last 4 chars with `...` for values longer than 11
+ - mask_value shows `***` for values 11 chars or shorter
+ - claudebox.sh contains `print_audit()` function
+ - print_audit displays three sections: "Sandbox-generated:", "Host (allowlisted):", "Extra (CLAUDEBOX_EXTRA_ENV):"
+ - PATH display splits by colon with one entry per line (indented)
+ - All audit output uses `>&2` for stderr
+ - ANSI colors are suppressed when stderr is not a TTY or NO_COLOR is set
+ - AUDIT_SANDBOX_KEYS, AUDIT_HOST_KEYS, AUDIT_EXTRA_KEYS arrays exist and are populated
+ - `nix build` succeeds (shellcheck passes)
+
+ Env audit display function exists with grouped sections, PATH splitting, value masking, and ANSI formatting. Display data is tracked in parallel arrays alongside ENV_ARGS.
+
+
+
+ Task 2: Add confirmation prompt with TTY detection and wire audit into launch flow
+ claudebox.sh
+ claudebox.sh
+
+Wire the audit display and confirmation prompt into the launch flow. This implements D-05, D-06, UX-02.
+
+Insert the following block AFTER print_audit is defined and BEFORE the --dry-run check (or before `exec bwrap` if dry-run check is already there). The audit+prompt should run before dry-run because dry-run implies --yes.
+
+Actually, looking at the flow: --dry-run should skip the audit (per research recommendation). So the order should be:
+
+1. If DRY_RUN is true, skip audit (it implies --yes)
+2. If SKIP_AUDIT is NOT true and NOT DRY_RUN, show audit and prompt
+
+Add this block after the env construction and print_audit function definition, before the dry-run check:
+
+```bash
+# Env audit and confirmation (D-05, D-06, D-07, UX-01, UX-02, UX-03)
+if [[ "$SKIP_AUDIT" != true && "$DRY_RUN" != true ]]; then
+ print_audit
+
+ # TTY check (D-06)
+ if [[ -t 0 ]]; then
+ read -r -p "Proceed? [Y/n] " response < /dev/tty 2>&1
+ response="${response,,}" # lowercase
+ if [[ "$response" == "n" || "$response" == "no" ]]; then
+ echo "Aborted." >&2
+ exit 1
+ fi
+ else
+ echo "${RED}Error: stdin is not a terminal. Pass --yes or -y to skip confirmation.${RESET}" >&2
+ exit 1
+ fi
+fi
+```
+
+Key details:
+- `Proceed? [Y/n]` -- default is proceed, Enter launches (D-05)
+- Only `n` or `no` aborts. Any other input (including empty/Enter) proceeds.
+- Non-TTY stdin aborts with actionable error message (D-06)
+- `read -r -p` with `< /dev/tty` to handle stdin being consumed by pipes
+- `${response,,}` lowercases the input for case-insensitive comparison
+- The `2>&1` on read sends the prompt text to wherever stdout goes (which combined with `< /dev/tty` reads from terminal)
+
+Wait -- the prompt from `read -p` goes to stderr by default in some shells but stdout in bash. To ensure the prompt goes to stderr per D-07, use:
+
+```bash
+echo -n "Proceed? [Y/n] " >&2
+read -r response < /dev/tty
+```
+
+This is cleaner and guarantees stderr for the prompt.
+
+
+ cd /home/toph/code/tools/claudebox && grep -q 'Proceed.*Y/n' claudebox.sh && grep -q '\-t 0' claudebox.sh && grep -q 'SKIP_AUDIT.*true' claudebox.sh && grep -q 'stdin is not a terminal' claudebox.sh && nix build 2>&1 && echo "PASS: confirmation prompt and nix build succeed"
+
+
+ - claudebox.sh contains `Proceed? [Y/n]` prompt text
+ - claudebox.sh checks `[[ -t 0 ]]` for TTY detection before prompting
+ - Non-TTY stdin prints error "stdin is not a terminal. Pass --yes or -y to skip confirmation." to stderr and exits 1
+ - The prompt and "Aborted." message go to stderr (>&2)
+ - Typing `n` or `no` (case-insensitive) at the prompt exits 1 with "Aborted."
+ - Pressing Enter (empty input) proceeds with launch
+ - The audit+prompt block is guarded by `SKIP_AUDIT != true && DRY_RUN != true`
+ - `nix build` succeeds (shellcheck passes, full script valid)
+ - Complete script flow: parse flags -> --check early exit -> resolve binaries -> build env -> audit+prompt (unless --yes/--dry-run) -> --dry-run print -> exec bwrap
+
+ Running `claudebox` shows env audit and prompts for confirmation. --yes/-y skips it. Non-TTY aborts with helpful error. nix build passes.
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| host env -> audit display | Env var values displayed to stderr, masking required for secrets |
+| user input -> read prompt | User response controls launch/abort decision |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Disposition | Mitigation Plan |
+|-----------|----------|-----------|-------------|-----------------|
+| T-02-03 | Information Disclosure | Env audit displaying ANTHROPIC_API_KEY | mitigate | mask_value() auto-masks any var name matching *KEY*, *TOKEN*, *SECRET*, *PASSWORD*, *CREDENTIAL*. Shows first 7 + last 4 chars only. Values <= 11 chars show `***`. |
+| T-02-04 | Information Disclosure | CLAUDEBOX_EXTRA_ENV secrets | mitigate | Same mask_value() applies to all displayed vars regardless of source category. User-added vars with sensitive names are masked. |
+| T-02-05 | Elevation of Privilege | Non-interactive auto-proceed | mitigate | D-06: non-TTY stdin aborts with error, never auto-proceeds. Scripts/CI must explicitly pass --yes. |
+
+
+
+1. `nix build` succeeds (shellcheck + compilation)
+2. `grep -c 'mask_value' claudebox.sh` returns >= 2 (definition + usage)
+3. `grep 'Proceed' claudebox.sh` shows the prompt text
+4. `grep 'SKIP_AUDIT' claudebox.sh` shows the guard condition
+5. Script flow order: flag parsing -> --check -> binary resolution -> env construction -> audit arrays -> audit+prompt -> --dry-run -> exec bwrap
+
+
+
+- Env audit displays three grouped sections with colored headers to stderr
+- PATH entries displayed one per line, indented
+- Sensitive values auto-masked (ANTHROPIC_API_KEY shows `sk-ant-...xxxx`)
+- Proceed? [Y/n] prompt with Enter=proceed, n=abort
+- Non-interactive stdin aborts with actionable error
+- --yes/-y skips entire audit+prompt
+- --dry-run also skips audit+prompt
+- nix build passes (shellcheck clean)
+
+
+