Language mascots: - 11 mascots across common/uncommon/rare/legendary tiers (Pythia, Asynclet, Bashling, Goroutling, Typeling, Vueling, Querion, Ferrix, Perlius, Cobolithon, Lispling) - Full evolution chains for all mascots (16 evolutions total in catalog) - Spawn via PostToolUse after language affinity milestones; probability scales with affinity level; only fires with no active encounter - Passive strength reduction: each Write/Edit in the mascot's language ticks current_strength down (floor 5%, triggers re-announcement) - Mascot-aware catch formula: base_rate + affinity_bonus (6% per level) + weakness_bonus + soft element gating via existing player_elements - Language-themed weakening menu and catch failure messages in CLI - Caught mascots stored as type="caught_language_mascot"; assignable as buddy - UserPromptSubmit uses distinct 🦎 announcement with language context Roster display: - New "Language Mascots" section between core buddymon and caught bug monsters - Language affinity table marks languages with spawnable mascots (🦎) - Discovery counter now tracks both bug monsters and mascots separately Veritarch (third evolution): - Debuglin → Verifex (Lv.100) → Veritarch (Lv.200) - TYPE FORTRESS / INVARIANT PROOF / ZERO FLAKE challenges - xp_multiplier 1.7, catch_rate 0.90 Script-first architecture: - All game logic extracted to lib/cli.py (~850 lines); SKILL.md is now a ~55-line relay — 88% token reduction per invocation - CLI emits [INPUT_NEEDED] and [HAIKU_NEEDED] markers for interactive flows - PostToolUse hook re-emits CLI stdout as additionalContext for inline display Session XP fix: - statusline.sh and session state now read from sessions/<pgrp>.json (per-window) with fallback to active.json; fixes stale XP in statusline
46 lines
1.6 KiB
Bash
46 lines
1.6 KiB
Bash
#!/usr/bin/env bash
|
|
# Buddymon statusline — displays active buddy + encounter in the CC status bar.
|
|
# Install: add to ~/.claude/settings.json → "statusLine" → "command"
|
|
# Or run: /buddymon statusline (installs automatically)
|
|
|
|
B="$HOME/.claude/buddymon"
|
|
|
|
# Bail fast if no state directory or no starter chosen
|
|
[[ -d "$B" ]] || exit 0
|
|
STARTER=$(jq -r '.starter_chosen // false' "$B/roster.json" 2>/dev/null)
|
|
[[ "$STARTER" == "true" ]] || exit 0
|
|
|
|
# Read state
|
|
ID=$(jq -r '.buddymon_id // ""' "$B/active.json" 2>/dev/null)
|
|
[[ -n "$ID" ]] || exit 0
|
|
|
|
LVL=$(jq -r ".owned[\"$ID\"].level // 1" "$B/roster.json" 2>/dev/null)
|
|
|
|
# Per-session XP (accurate for multi-window setups); fall back to active.json
|
|
PGRP=$(python3 -c "import os; print(os.getpgrp())" 2>/dev/null)
|
|
SESSION_FILE="$B/sessions/${PGRP}.json"
|
|
if [[ -f "$SESSION_FILE" ]]; then
|
|
XP=$(jq -r '.session_xp // 0' "$SESSION_FILE" 2>/dev/null)
|
|
else
|
|
XP=$(jq -r '.session_xp // 0' "$B/active.json" 2>/dev/null)
|
|
fi
|
|
|
|
ENC_JSON=$(jq -c '.active_encounter // null' "$B/encounters.json" 2>/dev/null)
|
|
ENC_DISPLAY=$(echo "$ENC_JSON" | jq -r '.display // ""' 2>/dev/null)
|
|
ENC_STRENGTH=$(echo "$ENC_JSON" | jq -r '.current_strength // 0' 2>/dev/null)
|
|
|
|
# ANSI colors
|
|
CY='\033[38;2;23;146;153m' # cyan — buddy
|
|
GR='\033[38;2;64;160;43m' # green — xp
|
|
RD='\033[38;2;203;60;51m' # red — encounter
|
|
DM='\033[38;2;120;120;120m' # dim — separators
|
|
RS='\033[0m'
|
|
|
|
printf "${CY}🐾 ${ID} Lv.${LVL}${RS}"
|
|
printf " ${DM}·${RS} ${GR}+${XP}xp${RS}"
|
|
|
|
if [[ "$ENC_JSON" != "null" ]] && [[ -n "$ENC_DISPLAY" ]]; then
|
|
printf " ${RD}⚔ ${ENC_DISPLAY} [${ENC_STRENGTH}%%]${RS}"
|
|
fi
|
|
|
|
echo
|