Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>Codex PetNew to Visual Studio Code? Get it now.
Codex Pet

Codex Pet

DinohouseDigitalLLC

|
1,505 installs
| (0) | Free
A sprite-sheet pet that walks around a VS Code panel, similar to vscode-pets.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Codex Pet

A minimal VS Code extension, in the spirit of vscode-pets, that plays a Codex pet sprite sheet in its own Activity Bar side panel.

Disclaimer: This is an independent, community project. It is not affiliated with, endorsed by, sponsored by, or supported by OpenAI or Codex in any way. "Codex" and any related names or marks are the property of their respective owners; they are used here only to describe the sprite-sheet pet format the extension loads.

Status

Loads any pet that follows the Codex v2 pet spec (pet.json + spritesheet.webp) from the bundled pets/<pet-id>/ folder, plus any pet dropped into the user pets folder (see Codex Pet: Open Pets Folder below) — that folder lives outside the extension install directory, so pets you add there survive extension updates/reinstalls. Multiple pets can be present at once — pick which one loads via command or setting (see below). A user pet with the same id as a bundled one takes precedence. If no pet is found, or its spritesheet fails to load, the pet renders as a placeholder blob so the movement/animation logic is still visible.

Commands

  • Codex Pet: Reveal Pet View (codexPet.start) — opens/focuses the Activity Bar view. The pet(s) shown are resolved from codexPet.selectedPets (if non-empty), then codexPet.selectedPet, then the last pet(s) picked via "Choose Pets...", or the only pet if there's just one. If none of those resolve and multiple pets exist, prompts with a picker.
  • Codex Pet: Choose Pets... (codexPet.choosePets) — shows a multi-select picker and switches the view to the chosen pet(s) immediately, remembering the choice for next time. How many you can select at once is gated by your highest-level pet: 1 pet slot by default, a 2nd unlocks at level 5, a 3rd at level 15, a 4th at level 30 — locked slots are shown in the picker (🔒) so the next unlock is visible before it's reachable. codexPet.choosePet still works as an alias for this command.
  • Codex Pet: Open Pets Folder (codexPet.openPetsFolder) — reveals your persistent user pets folder in Finder/Explorer (creating it if needed), so you can drop in a new pet folder that survives extension updates.
  • Codex Pet: Install Claude Code Hooks... (codexPet.installClaudeCodeHooks) — adds the hooks described below to ~/.claude/settings.json (with a confirmation dialog first). Safe to re-run: it only adds hooks that aren't already there, and never touches unrelated hooks you've configured.

Reacting to Claude Code activity

If claude-code is listed in codexPet.aiActivitySources (it is by default), the pet plays a "busy" animation (the review sprite state) with a small speech bubble while Claude Code is actively working, and returns to normal idle/walk behavior once it's done. The bubble shows what it's doing when available — the current tool name (e.g. "Bash", "Edit") during tool use, or "Thinking" while a prompt is being processed.

Separately, whenever one or more Claude Code sessions need something from you — finished responding and waiting on a prompt, or blocked on a permission dialog — a small red count badge appears on the pet; hovering it shows which session(s) are waiting, labeled with the session's first prompt (truncated to 60 chars) if available, falling back to the workspace folder name otherwise — there's no real "chat title" to read from Claude Code, so the first prompt is the closest stand-in.

This works by watching per-session status files under ~/.codex-pet/sessions/ (one JSON file per session, e.g. claude-code-<session-id>.json, containing { "source", "state": "busy" | "waiting", "updatedAt": <ms>, "label": <string | null>, "cwd": <string | null>, "title": <string | null> }). A busy status older than 30s is treated as stale and ignored, so a crashed session can't leave the pet stuck animating. A waiting status can sit for much longer before being dropped (default 15 minutes, via codexPet.waitingStaleMinutes) since a session can legitimately wait on a human for a long time — this timeout only exists to eventually clean up sessions that crashed after finishing a response but before ending.

Claude Code reports into these files via its hooks mechanism. The first time the extension activates with hooks missing, it offers to install them for you; you can also trigger this anytime via Codex Pet: Install Claude Code Hooks..., or do it by hand by adding to ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [{ "hooks": [{ "type": "command", "command": "~/.codex-pet/report-status.sh claude-code busy" }] }],
    "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "~/.codex-pet/report-status.sh claude-code busy Thinking" }] }],
    "Notification": [{ "hooks": [{ "type": "command", "command": "~/.codex-pet/report-status.sh claude-code waiting" }] }],
    "Stop": [{ "hooks": [{ "type": "command", "command": "~/.codex-pet/report-status.sh claude-code waiting" }] }],
    "SessionEnd": [{ "hooks": [{ "type": "command", "command": "~/.codex-pet/report-status.sh claude-code end" }] }]
  }
}

report-status.sh itself is (re)written to ~/.codex-pet/ by the extension on every activation, so it's always present once the extension has run once — the hooks above just need to call it. Claude Code pipes the hook's JSON payload (which includes session_id, cwd, and for PreToolUse, tool_name; for UserPromptSubmit, prompt) on stdin; the script picks those up automatically to key each session's file and label the bubble, unless an explicit label is passed as a third argument (as UserPromptSubmit does with Thinking). The first UserPromptSubmit's prompt text is kept as that session's title from then on, ignoring later prompts, so the waiting badge's tooltip stays stable across a conversation. end (SessionEnd) deletes that session's file instead of writing a state.

If you installed hooks before this per-session tracking landed, re-run Codex Pet: Install Claude Code Hooks... — the old Stop/SessionEnd commands (which reported idle) will be recognized as missing and the current ones added alongside them; remove the stale idle lines from ~/.claude/settings.json by hand afterward.

Reacting to GitHub Copilot activity (experimental)

GitHub Copilot Chat has no hooks/lifecycle mechanism like Claude Code's, and VS Code doesn't yet expose a public API for observing Copilot Chat request status (microsoft/vscode#310951 is open but unshipped). So instead of a real signal, adding copilot to codexPet.aiActivitySources (on by default) uses a heuristic: bursts of large or multi-part text-document edits look more like an agent streaming changes than someone typing key-by-key, so the extension treats those as "busy" and reports it into ~/.codex-pet/sessions/copilot-copilot.json (same status-file protocol as Claude Code above, with a fixed "Copilot" label). Copilot has no equivalent of Claude Code's Stop hook, so it never reports waiting — only busy, cleared back to nothing once the edit burst quiets down.

This is a guess, not a real signal — it can also fire on pastes, snippet expansion, formatters, find/replace, or other bulk edits from any source, not just Copilot. Enable it if you want a rough "something is editing a lot" indicator; leave it off if false positives bother you.

Adding a pet

Drop a folder under either the bundled pets/<pet-id>/ (repo-only, ships with the extension) or, for pets that should stick around across updates, the folder opened by Codex Pet: Open Pets Folder. Each pet folder needs:

  • pet.json — { "id", "displayName", "description", "spritesheetPath", "kind" }
  • the spritesheet file it points to (typically spritesheet.webp)

Then run Codex Pet: Choose Pet... to pick it up — no reload or code changes needed, the folders are scanned fresh each time a pet is resolved.

Settings

Setting Default Description
codexPet.location panel Where the pet view shows: panel (bottom, next to Terminal), sidebar (Activity Bar, next to Explorer/Git), or both (each location runs its own independent pet).
codexPet.selectedPet "" Pet id or folder name under pets/ to always load. Empty = last picked pet. Ignored when selectedPets is non-empty.
codexPet.selectedPets [] Pet ids/folder names to show at once, in draw order. Takes precedence over selectedPet. Capped by your unlocked slot count (see "Choose Pets..." above); extra entries are ignored.
codexPet.walkSpeed 40 Pixels/second while running.
codexPet.moveChance 0.5 Probability (0-1) of starting a move vs. a stationary animation each cycle.
codexPet.minActionSeconds 3 Minimum seconds spent in one action before picking a new one.
codexPet.maxActionSeconds 10 Maximum seconds spent in one action before picking a new one.
codexPet.idleAnimationSpeed 0.5 Speed multiplier for stationary idle animations (idle, wave, jump, waiting, review, failed). Doesn't affect walk/run.
codexPet.idleStateWeights {} Relative chance of each idle animation being picked, e.g. { "wave": 3, "jump": 1 }. Unlisted states default to weight 1; 0 disables that animation.
codexPet.jumpCooldown 5000 Minimum milliseconds between cursor-triggered jumps.
codexPet.petScale 1 Scale multiplier applied to the pet's sprite size.
codexPet.aiActivitySources ["claude-code","copilot"] Which AI tools trigger the busy animation + speech bubble: claude-code, copilot (experimental heuristic), both, or empty to disable. See below.
codexPet.waitingStaleMinutes 15 How long (minutes) a Claude Code session can sit in the "waiting for you" state before its badge entry is dropped as stale (crash cleanup only — doesn't affect normal waiting).
codexPet.xpEnabled true Whether the pet earns XP/levels from coding activity (AI tool activity, editor edits, terminal use, git commits, clicks). Progress (and level) is tracked separately per pet.
codexPet.petGrowthEnabled true Whether the pet's sprite size grows with its level, from petGrowthMinScale at level 1 up to petGrowthMaxScale at petGrowthMaxLevel. Both are multipliers of codexPet.petScale. Requires xpEnabled.
codexPet.petGrowthMinScale 0.5 Size multiplier at level 1, when growth is enabled.
codexPet.petGrowthMaxScale 1.5 Size multiplier at petGrowthMaxLevel, when growth is enabled.
codexPet.petGrowthMaxLevel 20 Level at which the pet reaches petGrowthMaxScale. Growth is linear from level 1, then caps.
codexPet.useNonstandardSprites true Use each pet's nonstandard spritesheet and manifest (smoother baked ping-pong loops) instead of the standard Codex-compatible one, when available. See Nonstandard sprite sheet format below. Renamed from codexPet.useSeamlessSprites, which still works as a deprecated alias.

Timing settings (walkSpeed, moveChance, min/maxActionSeconds) apply live to an already-open view — no reload needed. Changing selectedPet swaps the pet in the open view immediately too.

Sprite sheet layout (Codex v2 pet spec)

Every Codex pet spritesheet is a fixed 1536×1872px, 8-column × 9-row grid of 192×208px cells, magenta (#ff00ff) color-keyed background, one row per animation state:

Row State
0 idle
1 running right
2 running left
3 waving
4 jumping
5 failed
6 waiting
7 running
8 review

(Grid size and row order per codexpets.org.) The official Codex app owns frame timing/sequencing itself — pet packages only ship the manifest and image. Since this extension is its own renderer, that same grid geometry plus our own per-row frame counts/fps/loop live in media/sprite-config.json (shared across all pets, not per-pet). Per-row frame counts aren't part of the published spec, so those were eyeballed from the bundled sample sheets under pets/ (e.g. pets/hoggie/spritesheet.webp) — double check them against a pet's spritesheet if an animation looks like it's looping into a blank/magenta frame.

Nonstandard sprite sheet format

Unlike the standard sheet above, this is not part of the published Codex spec — it's a format this extension's own asset pipeline produces, used only when codexPet.useNonstandardSprites is on and a pet ships one. It exists to get smoother, hand-tuned animations (variable frame counts per action, baked loop/ping-pong behavior) than the standard grid's fixed 8-column layout allows. A pet opts in by shipping both files under pets/<pet-id>/nonstandard-seamless/:

  • spritesheet-seamless.webp — the atlas image, an arbitrary columns × rows grid of cell.width × cell.height cells (not fixed at 8×9/192×208 like the standard sheet — read the sizes from the manifest, never assume them).
  • spritesheet-seamless.json — the manifest. Presence of both files (each is stat-checked independently, see resolvePets) is what sets a pet's hasNonstandardVariant flag; missing either one falls back to the standard sheet regardless of the setting.

The renderer (normalizeNonstandardConfig) only reads a subset of the manifest — the fields it actually consumes:

Field Meaning
cell.width, cell.height Pixel size of one grid cell.
actions[].id One of the action ids in the table below; unrecognized ids (including all look-* direction-pose rows) are ignored.
actions[].rowIndex Which atlas row this action's frames live in.
actions[].frameCount How many columns (starting at 0) to play.
actions[].fps Playback speed for this action.
actions[].loop Whether the clip repeats or holds on its last frame.

actions[].id → renderer state, mirroring NONSTANDARD_STATE_IDS:

Manifest id Renderer state
idle idle
running-right runRight
running-left runLeft
waving wave
jumping jump
failed failed
waiting waiting
running run
review review

look-* action ids (direction poses, not animations — e.g. look-000-to-157.5) are deliberately left unmapped so they never enter the idle rotation; they're reserved for a future cursor-facing feature that needs a pose lookup rather than a state.

On-screen size is normalized against the pet's standard manifest, not copied from the nonstandard one: scale is derived as (standardFrameWidth * standardScale) / cell.width, so a nonstandard sheet with a different cell resolution still renders at the same footprint the standard manifest defines. This means every pet must still ship a standard pet.json + spritesheet.webp even when its nonstandard variant is what actually gets displayed.

That cell-size-based scale assumes each pet's creature fills its cell proportionally the same, which the generation pipeline's own per-pet size normalization doesn't guarantee across different pets (only within one pet's own actions, via sizeNormalization.targetVisibleSize — see below). The renderer applies a further correction, scaling by REFERENCE_VISIBLE_SIZE / targetVisibleSize (in media/main.js), so pets whose creature occupies a smaller or larger share of the cell still render at the same visible height as one another. REFERENCE_VISIBLE_SIZE is set to the visible idle-frame height standard (non-sizeNormalization) Codex pets actually draw at out of their shared 208px-tall cell (measured directly off installed sheets), since the nonstandard cell size matches the standard grid's.

Everything else in the manifest — schemaVersion, codexCompatible, sourcePet/sourceSpritesheet, frameGeneration, actions[].frames[], recommendedRenderer, playback, idleBehavior, sizeNormalization, bottomAlignment, artifacts, neutralLookFrame, timing — is generation- pipeline provenance (how the sheet was built from source keyframes, why frames were chosen, cleanup steps applied) preserved for reference and regeneration, not read by the extension at runtime.

Current behavior

  • Every minActionSeconds-maxActionSeconds seconds, randomly either starts moving (using the runRight/runLeft row for the chosen direction) or plays a stationary animation picked from idleStates (idle, wave, jump, waiting, review, failed).

  • While moving, walks across the bottom of the view and reverses direction at the edges.

  • Clicking the pet interrupts whatever it's doing and plays a random animation from reactionStates (wave, jump by default, configurable in media/sprite-config.json) for a fixed ~1.2s, with a small heart particle popping up as feedback, then resumes normal idle/walk behavior.

  • While the cursor is anywhere over the view, every shown pet runs toward it horizontally instead of wandering randomly, queuing up side by side rather than stacking, and settles into idle once it's underneath. If the cursor stops roughly above a pet, that pet jumps to try to reach it (on its own ~1.2s cooldown so it doesn't jump nonstop). Moving the cursor off the view resumes normal random idle/walk behavior for all of them.

  • Each pet earns XP (and levels shown as a small badge on its sprite) from active coding: a git commit is a flat one-off bonus to every shown pet, and clicking a pet gives it a small rate-limited bonus. Active-minute XP (AI-tool activity, editor edits, terminal use) is a shared pool across however many pets are shown — it grows sublinearly with pet count (showing more pets earns more total XP, but not proportionally more) and splits with catch-up weighting so a lower-level pet in the group gets a bigger share. Leveling up plays a jump + heart celebration. Progress persists per pet across restarts and updates; disable with codexPet.xpEnabled. Optionally (codexPet.petGrowthEnabled, on by default), the pet's sprite size grows with its level too, from a smaller starting size up to 1.5x the configured scale — and once a pet reaches codexPet.petGrowthMaxLevel, it gets a golden badge outline and a slow sparkle aura, so leveling further still has a visible payoff after size growth caps out.

  • On top of the active-minute pool, a few multipliers reward showing up regularly and sticking around, all additive and applied before the per-pet split:

    • Daily streak: +10% per consecutive weekday (Mon–Fri) you're active, capping at +100% on day 10. Weekends don't advance or break it — it's neutral, not required and not penalized. Missing a single weekday is forgiven (e.g. active Thu, off Fri, active Mon → streak intact); missing two weekdays in a row resets it.
    • Session streak: +10% per 30 continuous active minutes in one sitting, capping at +100% at 5 hours. A gap of up to 30 minutes just pauses the clock (it doesn't reset); a gap over 90 minutes resets it to zero.
    • Weekend bonus: flat +20% for any active minute on Sat/Sun.
    • Late-night bonus: flat +20% for any active minute between 10pm and 6am local time, any day.

    These stack freely (no combined ceiling) — commit XP and click XP are unaffected either way.

Privacy & Security

Codex Pet makes no network requests — no analytics, telemetry, or third-party services, and no runtime dependencies at all. Everything it does stays on your machine:

  • AI activity tracking (see above) only reads event metadata — tool names, timestamps, and the first ~20 characters of your first prompt per session (kept as a stable label for the waiting-badge tooltip). It never reads file contents, git commit messages/diffs, or terminal output.
  • Status files for in-progress AI sessions live at ~/.codex-pet/sessions/*.json and are deleted when a session ends (or after the configured staleness window).
  • XP/level/streak progress is stored in this extension's own VS Code global storage (xp.json, streak.json), plus a couple of small flags in VS Code's globalState (last-selected pet(s), whether you've dismissed the hooks-install prompt).
  • Claude Code hooks: installing them (via the command, or the one-time prompt on activation) edits ~/.claude/settings.json to add a few lines that shell out to ~/.codex-pet/report-status.sh. That script is written to disk by the extension but only ever executed by Claude Code's own hook mechanism, never by the extension itself — and nothing is installed without your explicit confirmation in the dialog first.
  • The extension never spawns subprocesses or execs external commands itself.

To remove all of Codex Pet's data, delete ~/.codex-pet/ and uninstall the extension (VS Code cleans up its own global storage automatically).

Development

See docs/DEVELOPMENT.md for building and running from source, and packaging/installing a local .vsix.

Not implemented (see vscode-pets for reference if you want these later)

  • Persisting pet position/state across window reloads
  • Sound effects, throw-ball
  • Reacting to Codex CLI activity (Claude Code is wired up via real hooks, and Copilot has an experimental heuristic — see the sections above). waiting/failed are still just triggered randomly for variety.
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft