In short
Local-first RPG progression layer for Claude Code, a deterministic ASCII pet that evolves from real coding activity (5 official hooks, 6 phases, 52 medal achievements, 18 species, embedded OTel collector, mobile PWA web view).
Launched on npm in 2 days.
Highlights
-
6 evolution phases
Egg → Hatchling → Junior → Adult → Elder → Mythic, triggered by level milestones
-
52 medal achievements
14 families bronze/silver/gold (+platinum on streak), idempotent backfill, 21 OTel-gated
-
18 deterministic species
Octopus, Dragon, Capybara, Axolotl… seed = SHA-256(user + host), rarity buckets 60/25/10/4/1 %
-
5 Claude Code hooks
UserPromptSubmit, PostToolUse, Stop, SessionStart, SessionEnd: sub-50ms, never blocking
-
Custom OTel collector
OTLP/HTTP/JSON daemon (port 7879, 127.0.0.1 bind): cumulative-delta ingest, Datadog/Honeycomb fan-out
-
Mobile PWA web view
petforge serve+ SSE + manifest + 512×512 icon: installable from mobile Safari/Chrome -
100% local
State in ~/.petforge/state.json, file-locked, atomic writes, Windows EPERM retry, silent recovery
-
Consent-based Buddy integration
petforge buddy importparses an Anthropic ASCII card: local visual, zero redistribution, zero scraping
By the numbers
- 52 Achievements
- 18 Species
- 6 Phases
- 5 Claude Hooks
- 461 Tests
- ~10.8K TS Lines
The story
Why I built it
Anthropic shipped Claude Buddy in April 2026, a terminal tamagotchi natively integrated in Claude Code, with 18 species, 5 rarities, 5 stats. Brilliant product. But it stops there: no levels, no evolution, no progression. The community asks for it on GitHub within the first week. No one delivers.
I saw a structural gap, not cosmetic, and built the missing layer. PetForge is neither a fork, nor an extension, nor a copy: it’s an independent engine that can display Buddy when present, and generate its own pet when not. 9 public releases in 2 calendar days at launch, shipped on npm as @mindvisionstudio/petforge.
Structural technical decisions
Absolute local-first + unusual cross-platform hardening
All state lives in ~/.petforge/state.json (file-locked via proper-lockfile on a dedicated sentinel, atomic tmp+fsync+rename writes). Zero telemetry, zero account, zero phone-home.
Windows-specific hardening: retry EPERM/EBUSY/ENOENT/EACCES on fs.rename (Defender, OneDrive, indexers) with exponential backoff 50/150/400 ms, max 4 attempts under the 1s hook timeout. Silent recovery: a corrupt state.json is saved as .corrupt.<ts>.json then recreated: a hook never crashes Claude Code.
Trademark-clean by design + consent-based Buddy integration
Not a single ’claude’ or ’buddy’ in the package name, repo name, CLI name, or assets. Buddy is invoked at runtime only via claude /buddy card (= reading public stdout). The petforge buddy import integration parses an Anthropic ASCII card from stdin/file, strips out stat lines to avoid duplicates, and stores the visual locally without redistribution. No scraping, no automatic fetch, no internal file copy.
PetForge engine core + custom OTel collector + mobile PWA
The deterministic engine is the product (18 species × 6 phases × 5 rarities × shiny = 570 visual combos). Beyond that: an OTLP/HTTP/JSON daemon (petforge collect) that ingests official Claude Code metrics (code.lines.added, tokens.in/out, cost.usd, pull_request.created…) and unlocks 21 achievements gated on real session quality (Code Architect 10K, Cache Lord ≥80 %, Frugal Coder 100p ≤ $1).
Single-file web view (page.ts, 1,303 lines of HTML+CSS+JS in one template literal) served over SSE in real time + installable PWA manifest for mobile.
Two strategic pivots v1 → v2 → v3 + 9 public releases in 2 days
v1: sprites + VS Code extension (rejected: install friction, audience too narrow). v2: RPG layer wrapping Buddy directly (rejected after license review + dependency risk). v3 (shipped): standalone engine, Buddy as opt-in, trademark-clean Apache 2.0. Three iterations of design before the first line of code, then 9 public npm releases in 2 days at launch: fast iteration capability demonstrated. No tech debt: 0 TS errors, 1 any across ~10,800 lines, biome clean.
The non-trivial challenge
Building on top of an Anthropic product without getting absorbed, copied, or blocked
Three parallel risks: (1) Anthropic ships the same feature (medium probability, low impact if the PetForge engine is core and Buddy just optional); (2) Buddy gets deprecated or changes format (mitigated: runtime detection + silent fallback); (3) trademark complaint if we flirt with Buddy/Claude in naming or copy assets (very low probability, critical impact, covered by absolute separation: zero Anthropic words, zero copied assets, runtime invoke only).
The real challenge isn’t technical, it’s legal-strategic: finding the right cleavage that lets you exist without depending, copying, or provoking. Plus the cross-platform challenge: keeping 5 Claude Code hooks under 50ms without blocking the user’s workflow, on Windows where Defender sometimes holds state.json post-write.
Lesson learned
Build on top of validated concepts when the gap is structural, not cosmetic. Buddy validated the appetite for a terminal companion: the community has already voted with GitHub issues on what’s missing.
Building the missing layer as a real independent product is more defensive (not an Anthropic extension at their mercy) and more instructive (the spec was rewritten three times to minimize dependency) than waiting for them to ship it themselves. Shipping in 2 days / 9 releases doesn’t happen without Spec → Plan → Blitz → Polish: 5 design specs + 6 implementation plans existed BEFORE the first commit.
Tech specs
Where it runs Node 20+ · atomic JSON · npm public
| Runtime | Node 20+ · TypeScript 5.9 strict · tsup 8.5 |
|---|---|
| CLI / TUI | Ink 7 (React 19.2) · figlet 1 · chalk 5 |
| State | proper-lockfile 4.1 · atomic JSON · Zod 4.4 |
| Observability | Custom OTLP/HTTP/JSON · Cumulative-delta agg · Fan-out forward |
| Tests / CI | Vitest 4.1 (461 tests) · Biome 2.4 · GitHub Actions |
| Distribution | npm public · @mindvisionstudio/petforge · Apache 2.0 |