update skills

This commit is contained in:
nessar committed 2026-09-25 10:00:00 +02:00
1 parent 9fb18f57ec
commit ff86cf48b4
82 files changed
+2156 -650

No files matched your search

+84 -63
View File
@@ -3,8 +3,8 @@
# A wizard — walks a human through a manual procedure step by step.
# Generated by the /wizard skill.
#
# Everything above the "STAGES" marker is the wizard library: do not hand-edit
# it. Author the per-step stages below the marker.
# Author the per-step stages below the "STAGES" marker. Adapt and test the
# persistence helpers when the target consumer needs a different format.
set -euo pipefail
@@ -19,14 +19,15 @@ else
BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
fi
# Author sets this at the top of the stages section.
# Author sets these two at the top of the stages section.
TOTAL_STAGES=0
TOTAL_MINUTES=0
_STAGE_INDEX=0
_MINUTES_ELAPSED=0
ENV_FILE="${ENV_FILE:-.env}"
WRITTEN_ENV=() # KEYs written to ENV_FILE this run
WRITTEN_SECRET=() # secret NAMEs set this run
SKIPPED=() # things we couldn't do (e.g. gh missing)
SKIPPED=() # manual steps still outstanding
# _clear — wipe the terminal so only the current step is on screen. No-op when
# output isn't a terminal, so piped logs stay readable.
@@ -35,24 +36,28 @@ _clear() {
if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
}
# banner "Title" — opening frame: what this wizard does.
# banner "Title" — opening frame: what this wizard does and how long it takes.
banner() {
_clear
printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
printf '%s %s stages%s\n\n' "$DIM" "$TOTAL_STAGES" "$RESET"
printf '%s %s stages · about %s minutes%s\n\n' \
"$DIM" "$TOTAL_STAGES" "$TOTAL_MINUTES" "$RESET"
printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
printf ' later — it remembers values already saved.%s\n' "$RESET"
pause "Ready to start?"
}
# stage "Name" — clear the screen, then announce a stage and show progress.
# Clearing keeps only the current step on screen.
# stage "Name" <minutes> — clear the screen, then announce a stage and show
# progress + time remaining. Clearing keeps only the current step on screen.
stage() {
_clear
_STAGE_INDEX=$((_STAGE_INDEX + 1))
printf '\n%s%s▸ Stage %s/%s · %s%s\n' \
"$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET"
local remaining=$((TOTAL_MINUTES - _MINUTES_ELAPSED))
(( remaining < 0 )) && remaining=0
_MINUTES_ELAPSED=$((_MINUTES_ELAPSED + ${2:-0}))
printf '\n%s%s▸ Stage %s/%s · %s%s %s(~%s min left)%s\n' \
"$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET" "$DIM" "$remaining" "$RESET"
}
# say "..." — a plain instruction line.
@@ -77,101 +82,114 @@ open_url() {
# pause "msg" — wait for the human to confirm they've done the manual part.
pause() {
printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
read -r _ || true
read -r _ || _cancel
}
_cancel() { printf '\nCancelled; no further steps were run.\n' >&2; exit 1; }
_valid_key() {
[[ "$1" =~ ^[A-Z_][A-Z0-9_]*$ && "$1" != HOME && "$1" != CODEX_HOME && "$1" != PATH && "$1" != ENV_FILE ]] || {
printf 'Invalid or reserved variable name.\n' >&2; return 1;
}
}
# confirm "question" — y/N gate; returns success on yes.
confirm() {
local reply=""
printf ' %s? %s [y/N] ' "$YELLOW" "$1"
read -r reply || true
[[ "$reply" =~ ^[Yy] ]]
read -r reply || _cancel
[[ "$reply" == y || "$reply" == Y || "$reply" == yes || "$reply" == YES ]]
}
# _existing KEY — current value of KEY in ENV_FILE, if any.
_existing() {
[[ -f "$ENV_FILE" ]] || return 1
local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
printf '%s' "${line#*=}"
[[ -e "$ENV_FILE" ]] || return 0
local line value=""
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*(export[[:space:]]+)?${1}[[:space:]]*= ]] || continue
value="${line#*=}"
# ponytail: only reuse the single-line format we emit; adapt for another dotenv dialect.
if [[ "$value" == \"* || "$value" == \`* || "$value" == *$'\r'* ]]; then
printf 'Existing value uses an unsupported dotenv format; adapt the helper first.\n' >&2; return 1
elif [[ "$value" == "'"* ]]; then
[[ "$value" == *"'" && ${#value} -ge 2 ]] || return 1
value="${value:1:${#value}-2}"
[[ "$value" != *"'"* ]] || return 1
else
[[ "$value" != *[[:space:]]* && "$value" != *[\'\"\`\\]* ]] || return 1
value="${value%%#*}"
fi
done < "$ENV_FILE"
printf '%s' "$value"
}
# ask KEY "Prompt" — read a value into $KEY. Offers the existing .env value as
# a default on re-runs (Enter keeps it). Visible input (non-secret).
ask() {
local key="$1" prompt="$2" current input
current=$(_existing "$key" || true)
_valid_key "$key" || return 1
current=$(_existing "$key") || return 1
if [[ -n "$current" ]]; then
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
else
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
fi
read -r input || true
read -r input || _cancel
[[ -z "$input" && -n "$current" ]] && input="$current"
[[ -n "$input" ]] || { printf 'A value is required.\n' >&2; return 1; }
printf -v "$key" '%s' "$input"
}
# ask_secret KEY "Prompt" — like ask, but input is hidden.
ask_secret() {
local key="$1" prompt="$2" current input
current=$(_existing "$key" || true)
_valid_key "$key" || return 1
current=$(_existing "$key") || return 1
if [[ -n "$current" ]]; then
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
else
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
fi
read -rs input || true
read -rs input || _cancel
printf '\n'
[[ -z "$input" && -n "$current" ]] && input="$current"
[[ -n "$input" ]] || { printf 'A value is required.\n' >&2; return 1; }
printf -v "$key" '%s' "$input"
}
# write_env KEY VALUE — upsert KEY=VALUE into ENV_FILE (creates it; replaces
# any existing line). Idempotent.
# write_env KEY VALUE — atomic, private single-line dotenv upsert.
# Single quotes preserve #, spaces, dollars and backslashes literally.
write_env() {
local key="$1" value="$2" tmp
touch "$ENV_FILE"
tmp=$(mktemp)
grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
printf '%s=%s\n' "$key" "$value" >> "$tmp"
mv "$tmp" "$ENV_FILE"
local key="$1" value="$2" tmp line
_valid_key "$key" || return 1
[[ -n "$value" && "$value" != *"'"* && "$value" != *$'\n'* && "$value" != *$'\r'* ]] || {
printf 'Cannot save an empty value, apostrophe, or newline in this dotenv format.\n' >&2; return 1;
}
_existing "$key" >/dev/null || return 1
tmp=$(mktemp "${ENV_FILE}.XXXXXX") || return 1
if [[ -e "$ENV_FILE" ]] && ! awk -v key="$key" '$0 !~ "^[[:space:]]*(export[[:space:]]+)?" key "[[:space:]]*="' "$ENV_FILE" > "$tmp"; then
rm -- "$tmp"
return 1
fi
if printf "%s='%s'\n" "$key" "$value" >> "$tmp" && mv -- "$tmp" "$ENV_FILE"; then
:
else
rm -- "$tmp"
printf 'Could not save dotenv value.\n' >&2; return 1
fi
WRITTEN_ENV+=("$key")
printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
}
# set_secret NAME VALUE — set a GitHub Actions repo secret via gh. Falls back
# to a warning (and records it) if gh is unavailable or unauthenticated.
set_secret() {
local name="$1" value="$2"
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
WRITTEN_SECRET+=("$name")
printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
return
fi
fi
SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
warn "skipped GitHub secret $name — gh not ready; set it later"
}
# set_var NAME VALUE — set a GitHub Actions repo variable (non-secret).
set_var() {
local name="$1" value="$2"
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
return
fi
fi
SKIPPED+=("GitHub variable $name")
warn "skipped GitHub variable $name — gh not ready; set it later"
}
# finish — clear, then a closing summary of everything configured.
finish() {
_clear
printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
if (( ${#SKIPPED[@]} )); then
warn 'Setup incomplete'
else
printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
fi
(( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
(( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
if (( ${#SKIPPED[@]} )); then
printf '\n'; warn "still to do by hand:"
for s in "${SKIPPED[@]}"; do note " - $s"; done
@@ -181,16 +199,20 @@ finish() {
# ──────────────────────────────────────────────────────────────────────────
# STAGES — author this section. One stage() per step the human takes.
# Replace the example below. Set TOTAL_STAGES to match the stages you write.
# Replace the example below. Set the two totals to match the stages you write.
# ──────────────────────────────────────────────────────────────────────────
# Sourcing exposes helpers for isolated checks without opening browsers.
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then return 0; fi
TOTAL_STAGES=1
TOTAL_MINUTES=5
banner "Stripe setup"
# ── Example stage: replace with your real steps ───────────────────────────
stage "Stripe — API keys"
say "We'll grab your Stripe test keys and store them for local dev + CI."
stage "Stripe — API keys" 5
say "We'll grab your Stripe test keys and store them for local development."
open_url "https://dashboard.stripe.com/test/apikeys"
step "On the API keys page, copy the Publishable key (starts pk_test_)."
ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"
@@ -198,7 +220,6 @@ step "Click 'Reveal test key' on the Secret key row, then copy it."
ask_secret STRIPE_SECRET_KEY "Paste the secret key:"
write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"
write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY" # CI needs this one
# ──────────────────────────────────────────────────────────────────────────
finish