How Claude Code Generates a Deterministic Terminal Buddy from Your UserID (18 Species, 5 Rarity Tiers)

The article dissects Claude Code's Buddy companion system, explaining how a deterministic PRNG seeded by a hashed userId creates a unique pet with 18 species and five rarity levels, detailing the skeleton‑soul separation, ASCII rendering, feature‑flagged time window, and prompt‑injected role boundaries.

James' Growth Diary
James' Growth Diary
James' Growth Diary
How Claude Code Generates a Deterministic Terminal Buddy from Your UserID (18 Species, 5 Rarity Tiers)

Source location

All Buddy logic resides in src/buddy/ across six files: companion.ts (deterministic engine), types.ts (type definitions), sprites.ts (ASCII frames), CompanionSprite.tsx (React renderer), prompt.ts (system‑prompt text), useBuddyNotification.tsx (feature‑flagged time window).

Deterministic PRNG

The engine uses Mulberry32 seeded with an FNV‑1a hash of userId + SALT. The comment “good enough for picking ducks” indicates it is not cryptographically secure. The hash‑seed‑PRNG chain yields a reproducible CompanionBones object.

function mulberry32(seed:number){
  let a = seed >>> 0;
  return function(){
    a |= 0;
    a = (a + 0x6d2b79f5) | 0;
    let t = Math.imul(a ^ (a >>> 15), 1 | a);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  }
}

function hashString(s:string):number{
  if(typeof Bun !== 'undefined'){
    return Number(BigInt(Bun.hash(s)) & 0xffffffffn);
  }
  let h = 2166136261;
  for(let i=0;i<s.length;i++){
    h ^= s.charCodeAt(i);
    h = Math.imul(h,16777619);
  }
  return h >>> 0;
}

const SALT='friend-2026-401';

export function roll(userId:string){
  const key = userId + SALT;
  if(rollCache?.key===key) return rollCache.value;
  const value = rollFrom(mulberry32(hashString(key)));
  rollCache = {key,value};
  return value;
}

Rarity system

Five tiers: common (~60 %), uncommon (~25 %), rare (~10 %), epic (~4 %), legendary (~1 %). Higher rarity raises attribute floor and enables optional hats. Attributes are generated with a “peak + valley” mechanism, giving each Buddy one strong and one weak stat among DEBUGGING, PATIENCE, CHAOS, WISDOM, SNARK.

Skeleton vs Soul separation

“Bones” (rarity, species, eye, hat, shiny, stats) are derived deterministically from the hashed userId and never persisted. “Soul” (name, personality) is generated by the LLM and stored in the config as StoredCompanion. getCompanion() merges stored soul with freshly computed bones, letting bones overwrite any stored bone fields.

export function getCompanion(){
  const stored = getGlobalConfig().companion; // only soul fields
  if(!stored) return undefined;
  const {bones} = roll(companionUserId()); // bones always from hash(userId)
  return {...stored,...bones}; // bones overwrite stored bone fields
}

ASCII sprite rendering

Sprites are defined for 18 species, each with 3 frames of 5 lines × 12 characters. Rendering replaces the {E} placeholder with one of six eye symbols, inserts a hat line when appropriate, and trims the top line only if every frame’s first line is empty, preserving animation consistency. The animation engine runs on a 500 ms timer with an IDLE_SEQUENCE that includes blink frames ( -1).

export function renderSprite(bones,frame=0){
  const frames = BODIES[bones.species];
  const body = frames[frame % frames.length]!
    .map(line=>line.replaceAll('{E}',bones.eye));
  const lines = [...body];
  if(bones.hat!=='none' && !lines[0].trim()){
    lines[0] = HAT_LINES[bones.hat];
  }
  if(!lines[0].trim() && frames.every(f=>!f[0].trim())) lines.shift();
  return lines;
}

Time‑window feature flag and prompt injection

The companion is gated by feature('BUDDY') and a local‑date window (April 1‑7 2026). Using local time creates a 24‑hour rolling exposure across time zones. The system prompt generated by companionIntroText defines a separate role for the Buddy and instructs the LLM to keep its replies to one line when addressed.

export function isBuddyTeaserWindow(){
  if('external'==='ant') return true; // always on internally
  const d = new Date();
  return d.getFullYear()===2026 && d.getMonth()===3 && d.getDate()<=7;
}

export function companionIntroText(name,species){
  return `# 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.
Don't explain that you're not ${name} — they know.`;
}

Obfuscated species names

Species strings are built from character‑code arrays (e.g., c(0x64,0x75,0x63,0x6b) for “duck”) to avoid detection by a canary scan that looks for literal strings in the bundled CLI. The array is cast to a literal type, which is stripped at compile time.

Design insights

Deterministic generation provides a stateless, user‑bound personalization without server storage.

Separating bones from soul prevents configuration‑file tampering.

Choosing a simple PRNG avoids over‑engineering.

System prompts can define multi‑role boundaries without complex agent‑to‑agent protocols.

Critique

Sharing the feature flag with production code may increase deployment complexity in enterprise environments.

Initial soul generation requires network access; offline usage yields an incomplete Buddy.

Fixed 12‑character width assumes monospaced fonts, causing misalignment on non‑Western or proportional fonts.

Practical reuse

Developers can adopt the three‑step deterministic role generation (hash → PRNG → bones) and the immutable‑mutable field pattern for their own CLI tools, reusing the Mulberry32 implementation and the feature‑flag gating logic.

Conclusion

Buddy occupies only six of the 512 K lines of Claude Code but showcases dense design: a lightweight PRNG, skeleton/soul split, rarity weighting, adaptive rendering, time‑windowed release, and prompt‑driven role separation.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

TypeScriptfeature flagsClaude CodeLLM promptASCII animationBuddy companiondeterministic PRNG
James' Growth Diary
Written by

James' Growth Diary

I am James, focusing on AI Agent learning and growth. I continuously update two series: “AI Agent Mastery Path,” which systematically outlines core theories and practices of agents, and “Claude Code Design Philosophy,” which deeply analyzes the design thinking behind top AI tools. Helping you build a solid foundation in the AI era.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.