feat: configurable puppy emoji (puppymoji) 🦴#301
Open
mattnico wants to merge 3 commits intompfaffenberger:mainfrom
Open
feat: configurable puppy emoji (puppymoji) 🦴#301mattnico wants to merge 3 commits intompfaffenberger:mainfrom
mattnico wants to merge 3 commits intompfaffenberger:mainfrom
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Make the hard-coded
🐶emoji user-configurable via a newpuppy_emojiconfig key. Mirrors exactly howpuppy_namealready works — defaults to🐶so existing users see no change.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_emojiprompt_toolkit_completion.py( 🦴 )spinner_base.pycli_runner.py/showstatus header + newpuppy_emoji:rowconfig_commands.pyonboarding_slides.pyagent_code_puppy.py/)api/app.py/terminal)api/app.py(substituted at request time)api/app.pyIntentionally NOT touched (YAGNI / branding / content)
oauth_puppy_html.pystylized sprites (cannons, crying dogs, themed art)error_logging.py/__init__.pydecorative commentsCode-Puppy 🐶(the agent's brand, not the user's pup)Validation
set_puppy_emoji()rejects empty / whitespace-only / >16 chars /None🐕🦺(4 codepoints) without enabling abuseTests
TestPuppyEmojicovering get/set/validation/default/whitespace/ZWJcurrent_frameTestGetConfigKeyssnapshots for the new key + 2 spinner tests made hermetic — they were silently coupled to the dev's realpuppy.cfg)/and/terminalreflect a custom emojiruff checkclean on all touched filesDiff
11 files, +209 / -36, 2 commits cleanly based on
origin/main. Each change is small and follows the existingpuppy_namepattern.Pre-existing oddity worth a follow-up (not in scope)
SpinnerBase.THINKING_MESSAGE/WAITING_MESSAGE/puppy_nameare evaluated at class-definition time, so changingpuppy_namemid-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/mainwith only the 2 puppymoji commits.