@ramarivera/coding-buddy 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramarivera/coding-buddy",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Persistent coding companion for Claude Code, Pi, and Oh My Pi",
5
5
  "type": "module",
6
6
  "omp": {
package/server/state.ts CHANGED
@@ -454,6 +454,10 @@ export interface StatusState {
454
454
  reaction: string;
455
455
  muted: boolean;
456
456
  achievement: string;
457
+ /** Epoch ms the achievement was awarded; 0 when none. The statusline expires
458
+ * the trophy banner on this, otherwise it latches into the shared
459
+ * status.json and pins every session's bubble to the last unlock. */
460
+ achievementAt: number;
457
461
  frames: string[];
458
462
  compactFrames: string[];
459
463
  minimalFrames: string[];
@@ -543,6 +547,7 @@ export function writeStatusState(
543
547
  reaction: "",
544
548
  muted: muted ?? false,
545
549
  achievement: achievement ?? "",
550
+ achievementAt: achievement ? Date.now() : 0,
546
551
  frames,
547
552
  compactFrames,
548
553
  minimalFrames,
@@ -58,6 +58,9 @@ STARS=$(jq -r '.stars // ""' "$STATE" 2>/dev/null)
58
58
  SHINY=$(jq -r '.shiny // false' "$STATE" 2>/dev/null)
59
59
  REACTION_FILE="$BUDDY_STATE_DIR/reaction.$SID.json"
60
60
  ACHIEVEMENT=$(jq -r '.achievement // ""' "$STATE" 2>/dev/null)
61
+ # "absent" distinguishes a legacy status.json (no field at all) from an explicit
62
+ # 0, which means "no achievement pending" and must not render.
63
+ ACHIEVEMENT_AT=$(jq -r 'if has("achievementAt") then (.achievementAt // 0) else "absent" end' "$STATE" 2>/dev/null)
61
64
  LEVEL=$(jq -r '.level // 1' "$STATE" 2>/dev/null)
62
65
  MOOD=$(jq -r '.mood // "focused"' "$STATE" 2>/dev/null)
63
66
 
@@ -229,10 +232,10 @@ DETECTED_COLS="$COLS"
229
232
  DETECTED_ROWS="$ROWS"
230
233
 
231
234
  # ─── Reaction bubble (with TTL check) ────────────────────────────────────────
235
+ # The achievement banner is resolved further down, once REACTION_TTL is known —
236
+ # it expires on the same clock as a reaction. Without that it latches into the
237
+ # shared status.json and pins every session's bubble to the last trophy.
232
238
  BUBBLE=""
233
- if [ -n "$ACHIEVEMENT" ] && [ "$ACHIEVEMENT" != "null" ] && [ "$ACHIEVEMENT" != "" ]; then
234
- BUBBLE=$'\xf0\x9f\x8f\x86'" $ACHIEVEMENT"
235
- fi
236
239
  REACTION_TTL=900
237
240
  INNER_W=44
238
241
  MARGIN=8
@@ -307,6 +310,31 @@ _sweep_expired_reactions() {
307
310
  }
308
311
 
309
312
  _sweep_expired_reactions
313
+
314
+ # Achievement banner: shown only while fresh. ACHIEVEMENT_AT is epoch ms, written
315
+ # alongside the name by writeStatusState. A legacy status.json without the field
316
+ # (pre-upgrade, or a snapshot fixture) is treated as fresh — the next write from
317
+ # the server backfills it.
318
+ #
319
+ # Validity and age are separate gates on purpose. A zeroed or malformed
320
+ # achievementAt means "nothing pending" and must never render, including under
321
+ # reactionTTL=0 — that opt-out disables *expiry*, not the field's meaning.
322
+ if [ -n "$ACHIEVEMENT" ] && [ "$ACHIEVEMENT" != "null" ]; then
323
+ ACH_FRESH=1
324
+ if [ "$ACHIEVEMENT_AT" != "absent" ]; then
325
+ case "$ACHIEVEMENT_AT" in
326
+ ''|0|*[!0-9]*) ACH_FRESH=0 ;;
327
+ *)
328
+ if [ "$REACTION_TTL" -gt 0 ] 2>/dev/null; then
329
+ ACH_AGE=$(( ($(date +%s) * 1000 - ACHIEVEMENT_AT) / 1000 ))
330
+ [ "$ACH_AGE" -ge "$REACTION_TTL" ] && ACH_FRESH=0
331
+ fi
332
+ ;;
333
+ esac
334
+ fi
335
+ [ "$ACH_FRESH" -eq 1 ] && BUBBLE=$'\xf0\x9f\x8f\x86'" $ACHIEVEMENT"
336
+ fi
337
+
310
338
  REACTION=$(jq -r '.reaction // ""' "$REACTION_FILE" 2>/dev/null)
311
339
  if [ -n "$REACTION" ] && [ "$REACTION" != "null" ] && [ "$REACTION" != "" ]; then
312
340
  FRESH=0