← Back to projects

PetForge

Live · npm v3.6.0

Local-first RPG progression layer for Claude Code — a deterministic ASCII pet that evolves from real coding activity (5 official hooks, 6 phases, 46 medal achievements, 19 species, embedded OTel collector, mobile PWA web view). Shipped on npm in 2 days / 64 commits / 9 releases.

FR EN
46 Achievements
19 Species
6 Phases
5 Claude Hooks
313 Tests
~9.6K TS Lines

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 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. 64 commits / 9 public releases in 2 calendar days, shipped on npm as `@mindvisionstudio/petforge`.

Structural technical decisions

01
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.

02
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.

03
PetForge engine core + custom OTel collector + mobile PWA

The deterministic engine is the product (19 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 12 achievements gated on real session quality (Code Architect 10K, Cache Lord ≥80 %, Frugal Coder 100p ≤ $1). Single-file web view (`page.ts`, 1,033 lines of HTML+CSS+JS in one template literal) served over SSE in real time + installable PWA manifest for mobile.

04
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 npm releases in 2 days (v1.0, v1.1, v1.2, v2.0, v2.0.1, v2.0.2, v2.1, v3.0, v3.1, v3.2, v3.3, v3.4) — fast iteration capability demonstrated. No tech debt: 0 TS errors, 1 `any` across 9,596 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.

Features

🥚
6 evolution phases

Egg → Hatchling → Junior → Adult → Elder → Mythic, triggered by level milestones

🏆
46 medal achievements

13 families bronze/silver/gold (+platinum on streak), idempotent backfill, 12 OTel-gated

🐣
19 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 import` parses an Anthropic ASCII card — local visual, zero redistribution, zero scraping

Tech Stack

Runtime
Node 20+TypeScript 5.9 stricttsup 8.5
CLI / TUI
Ink 7 (React 19.2)figlet 1chalk 5
State
proper-lockfile 4.1atomic JSONZod 4.4
Observability
Custom OTLP/HTTP/JSONCumulative-delta aggFan-out forward
Tests / CI
Vitest 4.1 (313 tests)Biome 2.4GitHub Actions
Distribution
npm public@mindvisionstudio/petforgeApache 2.0