buddymon/lib/statusline.sh
pyr0ball 457276e302 feat: language mascot system + script-first architecture (v0.2.0)
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
2026-04-10 01:31:51 -07:00

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