const WIZARD_TEMPLATE: &str = "#!/usr/bin/env bash\n#\n# A wizard walks a human through a manual procedure, step by step.\n# Generated by the wizard skill.\n#\n# The agent runs this in the seat\'s own terminal and hands the keyboard over\n# for hidden input; a person with no agent can still run it themselves.\n#\n# Everything above the \"STAGES\" marker is the wizard library: do not hand-edit\n# it. Author the per-step stages below the marker.\n\nset -euo pipefail\n\n# \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n# Wizard library: delightful, consistent UX, identical across every wizard.\n# \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n\n# **Every capability is probed, never assumed.** `tput dim` exits non-zero on\n# a TERM without it (ansi, rxvt) and `tput clear` on a TERM it does not know;\n# under `set -e` either would kill the wizard before its first line.\nif [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then\n BOLD=$(tput bold 2>/dev/null || true)\n DIM=$(tput dim 2>/dev/null || true)\n RESET=$(tput sgr0 2>/dev/null || true)\n BLUE=$(tput setaf 4 2>/dev/null || true)\n GREEN=$(tput setaf 2 2>/dev/null || true)\n YELLOW=$(tput setaf 3 2>/dev/null || true)\n CLEAR=$(tput clear 2>/dev/null || true)\nelse\n BOLD=\"\"; DIM=\"\"; RESET=\"\"; BLUE=\"\"; GREEN=\"\"; YELLOW=\"\"; CLEAR=\"\"\nfi\n\n# Author sets this at the top of the stages section.\nTOTAL_STAGES=0\n\n_STAGE_INDEX=0\nENV_FILE=\"${ENV_FILE:-.env}\"\nWRITTEN_ENV=() # KEYs written to ENV_FILE this run\nWRITTEN_SECRET=() # secret NAMEs set this run\n_ENV_TMP=\"\" # the temp a write_env is staging\n\n_cleanup() {\n if [[ -n \"$_ENV_TMP\" && -f \"$_ENV_TMP\" ]]; then rm -f \"$_ENV_TMP\"; fi\n return 0\n}\ntrap _cleanup EXIT\n\n# _clear wipes the terminal so only the current step is on screen. No-op when\n# output isn\'t a terminal, so piped logs stay readable.\n_clear() {\n [[ -t 1 ]] || return 0\n if [[ -n \"$CLEAR\" ]]; then printf \'%s\' \"$CLEAR\"; else printf \'\\033[2J\\033[3J\\033[H\'; fi\n}\n\n# banner \"Title\" shows the opening frame: what this wizard does.\nbanner() {\n _clear\n printf \'\\n%s%s %s%s\\n\' \"$BOLD\" \"$BLUE\" \"$1\" \"$RESET\"\n printf \'%s %s stages%s\\n\\n\' \"$DIM\" \"$TOTAL_STAGES\" \"$RESET\"\n printf \'%s This wizard tells you exactly what to do and captures the values you\\n\' \"$DIM\"\n printf \' copy back. Stop any time with Ctrl-C and re-run later, since it remembers\\n\'\n printf \' values already saved.%s\\n\' \"$RESET\"\n pause \"Ready to start?\"\n}\n\n# stage \"Name\" clears the screen, then announces a stage and shows progress.\n# Clearing keeps only the current step on screen.\nstage() {\n _clear\n _STAGE_INDEX=$((_STAGE_INDEX + 1))\n printf \'\\n%s%s\u{25b8} Stage %s/%s \u{b7} %s%s\\n\' \\\n \"$BOLD\" \"$BLUE\" \"$_STAGE_INDEX\" \"$TOTAL_STAGES\" \"$1\" \"$RESET\"\n}\n\n# say \"...\" prints a plain instruction line.\nsay() { printf \' %s\\n\' \"$1\"; }\n# step \"...\" is a numbered-feeling action the human takes in the browser.\nstep() { printf \' %s\u{2022}%s %s\\n\' \"$BLUE\" \"$RESET\" \"$1\"; }\nnote() { printf \' %s%s%s\\n\' \"$DIM\" \"$1\" \"$RESET\"; }\nwarn() { printf \' %s\u{26a0} %s%s\\n\' \"$YELLOW\" \"$1\" \"$RESET\"; }\n\n# _stop MESSAGE prints a failure and ends the run. A CI value has no other\n# home, so a failed write stops the wizard rather than recording a name whose\n# value is nowhere; a re-run asks for it again.\n_stop() {\n printf \' %s\u{2717} %s%s\\n\' \"$YELLOW\" \"$1\" \"$RESET\"\n exit 1\n}\n\n# _valid_key KEY aborts unless KEY is a shell-assignment-shaped name. **It is\n# never interpolated into a pattern**: as a regex, `A.B` would match a\n# different line and `A[` would empty the file.\n_valid_key() {\n [[ \"$1\" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] && return 0\n _stop \"$1 is not a valid environment variable name: use letters, digits and _, not starting with a digit\"\n}\n\n# _no_newline KEY VALUE aborts when VALUE spans lines: an env file holds one\n# line per key.\n_no_newline() {\n [[ \"$2\" != *$\'\\n\'* ]] && return 0\n _stop \"$1 contains a newline, which $ENV_FILE cannot hold: put a multi-line value in the file its program reads, or in the vault\"\n}\n\n# open_url URL opens it in the human\'s browser, cross-platform incl. WSL.\n# This is the fallback for when no browser is available: an agent driving the\n# wizard opens the page in the seat\'s browser and hands the tab over instead.\nopen_url() {\n local url=\"$1\" opener=\"\"\n printf \' %s\u{2197} opening%s %s\\n\' \"$GREEN\" \"$RESET\" \"$url\"\n if command -v wslview >/dev/null 2>&1; then opener=\"wslview\"\n elif command -v explorer.exe >/dev/null 2>&1; then opener=\"explorer.exe\"\n elif command -v xdg-open >/dev/null 2>&1; then opener=\"xdg-open\"\n elif command -v open >/dev/null 2>&1; then opener=\"open\"\n fi\n if [[ -z \"$opener\" ]]; then\n warn \"couldn\'t open a browser; visit it manually: $url\"\n return 0\n fi\n # **The warning is emitted outside the redirection.** Redirecting the\n # whole block would swallow it along with the opener\'s own noise.\n \"$opener\" \"$url\" >/dev/null 2>&1 || warn \"couldn\'t open a browser, so visit it manually: $url\"\n}\n\n# pause \"msg\" waits for the human to confirm they\'ve done the manual part.\npause() {\n printf \' %s%s%s \' \"$DIM\" \"${1:-Press Enter to continue}\" \"$RESET\"\n IFS= read -r _ || true\n}\n\n# confirm \"question\" is a y/N gate; returns success on yes. **A bare call\n# ends the wizard under `set -e` when the answer is no**, so this says so and\n# the skill shows the if/else idiom.\nconfirm() {\n local reply=\"\"\n printf \' %s? %s [y/N] \' \"$YELLOW\" \"$1\"\n IFS= read -r reply || true\n if [[ \"$reply\" =~ ^[Yy] ]]; then return 0; fi\n printf \' %s\u{b7} answered no: %s - nothing was changed%s\\n\' \"$DIM\" \"$1\" \"$RESET\"\n return 1\n}\n\n# _existing KEY: current value of KEY in ENV_FILE. Returns 0 and prints the\n# value, 1 when there is no such line, 2 when the file could not be read.\n# **It never calls `_stop` itself**: ask/ask_secret run it in a command\n# substitution, where an exit only ends the subshell and its message would be\n# captured as the value. The caller turns 2 into the wizard\'s own message.\n_existing() {\n [[ -f \"$ENV_FILE\" ]] || return 1\n local key=\"$1\" line status=0\n # awk\'s own error is 2; 1 is this program\'s \"no such line\", the only exit\n # that means a normal miss.\n line=$(awk -v key=\"$key\" \\\n \'index($0, key \"=\") == 1 { found = $0 } END { if (found != \"\") { print found; exit 0 } exit 1 }\' \\\n \"$ENV_FILE\") || status=$?\n if (( status == 1 )); then return 1; fi\n if (( status != 0 )); then return 2; fi\n printf \'%s\' \"${line#*=}\"\n}\n\n# ask KEY \"Prompt\" reads a value into $KEY. Offers the existing .env value as\n# a default on re-runs (Enter keeps it). Visible input (non-secret).\nask() {\n local key=\"$1\" prompt=\"$2\" current status=0 input\n _valid_key \"$key\"\n current=$(_existing \"$key\") || status=$?\n if (( status > 1 )); then\n _stop \"could not read $ENV_FILE; check that it is readable, then re-run\"\n fi\n if [[ -n \"$current\" ]]; then\n printf \' %s%s%s %s[Enter keeps current]%s \' \"$BOLD\" \"$prompt\" \"$RESET\" \"$DIM\" \"$RESET\"\n else\n printf \' %s%s%s \' \"$BOLD\" \"$prompt\" \"$RESET\"\n fi\n IFS= read -r input || true\n [[ -z \"$input\" && -n \"$current\" ]] && input=\"$current\"\n printf -v \"$key\" \'%s\' \"$input\"\n}\n\n# ask_secret KEY \"Prompt\" is like ask, but input is hidden.\nask_secret() {\n local key=\"$1\" prompt=\"$2\" current status=0 input\n _valid_key \"$key\"\n current=$(_existing \"$key\") || status=$?\n if (( status > 1 )); then\n _stop \"could not read $ENV_FILE; check that it is readable, then re-run\"\n fi\n if [[ -n \"$current\" ]]; then\n printf \' %s%s%s %s[Enter keeps current]%s \' \"$BOLD\" \"$prompt\" \"$RESET\" \"$DIM\" \"$RESET\"\n else\n printf \' %s%s%s \' \"$BOLD\" \"$prompt\" \"$RESET\"\n fi\n IFS= read -rs input || true\n printf \'\\n\'\n [[ -z \"$input\" && -n \"$current\" ]] && input=\"$current\"\n printf -v \"$key\" \'%s\' \"$input\"\n}\n\n# write_env KEY VALUE upserts KEY=VALUE into ENV_FILE (creates it; replaces\n# any existing line). Idempotent. **The temp sits beside ENV_FILE**, so the\n# rename is atomic and a secret never lands in $TMPDIR.\nwrite_env() {\n local key=\"$1\" value=\"$2\" status=0\n _valid_key \"$key\"\n _no_newline \"$key\" \"$value\"\n touch \"$ENV_FILE\" || _stop \"could not create $ENV_FILE\"\n _ENV_TMP=$(mktemp \"${ENV_FILE}.XXXXXX\") \\\n || _stop \"could not create a temp file beside $ENV_FILE\"\n awk -v key=\"$key\" \'index($0, key \"=\") != 1\' \"$ENV_FILE\" > \"$_ENV_TMP\" || status=$?\n if (( status != 0 )); then\n rm -f \"$_ENV_TMP\"; _ENV_TMP=\"\"\n _stop \"could not read $ENV_FILE (awk exit $status); nothing was written\"\n fi\n if ! printf \'%s=%s\\n\' \"$key\" \"$value\" >> \"$_ENV_TMP\"; then\n rm -f \"$_ENV_TMP\"; _ENV_TMP=\"\"\n _stop \"could not write the new value for $key; nothing was changed\"\n fi\n if ! mv \"$_ENV_TMP\" \"$ENV_FILE\"; then\n rm -f \"$_ENV_TMP\"; _ENV_TMP=\"\"\n _stop \"could not replace $ENV_FILE with the new value for $key\"\n fi\n _ENV_TMP=\"\"\n if [[ \" ${WRITTEN_ENV[*]:-} \" != *\" $key \"* ]]; then WRITTEN_ENV+=(\"$key\"); fi\n printf \' %s\u{2713} wrote%s %s \u{2192} %s\\n\' \"$GREEN\" \"$RESET\" \"$key\" \"$ENV_FILE\"\n}\n\n# set_secret NAME VALUE sets a GitHub Actions repo secret via gh. **A CI-only\n# value has no other home**, so a failure stops the stage instead of leaving\n# a name whose value is nowhere; nothing recorded means a re-run asks again.\nset_secret() {\n local name=\"$1\" value=\"$2\"\n command -v gh >/dev/null 2>&1 \\\n || _stop \"GitHub secret $name needs the gh CLI, which is not installed: install gh and re-run\"\n gh auth status >/dev/null 2>&1 \\\n || _stop \"GitHub secret $name needs gh signed in: run gh auth login, then re-run\"\n printf \'%s\' \"$value\" | gh secret set \"$name\" >/dev/null 2>&1 \\\n || _stop \"GitHub secret $name was not set: the gh call failed. Nothing was recorded, so a re-run asks for it again\"\n WRITTEN_SECRET+=(\"$name\")\n printf \' %s\u{2713} set%s GitHub secret %s\\n\' \"$GREEN\" \"$RESET\" \"$name\"\n}\n\n# set_var NAME VALUE sets a GitHub Actions repo variable (non-secret).\nset_var() {\n local name=\"$1\" value=\"$2\"\n command -v gh >/dev/null 2>&1 \\\n || _stop \"GitHub variable $name needs the gh CLI, which is not installed: install gh and re-run\"\n gh auth status >/dev/null 2>&1 \\\n || _stop \"GitHub variable $name needs gh signed in: run gh auth login, then re-run\"\n gh variable set \"$name\" --body \"$value\" >/dev/null 2>&1 \\\n || _stop \"GitHub variable $name was not set: the gh call failed. Nothing was recorded, so a re-run asks for it again\"\n printf \' %s\u{2713} set%s GitHub variable %s\\n\' \"$GREEN\" \"$RESET\" \"$name\"\n}\n\n# finish clears, then shows a closing summary of everything configured.\nfinish() {\n _clear\n printf \'\\n%s%s \u{2713} Setup complete%s\\n\' \"$BOLD\" \"$GREEN\" \"$RESET\"\n (( ${#WRITTEN_ENV[@]} )) && note \"wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}\"\n (( ${#WRITTEN_SECRET[@]} )) && note \"set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}\"\n printf \'\\n\'\n}\n\n# \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n# STAGES: author this section. One stage() per step the human takes.\n# Replace the example below. Set TOTAL_STAGES to match the stages you write.\n# ENV_FILE is relative: run the wizard from the repo root it should land in.\n# \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n\nTOTAL_STAGES=1\n\nbanner \"Stripe setup\"\n\n# \u{2500}\u{2500} Example stage: replace with your real steps \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\nstage \"Stripe: API keys\"\nsay \"We\'ll grab your Stripe test keys and store them for local dev + CI.\"\n# The agent opens this page in the seat\'s browser and hands the tab over;\n# open_url is the fallback for when no browser is available.\nopen_url \"https://dashboard.stripe.com/test/apikeys\"\nstep \"On the API keys page, copy the Publishable key (starts pk_test_).\"\nask STRIPE_PUBLISHABLE_KEY \"Paste the publishable key:\"\nstep \"Click \'Reveal test key\' on the Secret key row, then copy it.\"\nask_secret STRIPE_SECRET_KEY \"Paste the secret key:\"\nwrite_env STRIPE_PUBLISHABLE_KEY \"$STRIPE_PUBLISHABLE_KEY\"\nwrite_env STRIPE_SECRET_KEY \"$STRIPE_SECRET_KEY\"\nset_secret STRIPE_SECRET_KEY \"$STRIPE_SECRET_KEY\" # CI needs this one\n# \u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n\nfinish\n";