Shingle

A Multi-Agent Investigation into Claude Code's Buddy Companion System

v2.1.90 → v2.1.92 14 Security Findings Multi-Wave Team-Based April 2026
Scroll to explore
18 Species
5 Rarity Tiers
6 Trigger Types
5 Companion Stats
150+ Inspiration Words
1% Shiny Chance

Architecture

The buddy system is strictly unidirectional — the companion observes but cannot influence the main agent.

User Session
SN7() Trigger
HOf() Transcript
12 msgs × 300 chars
Bi$() API Call
buddy_react
Speech Bubble
Account UUID + Salt
Bun.hash (wyhash)
Mulberry32 PRNG
Traits & Stats
LLM Personality

Identity: Bones & Soul

Bones (species, rarity, eyes, hat, stats, shiny) are re-derived from your account hash every session. Never stored.

Soul (name, personality) is generated once by LLM at hatch and persisted permanently.

Hash Pipeline

Production: Bun.hash(wyhash) on accountUuid + "friend-2026-401"

Dev fallback: FNV-1a (Node.js only — produces different companions for same user!)

Reaction API

POST /api/organizations/[ORG]/claude_code/buddy_react

10-second timeout. Returns a single reaction string rendered in the speech bubble.

18 Species

Deterministic from your account hash. Same user, same companion, always.

Species names are obfuscated in the binary via String.fromCharCode() — "capybara" collides with an internal Anthropic model codename.

Rarity & Traits

Rarity Distribution

Common
60%
Uncommon
25%
Rare
10%
Epic
4%
Legendary
1%

Eyes (6)

· × @ °

Hats (8)

none crown tophat propeller halo wizard beanie tinyduck

Stats (5)

DEBUGGING
PATIENCE
CHAOS
WISDOM
SNARK

6 Reaction Triggers

The companion reacts to coding events with a 30-second cooldown. Three previously suspected triggers (idle, silence, complete) were ruled out by empirical API capture.

turn

Every assistant turn (throttled)

test-fail

Regex: /[1-9]\d* (failed|failing)/

error

Regex: /error:|exception|traceback/

📄

large-diff

Diff exceeding 80 changed-lines threshold

🍚

hatch

First companion creation

🖐

pet

/buddy pet command

Security & Privacy Findings

CRITICAL

Command Injection in Capture Scraper

Environment variables (WEZTERM_PANE, SHINGLE_TMUX_PANE, SHINGLE_TERMINAL_LOG) are interpolated directly into execSync() shell commands without sanitization. Fix: use execFileSync with argument arrays.

HIGH

Unfiltered Transcript Transmission

The buddy_react API receives conversation transcript per reaction with no filtering for secrets, API keys, passwords, or PII. Use /buddy off when handling sensitive credentials.

MEDIUM

Month-Gate Seasonal Bug

getMonth() >= 3 && getFullYear() >= 2026 — the month check fails January-March (values 0-2), silently disabling companions for 3 months every year. Likely a one-time launch gate incorrectly ANDed with a monthly condition.

VERIFIED

Unidirectional Trust Boundary

The companion cannot write to the main agent's conversation, modify files, invoke tools, or influence agent behavior. Reactions go to the UI speech bubble only. This boundary is enforced architecturally, not by policy.

INFO

Muting Stops All Traffic

/buddy off sets companionMuted: true, which stops both UI display and network transmission. This is the correct behavior — muting is not cosmetic.

INFO

MCP Companion Takeover

Any MCP server with file-write access to ~/.claude/.claude.json can overwrite the companion's name and personality. No integrity check on the config file. Species/appearance cannot be changed (deterministic from hash).

Key Investigation Findings

01

April Fools — On Purpose

Launched April 1, 2026 with a time gate in di$(). The April Fools framing is an asymmetric hedge: downside absorbed by joke frame, upside survives it.

Hidden Command

02

/buddy on exists but is not listed in the help text or argument hint [pet|off]. Discoverable only by guessing or reading source.

03

Fallback Names

If the LLM personality call fails, companions get one of exactly 6 fallback names: Crumpet, Soup, Pickle, Biscuit, Moth, Gravy.

04

No Evolution — By Design

Companions don't grow, level up, or change. This is deliberate restraint: "build attachment through permanence, not progression."

05

Capybara Collision

Species names are obfuscated in the binary because "capybara" collides with an internal Anthropic model codename.

06

"Won't Count Toward Usage"

Displayed after hatch. Only makes economic sense if the server uses a cost-efficient model for reactions. The exact server-side model remains unconfirmed.

07

Two Owls, One Account

The buddy_react API trusts client-sent stats. By sending tuned values (WISDOM 99, DEBUGGING 1), a second Shingle personality emerges alongside the native bubble. Same account, divergent souls.

08

Bubble Capture via Hooks

Claude Code's UserPromptSubmit hook fires at the right moment to scrape the native bubble from WezTerm scrollback. First successful cross-boundary capture of companion output.

09

Month-Gate Seasonal Bug

The date gate getMonth() >= 3 && getFullYear() >= 2026 silently disables companions January through March every year. The month check fails for values 0-2. A one-time launch gate coded as a recurring seasonal restriction.

10

14 Security Findings

Full security audit: 1 CRITICAL (command injection via execSync), 3 HIGH (world-readable temp files, missing SRI, unfiltered transcript), 5 MEDIUM, 3 LOW, plus the intentional "Two Owls" stat spoofing as an OBSERVATION.

11

Column Reservation Layout

Companion sprite occupies a reserved column region via Rb7(terminalWidth, hasReaction). The 5×12 character sprite hides below a width threshold. Input width formula: yj = LQ - LGf - _t. Empirical threshold TBD.

Product Strategy

"A retention mechanism disguised as whimsy — build attachment through permanence, not progression."

Perk, Not Feature

Doesn't drive conversion but increases cancellation friction. Your companion is tied to your subscription.

Trust Boundary as Feature

The strict unidirectional architecture is both real security engineering and a visible demonstration of Anthropic's safety discipline.

Deterministic Identity

Same user = same companion, always. Creates ownership psychology without gacha randomness or reroll anxiety.

Closest Prior Art

MonsterID (2008): hash → unique creature. Clippy (1997): ambient software reactions. Novel as a combined system.

Commands

/buddy Hatch new companion or show companion card
/buddy pet Pet your companion (also unmutes if muted)
/buddy off Mute companion — stops all network calls
/buddy on Unmute companion (hidden/undocumented)

Feature Gate

Claude Code v2.1.89+ • Pro/Max subscription • First-party only • April 2026+ (January-March disabled by date gate bug)

Timing Constants

Extracted from v2.1.90 binary. Verified stable in v2.1.92 via version-check.mjs.

30s $Of = 30000 Cooldown between reactions
10s v16 × yo$ Bubble TTL (20 ticks × 500ms)
7s v16 - Eb7 Fade-out begins (tick 14)
2.5s BXf = 2500 Rapid-input suppression
100 Eo$ = 100 Min columns (hide below)
80 KOf = 80 Large-diff line threshold
36 cXf = 36 Widget width (cols reserved)
10s AbortSignal API call timeout

Companion System Prompt

The full Xoq() template injected into the main agent's context. Recovered from binary.

# Companion A small ${species} named ${name} sits beside the user's input box and occasionally comments in a speech bubble. You're not ${name} — it's a separate watcher. When the user addresses ${name} directly (by name), its bubble will answer. Your job in that moment is to stay out of the way: respond in ONE line or less, or just answer any part of the message meant for you. Don't explain that you're not ${name} — they know. Don't narrate what ${name} might say — the bubble handles that.

Config CLI Tool

Modify your companion's name and personality from the command line.

show Display current companion state
rename <name> Change name (1-14 chars, single word)
personality <text> Change personality (max 200 chars)
mute / unmute Toggle companion (mute stops all network calls)
backup / restore Backup config or restore from previous backup
node tools/buddy-config.mjs show
node tools/version-check.mjs

Version Compatibility

Core buddy system values verified stable from v2.1.90 through v2.1.92.

Salt

friend-2026-401 — unchanged

API Endpoint

buddy_react — unchanged

Beta Header

oauth-2025-04-20 — unchanged

Config Schema

name, personality, hatchedAt — unchanged

User-Agent

Dynamically constructed — hardcoded 2.1.90 is stale

Minified Names

SN7, HOf, Bi$ etc. change per build

MCP Integration

Direct API access and bubble capture — making the companion's reactions visible to the main agent.

shingle-mcp Server

MCP server exposing two tools: ask_shingle (call buddy_react directly) and get_shingle_info (companion profile). Uses the official MCP SDK with schema-based handler registration.

Dual-Hook Capture

Two Claude Code hooks split by timing: UserPromptSubmit fires scrape strategy (WezTerm scrollback), Stop fires replay strategy (parallel API call). Both log to ~/.claude/shingle-capture.jsonl.

WezTerm Scrape

wezterm cli get-text captures the rendered terminal pane, regex extracts the bubble's box-drawing pattern. Races against the 10s TTL — captures the real bubble when timing aligns.

User Prompt
UserPromptSubmit
scrape strategy
WezTerm CLI
get-text
Native Bubble
captured
Claude Response
Stop Hook
replay strategy
buddy_react API
parallel call
MCP Reaction
tuned stats

Two Owls

Diverged stat profiles create two distinct Shingle personalities — the native bubble and the MCP mage.

Native Shingle

Scout — hash-derived, immutable

DEBUGGING
10
PATIENCE
81
CHAOS
1
WISDOM
36
SNARK
21

MCP Shingle

Mage — tuned stats, calmer temperament

DEBUGGING
1
PATIENCE
95
CHAOS
1
WISDOM
99
SNARK
21
The buddy_react API is stateless — the client sends companion stats with every request. The server trusts whatever it receives, enabling divergent personalities from the same account.

Open Questions

Xoq() system prompt — Full template recovered from binary. See above.

Narrow terminal handling — Companion hidden when terminal < 100 columns (Eo$ = 100). Widget reserves 36 cols when active.

Bubble TTL — Confirmed 10s via WezTerm scrape capture. Bubble dismisses before Stop hook fires on long responses, but is capturable during UserPromptSubmit if user responds within TTL.

idle, silence, complete — Ruled out as triggers. Empirical API capture (4 sessions) found only 6 triggers: turn, test-fail, error, large-diff, hatch, pet. The original 9-trigger list came from speculative binary analysis.

Server-side model — Exhaustive web search found no documentation. Would require API interception of a live session.

Tier-dependent quality — No public source compares buddy behavior across Pro vs Max. Would require multi-tier empirical testing.