Skip to content

feat: configurable puppy emoji (puppymoji) 🦴#301

Open
mattnico wants to merge 3 commits intompfaffenberger:mainfrom
mattnico:feat/puppymoji
Open

feat: configurable puppy emoji (puppymoji) 🦴#301
mattnico wants to merge 3 commits intompfaffenberger:mainfrom
mattnico:feat/puppymoji

Conversation

@mattnico
Copy link
Copy Markdown
Contributor

What

Make the hard-coded 🐶 emoji user-configurable via a new puppy_emoji config key. Mirrors exactly how puppy_name already works — defaults to 🐶 so existing users see no change.

/set puppy_emoji 🦊
/set puppy_emoji 🐺
/set puppy_emoji 🐕‍🦺   # ZWJ sequences work too

Why

Some of us want our pup to be a fox, a wolf, or a bone 🦴. Trivial to ship, zero behavior change for default users, fully reversible.

Surfaces that now respect puppy_emoji

Surface Source
Interactive prompt prefix prompt_toolkit_completion.py
Thinking spinner frames ( 🦴 ) spinner_base.py
Startup banner & 'Continuing in Interactive Mode' cli_runner.py
/show status header + new puppy_emoji: row config_commands.py
Onboarding wizard slides onboarding_slides.py
Agent self-intro ('what is code puppy?') agent_code_puppy.py
API landing page (/) api/app.py
API terminal page (/terminal) api/app.py (substituted at request time)
API startup/shutdown log lines api/app.py

Intentionally NOT touched (YAGNI / branding / content)

  • Pack-leader ASCII art (multi-puppy art, not user identity)
  • MOTD historical content (date-stamped messages)
  • oauth_puppy_html.py stylized sprites (cannons, crying dogs, themed art)
  • READMEs / SETUP docs
  • Plugin examples
  • error_logging.py / __init__.py decorative comments
  • Agent product display name Code-Puppy 🐶 (the agent's brand, not the user's pup)

Validation

  • set_puppy_emoji() rejects empty / whitespace-only / >16 chars / None
  • Whitespace trimmed on read & write
  • Falls back to default if config blanked out
  • 16-char cap allows ZWJ combos like 🐕‍🦺 (4 codepoints) without enabling abuse

Tests

  • 10 new tests in TestPuppyEmoji covering get/set/validation/default/whitespace/ZWJ
  • 2 new spinner tests verifying live emoji propagates to current_frame
  • Updated 4 existing tests (2 TestGetConfigKeys snapshots for the new key + 2 spinner tests made hermetic — they were silently coupled to the dev's real puppy.cfg)
  • End-to-end TestClient verified / and /terminal reflect a custom emoji
  • 85/85 targeted tests pass; ruff check clean on all touched files

Diff

11 files, +209 / -36, 2 commits cleanly based on origin/main. Each change is small and follows the existing puppy_name pattern.

Pre-existing oddity worth a follow-up (not in scope)

SpinnerBase.THINKING_MESSAGE / WAITING_MESSAGE / puppy_name are evaluated at class-definition time, so changing puppy_name mid-session doesn't propagate to the spinner without restart. Same lazy-resolution pattern this PR uses for emoji would fix it. Happy to do as a separate PR if desired.

History note

Supersedes #300, which was closed because it was inadvertently based on a local main with 7 unrelated WIP commits (termflow word-wrap work) that polluted the diff and broke unrelated CI checks. This PR is re-cut clean from origin/main with only the 2 puppymoji commits.

Matt Nicolaysen added 3 commits April 21, 2026 21:22
Replace the hard-coded 🐶 at every user-facing runtime surface with a
new puppy_emoji config (defaults to 🐶, mirrors how puppy_name works).

- config.get_puppy_emoji() / set_puppy_emoji() with validation
  (non-empty, max 16 chars to allow ZWJ sequences like 🐕\u200d🦺)
- puppy_emoji exposed via get_config_keys() so /set & tab-completion work
- Replaced hard-coded 🐶 in:
  - interactive prompt prefix (prompt_toolkit_completion)
  - startup banner & 'Continuing in Interactive Mode' (cli_runner)
  - /show status header (config_commands)
  - onboarding wizard slides
  - agent self-intro ('what is code puppy?')
  - API landing page (/) & terminal page (/terminal)
  - API startup/shutdown log lines
- terminal.html stays a static asset; emoji is substituted at request
  time so we don't have to add Jinja2 just for one token.
- Intentionally left alone: pack-leader ASCII art, MOTD historical
  content, oauth_puppy_html sprites, README/SETUP docs, plugin
  examples, error_logging docstring, agent product display name
  ('Code-Puppy 🐶'). Those are branding/content, not user identity.

Tests: 10 new tests for get/set/validation + updated 2 snapshot tests
in TestGetConfigKeys for the new key. End-to-end verified via
TestClient that '/' and '/terminal' both reflect a custom emoji.

Use it: /set puppy_emoji 🦊
Make SpinnerBase.current_frame render with the live puppy_emoji config
on every access, so '/set puppy_emoji 🦴' flips both the prompt prefix
AND the 'Betty White is thinking... ( 🦴 )' spinner without a restart.

- New _build_spinner_frames(emoji) helper as the single source of truth
  for the bouncing-puppy frame template (DRY: same shape used by both
  the frozen FRAMES default and the live current_frame render)
- SpinnerBase.FRAMES kept as a class attribute frozen at import time
  with DEFAULT_PUPPY_EMOJI for backward compatibility — tests and any
  external code that reads it stay green
- current_frame property now calls get_puppy_emoji() per access so the
  user's chosen emoji shows live; frame index logic untouched
- Made two existing spinner tests hermetic by patching get_puppy_emoji
  to the default; they were silently coupled to the developer's real
  puppy.cfg (caught when puppy_emoji=🦴 was set in the dev config)
- Added 2 new tests:
  * current_frame uses live puppy_emoji on access (not import-time)
  * FRAMES class attr stays default for backward compat

Note: SpinnerBase.THINKING_MESSAGE / WAITING_MESSAGE / puppy_name still
freeze at import (pre-existing behavior — puppy_name changes don't
propagate to spinner without restart). Out of scope for puppymoji;
worth a separate ticket if anyone ever cares.
Pure cosmetic line-wrapping caught by 'ruff format --check .' in CI:

- onboarding_slides.py: wrap one long Press-Enter f-string
- test_console_spinner_coverage.py: parenthesize the 2-context with
  block (PEP 654 style preferred by ruff)

No behavior change. 51/51 spinner tests still pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant