buddymon/hooks-handlers/session-stop.sh
pyr0ball 8e0a5f82cb feat: evolution system — prestige to evolved form at Lv.100
Evolution triggers at Lv.100 for all three starters:
  Pyrobyte → 🌋 Infernus  (power 40→70, catch_rate 0.45→0.55)
  Debuglin → 🔬 Verifex   (power 35→60, catch_rate 0.60→0.75)
  Minimox  → 🌑 Nullex    (power 35→55, catch_rate 0.50→0.65)

/buddymon evolve: checks eligibility, shows stat preview, resets buddy
to Lv.1 in evolved form, archives old form with evolved_into marker,
carries challenges forward.

session-stop.sh now prints EVOLUTION READY banner when level hits 100
or when already eligible at session end.
2026-04-02 23:08:32 -07:00

127 lines
4.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# Buddymon Stop hook — tally session XP, check challenge, print summary
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "$(realpath "$0")")")}"
source "${PLUGIN_ROOT}/lib/state.sh"
buddymon_init
ACTIVE_ID=$(buddymon_get_active)
SESSION_XP=$(buddymon_get_session_xp)
if [[ -z "${ACTIVE_ID}" ]] || [[ "${SESSION_XP}" -eq 0 ]]; then
exit 0
fi
# Load catalog for display info
CATALOG="${PLUGIN_ROOT}/lib/catalog.json"
SUMMARY=$(python3 << PYEOF
import json, os
catalog_file = '${CATALOG}'
active_file = '${BUDDYMON_DIR}/active.json'
roster_file = '${BUDDYMON_DIR}/roster.json'
session_file = '${BUDDYMON_DIR}/session.json'
encounters_file = '${BUDDYMON_DIR}/encounters.json'
catalog = json.load(open(catalog_file))
active = json.load(open(active_file))
roster = json.load(open(roster_file))
session = json.load(open(session_file))
buddy_id = active.get('buddymon_id')
if not buddy_id:
print('')
exit()
b = (catalog.get('buddymon', {}).get(buddy_id)
or catalog.get('evolutions', {}).get(buddy_id) or {})
display = b.get('display', buddy_id)
xp_earned = active.get('session_xp', 0)
level = roster.get('owned', {}).get(buddy_id, {}).get('level', 1)
total_xp = roster.get('owned', {}).get(buddy_id, {}).get('xp', 0)
xp_needed = level * 100
# Check level up
leveled_up = False
new_level = level
while total_xp >= new_level * 100:
new_level += 1
leveled_up = True
if leveled_up:
# Save new level
roster['owned'][buddy_id]['level'] = new_level
json.dump(roster, open(roster_file, 'w'), indent=2)
# Session stats
commits = session.get('commits_this_session', 0)
tools = session.get('tools_used', 0)
langs = session.get('languages_seen', [])
challenge_completed = session.get('challenge_completed', False)
challenge = active.get('challenge')
lines = [f"\n## 🐾 Session complete — {display}"]
lines.append(f"**+{xp_earned} XP earned** this session")
if commits:
lines.append(f" · {commits} commit{'s' if commits != 1 else ''}")
if langs:
lines.append(f" · New languages: {', '.join(langs)}")
if leveled_up:
lines.append(f"\n✨ **LEVEL UP!** {display} is now Lv.{new_level}!")
# Check if evolution is now available
catalog_entry = (catalog.get('buddymon', {}).get(buddy_id)
or catalog.get('evolutions', {}).get(buddy_id) or {})
evolutions = catalog_entry.get('evolutions', [])
evo = next((e for e in evolutions if new_level >= e.get('level', 999)), None)
if evo:
into = catalog.get('evolutions', {}).get(evo['into'], {})
lines.append(f"\n⭐ **EVOLUTION READY!** {display} can evolve into {into.get('display', evo['into'])}!")
lines.append(f" Run `/buddymon evolve` to prestige — resets to Lv.1 with upgraded stats.")
else:
filled = min(20, total_xp * 20 // xp_needed)
bar = '█' * filled + '░' * (20 - filled)
lines.append(f"XP: [{bar}] {total_xp}/{xp_needed}")
# Remind if already at evolution threshold but hasn't evolved yet
catalog_entry = (catalog.get('buddymon', {}).get(buddy_id)
or catalog.get('evolutions', {}).get(buddy_id) or {})
evolutions = catalog_entry.get('evolutions', [])
evo = next((e for e in evolutions if level >= e.get('level', 999)), None)
if evo:
into = catalog.get('evolutions', {}).get(evo['into'], {})
lines.append(f"\n⭐ **Evolution available:** `/buddymon evolve` → {into.get('display', evo['into'])}")
if challenge:
if challenge_completed:
lines.append(f"\n🏆 **Challenge complete:** {challenge.get('name','?')} — bonus XP awarded!")
else:
lines.append(f"\n⏳ Challenge in progress: {challenge.get('name','?')}")
print('\n'.join(lines))
PYEOF
)
# Reset session XP + clear challenge so next session assigns a fresh one
python3 << PYEOF
import json
active_file = '${BUDDYMON_DIR}/active.json'
active = json.load(open(active_file))
active['session_xp'] = 0
active['challenge'] = None
json.dump(active, open(active_file, 'w'), indent=2)
PYEOF
# Reset session file
buddymon_session_reset
SUMMARY_JSON=$(python3 -c "import json,sys; print(json.dumps(sys.argv[1]))" "${SUMMARY}" 2>/dev/null)
[[ -z "${SUMMARY_JSON}" ]] && SUMMARY_JSON='""'
cat << EOF
{"systemMessage": ${SUMMARY_JSON}}
EOF
exit 0