Skip to content

feat: restore buddy companion system (Phase 1)#49

Closed
amDosion wants to merge 10 commits into
claude-code-best:mainfrom
amDosion:feat/buddy-system
Closed

feat: restore buddy companion system (Phase 1)#49
amDosion wants to merge 10 commits into
claude-code-best:mainfrom
amDosion:feat/buddy-system

Conversation

@amDosion
Copy link
Copy Markdown
Collaborator

@amDosion amDosion commented Apr 2, 2026

Summary

  • Restore the buddy companion system's missing action layer (command dispatcher, hatch logic, observer)
  • Enable feature('BUDDY') via --feature=BUDDY in dev.ts — single compile switch, zero scattered changes
  • Replace empty stub src/commands/buddy/index.ts with proper Command metadata
  • New files only: buddy.ts (command impl), observer.ts (reaction observer), 2 test files

What works after this PR

Command Behavior
/buddy Hatch a companion (deterministic local soul) or show existing card
/buddy pet Trigger hearts animation, auto-unmute
/buddy off Mute companion (sprite hidden, prompt injection stops)
/buddy on Unmute companion

Observer triggers reactions on: user mentioning companion name, test failures, error output.

Design decisions

  • No existing business code modified — only dev.ts (+1 line) and the empty stub index.ts
  • --feature=BUDDY is Bun's native feature flag mechanism, same as Anthropic's internal build system
  • Observer self-registers on globalThis via side-effect import, so REPL.tsx's bare fireCompanionObserver() call works without import changes
  • Phase 1 is local-only — no remote API calls, no model-generated souls

Test plan

  • 29 tests / 75 assertions / 0 failures
  • /buddy hatch produces deterministic companion with sprite, stats, personality
  • /buddy offcompanionMuted: true, /buddy onfalse
  • /buddy pet sets companionPetAt, auto-unmutes if muted
  • Observer detects name mention, test failure patterns, error patterns
  • Observer returns undefined when muted or no companion
  • No regression on existing 1307 tests
  • Manual smoke: bun run dev/buddy → sprite visible, reactions trigger

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added the Claude Code Buddy system: interactive /buddy command (hatch/pet/mute/rehatch), local companion generation, reaction triggers, ASCII companion widget, and a new built-in status line for model/context/cost visibility.
  • Documentation

    • Added extensive Buddy System docs and a project/agents overview with architecture and implementation plans.
  • Tests

    • Added tests for buddy commands and observer/reaction logic.
  • Chores

    • Updated referenced submodule, added local Claude dev settings, and updated .gitignore.

unraid and others added 8 commits April 2, 2026 17:11
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
移除 shims/、xiaohongshu/、vendor/、旧版 docs、多余 src 组件等 107 个文件

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…st display

Adds a default built-in status line that shows model name, context usage with
token counts, 5h session and 7d weekly rate limit progress bars with countdowns,
and cumulative cost. No configuration needed — displays automatically when no
external statusLine command is configured. Refactors StatusLine.tsx into routing
component with ExternalStatusLine (existing) and BuiltinStatusLineWrapper (new).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove external command status line path, keep built-in only
- Fix token data source: use currentUsage (same as contextUsedPct)
- Mock Date.now() in tests for deterministic results
- Switch imports to src/ aliases, remove dead KAIROS gate
- Add status line screenshot

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
备份 buddy 系统反编译还原文档及相关改动,为后续 buddy 功能恢复做准备。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supplement missing action layer for the buddy companion system:
- Command dispatcher (buddy.ts): /buddy hatch, /buddy pet, /buddy off/on
- Local soul generation: deterministic name + personality from species/stats
- Card formatting: ASCII sprite, stats bars, rarity display
- Observer (observer.ts): local template-based reactions for name mention,
  test failures, and errors; self-registers on globalThis for REPL compat
- Enable BUDDY feature flag via --feature=BUDDY in dev.ts
- Replace empty stub index.ts with proper Command metadata

29 tests, 75 assertions, 0 failures. No existing files modified except
dev.ts (1 line) and the empty stub index.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Adds a local "Claude Code Buddy" feature: deterministic companion generation, persistence, UI/sprite assets, /buddy command (hatch/pet/on/off/rehatch), observer/reactor for triggers, tests and docs; also bumps anthropic-skills submodule and adds local Claude settings.

Changes

Cohort / File(s) Summary
Submodule
\.agents/skills/anthropic-skills, \.claude/skills/anthropic-skills
Advanced submodule pointer to 98669c11...6621 (submodule SHA update only).
Local Claude config & ignores
\.claude/settings.local.json, \.gitignore
Added local Claude permissions file and ignored .claude/.agents skills artifacts.
Top-level docs
AGENTS.md
New repository guide describing architecture, build/dev/test commands, and assumptions.
Buddy design & docs
docs/claude-buddy-system/README.md, docs/claude-buddy-system/IMPLEMENTATION-PLAN.md
Added full design, species/rarity catalog, gating, phased implementation plan, and testing guidance.
Buddy types & assets
docs/claude-buddy-system/src/buddy/types.ts, docs/claude-buddy-system/src/buddy/sprites.ts
New core types (Species, Rarity, Stats, Companion shapes) and ASCII sprite/hats + rendering helpers.
Companion core (docs & runtime)
docs/claude-buddy-system/src/buddy/companion.ts, src/buddy/companion.ts, src/buddy/types.ts
Deterministic roll (FNV‑1a + Mulberry32), roll/get/create companion, persistence helpers; added seed support and generateSeed(); CompanionSoul now may include seed.
Buddy command (docs & runtime)
docs/claude-buddy-system/src/buddy/buddyCommand.ts, src/commands/buddy/buddy.ts, src/commands/buddy/index.ts
Implemented /buddy command (hatch/rehatch, pet, mute/unmute), rendering of companion card, state persistence, dynamic loading and observer import.
Observer & reactions
src/buddy/observer.ts, docs/claude-buddy-system/src/buddy/buddyReaction.ts
Added local observer detecting addressed/test_failed/error, deterministic local reaction builder, and stubbed remote reaction API + system-prompt builder.
Widget & UI helpers (docs)
docs/claude-buddy-system/src/buddy/CompanionWidget.ts
Added animation frames and exported helpers: calculateReactionThreshold and HATCH_EGG_FRAMES.
Notification & gating utilities (docs)
docs/claude-buddy-system/src/buddy/useBuddyNotification.ts
Added isBuddyLive(), useBuddyTeaser() stub, and findBuddyMentions() helper.
Dev script
scripts/dev.ts
Dev spawn now injects default features (including BUDDY) and respects FEATURE_* env vars as repeated --feature args.
Status line refactor & tests
src/components/BuiltinStatusLine.tsx, src/components/StatusLine.tsx, src/components/__tests__/BuiltinStatusLine.test.ts
New builtin status line component with countdown/rate-limit UI; simplified StatusLine to use the builtin; added countdown tests.
Tests
src/buddy/__tests__/observer.test.ts, src/commands/buddy/__tests__/buddy.test.ts
Added tests for observer detection and /buddy command flows (hatch/pet/mute/rehatch).
Documentation examples & assets
docs/claude-buddy-system/src/buddy/*
Multiple example modules and prose for buddy system (widget, sprites, reaction shapes, command examples).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as /buddy Command
    participant Companion as Companion Service
    participant Config as Global Config
    participant Observer

    User->>CLI: "/buddy" (first call)
    CLI->>Companion: rollWithSeed / build soul
    Companion-->>CLI: bones + soul
    CLI->>Config: save companion (hatchedAt, seed)
    Config-->>CLI: persisted
    CLI-->>User: rendered companion card

    Note over Observer,Config: Observer registered on import
    User->>Observer: fireCompanionObserver(messages, callback)
    Observer->>Config: getCompanion(), getGlobalConfig()
    Observer->>Observer: detectReactionReason(messages, name)
    alt no companion or muted
        Observer-->>User: undefined
    else trigger matched
        Observer->>Observer: buildLocalReaction(reason, seed)
        Observer-->>User: reaction string
    end
Loading
sequenceDiagram
    participant User
    participant CLI as /buddy Command
    participant AppState
    participant Config as Global Config

    User->>CLI: "/buddy pet"
    alt no companion
        CLI-->>User: "no companion yet"
    else companion exists
        CLI->>AppState: setAppState({ companionPetAt: now })
        AppState-->>CLI: updated
        CLI-->>User: pet response (heart)
    end

    User->>CLI: "/buddy off"
    CLI->>Config: set companionMuted = true
    CLI-->>User: "Companion muted"

    User->>CLI: "/buddy on"
    CLI->>Config: set companionMuted = false
    CLI-->>User: "Companion unmuted"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • 合并最新主分支 #36: Modifies src/buddy/companion.ts to add generateSeed() and change getCompanion() to use rollWithSeed(...), overlapping companion seeding and persistence changes.

Poem

🐰 I rolled a friend from hash and seed,

With tiny eyes and statious speed,
It chirps when tests go bump or fail,
Hatches in logs and tells a tale,
Pet it once — it wags with glee!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: restore buddy companion system (Phase 1)' is directly related to the main change, which is restoring the buddy companion system's action layer, command dispatcher, hatch logic, and observer. It accurately reflects the primary objective of the pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/buddy-system

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (9)
src/components/StatusLine.tsx (1)

23-24: Unused vimMode prop.

The vimMode prop is declared in Props but never used in StatusLineInner. If this is intentionally kept for API compatibility with existing call sites, consider adding a brief comment. Otherwise, it can be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/StatusLine.tsx` around lines 23 - 24, The Props type declares
a vimMode field that is never used in the component; either remove vimMode from
the Props type and any component signature/consumers (search for Props and
StatusLineInner) or explicitly document why it is preserved for API
compatibility by adding a short comment above the vimMode declaration and inside
StatusLineInner noting it is intentionally unused; update the Props interface
and StatusLineInner parameter list accordingly so the code and documentation
remain consistent.
docs/claude-buddy-system/src/buddy/buddyCommand.ts (2)

186-188: console.log debug statement should be removed or converted to debug logging.

This log statement will output to the terminal in any context where this code runs, potentially confusing users.

-  const prompt = buildSoulPrompt(bones, inspirationSeed);
-  console.log('[buddy] Would call AI with prompt:', prompt);
+  const prompt = buildSoulPrompt(bones, inspirationSeed);
+  // TODO: Implement actual AI call to Haiku model
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/src/buddy/buddyCommand.ts` around lines 186 - 188,
Remove the stray console.log in the buddy command where the prompt is built (the
lines using buildSoulPrompt and the local variable prompt); either delete the
console.log('[buddy] Would call AI with prompt:', prompt) entirely or replace it
with a proper debug logger call (e.g., use the existing module logger or a
debug-level function) so that prompt content is not printed to stdout in normal
runs.

197-203: Config stubs are no-ops — command won't persist any state.

_getConfig() returns an empty object and _updateConfig() does nothing. Commands like /buddy off, /buddy on, and /buddy pet will appear to succeed but won't persist any state changes. This should either throw to prevent accidental use or include a TODO comment marking these as requiring implementation.

 // Config 存根 (需要对接实际 config 系统)
 function _getConfig(): any {
-  return {};
+  // TODO: Replace with getGlobalConfig() from src/utils/config.ts
+  throw new Error('_getConfig stub not implemented')
 }
 function _updateConfig(patch: any): void {
-  // 源码: S8((prev) => ({ ...prev, ...patch }))
+  // TODO: Replace with saveGlobalConfig() from src/utils/config.ts
+  throw new Error('_updateConfig stub not implemented')
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/src/buddy/buddyCommand.ts` around lines 197 - 203,
The config stubs _getConfig() and _updateConfig(patch) are no-ops which means
commands like /buddy on/off/pet won’t persist state; update these functions so
they either throw a clear runtime error to prevent accidental use or include a
prominent TODO and warning log indicating they must be implemented against the
real config system. Specifically, change _getConfig to throw (or return a
documented sentinel) and change _updateConfig to throw (or log and throw) until
a real config integration is provided; ensure both functions and their error
messages reference the function names _getConfig and _updateConfig so callers
can identify the missing implementation.
docs/claude-buddy-system/src/buddy/buddyReaction.ts (1)

20-27: Unused constants may be premature abstractions.

REACTION_TIMEOUT, MAX_RETRIES, and MAX_REACTION_LENGTH are defined but not used in this file. If these are intended for future API implementation, consider adding a comment explaining their purpose, or removing them until needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/src/buddy/buddyReaction.ts` around lines 20 - 27,
The constants REACTION_TIMEOUT, MAX_RETRIES, and MAX_REACTION_LENGTH are
declared but unused; either remove them to avoid premature abstraction or
document/prepare them for future use: if you intend to keep them, add a brief
comment explaining their planned purpose and usage and/or export them (e.g.,
export const REACTION_TIMEOUT) so callers can use them; otherwise delete the
declarations to clean up dead code. Ensure you update any tests or imports if
you choose to export rather than remove.
src/buddy/observer.ts (1)

138-161: async function performs no async operations — consider making synchronous.

fireCompanionObserver is declared async but contains no await statements. The function is entirely synchronous. While this doesn't cause bugs (the returned Promise resolves immediately), it's misleading and adds unnecessary overhead.

However, if Phase 2 plans to add remote API calls here, keeping it async is forward-compatible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/buddy/observer.ts` around lines 138 - 161, The function
fireCompanionObserver is declared async but contains no awaits; remove the async
keyword and change its signature from "export async function
fireCompanionObserver(...): Promise<void>" to a synchronous "export function
fireCompanionObserver(...): void", then update any call sites that currently
await or treat it as a Promise to call it synchronously (remove awaits or .then
handlers); alternatively, if you prefer forward compatibility, keep the async
but add a clear comment explaining it's intentionally async for future remote
calls—ensure the function name (fireCompanionObserver), its parameters
(messages, callback), and any callers are updated accordingly.
src/buddy/__tests__/observer.test.ts (1)

10-17: Consider using mock.module() for config isolation instead of direct mutation.

While the current approach works because NODE_ENV=test uses an in-memory config object, using mock.module() would make tests more isolated and explicit about mocking behavior. This is a minor suggestion given the current approach works correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/buddy/__tests__/observer.test.ts` around lines 10 - 17, The test helper
resetConfig directly mutates global config via saveGlobalConfig/getGlobalConfig
(in resetConfig), which relies on in-memory behavior; instead make the config
isolation explicit by using mock.module() to provide a mocked config module or
factory for companion/companionMuted for the tests; replace the
saveGlobalConfig(...) call in resetConfig with a mock.module() setup that
returns the desired config shape (including companion and companionMuted
undefined) or mock the module that exposes getGlobalConfig/saveGlobalConfig so
each test gets an isolated fake config instance.
docs/claude-buddy-system/src/buddy/companion.ts (1)

329-345: Silent failure in config read hides errors and returns stale data.

The empty catch {} block at line 342 swallows all errors including permission issues, JSON parse errors for corrupted files, or encoding problems. Consider at minimum logging the error for debugging purposes in this reference implementation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/src/buddy/companion.ts` around lines 329 - 345, The
catch-all in _readCompanionFromConfig silently swallows all exceptions (file IO,
permissions, JSON parse, etc.); update the catch block to log the caught error
(including the error object and the computed configPath) via a logger or
console.error before returning undefined so failures are visible for debugging;
ensure you reference _readCompanionFromConfig and the configPath/companion
variables when adding the diagnostic message and keep the function's behavior of
returning undefined on error.
docs/claude-buddy-system/src/buddy/CompanionWidget.ts (1)

214-221: Hatch animation has only 3 frames — may appear abrupt.

The HATCH_EGG_FRAMES array contains only 3 frames: intact egg, cracked egg, and star burst. The comment on lines 206-212 describes a 5-step sequence. Consider adding intermediate frames for smoother animation if this is used in production.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/src/buddy/CompanionWidget.ts` around lines 214 -
221, HATCH_EGG_FRAMES currently has only 3 frames (intact, cracked, starburst)
but comments expect a 5-step sequence; add two intermediate ASCII frames to
smooth the hatch transition (e.g., a subtle hairline crack frame and a
wider-crack/pre-starlight frame) between the existing intact and cracked and
between cracked and starburst frames, updating each frame object in
HATCH_EGG_FRAMES (offset and lines arrays) so the sequence visually progresses
over five frames and preserves consistent art width and alignment.
src/commands/buddy/buddy.ts (1)

6-20: Switch this file to src/ alias imports.

The new command is using deep relative traversals throughout, which violates the repo import convention and will get brittle the next time this folder moves. Please rewrite these to src/... aliases instead.

As per coding guidelines, Import src/ path alias via tsconfig mapping instead of relative paths in imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/buddy.ts` around lines 6 - 20, Replace the deep relative
imports at the top of the file with the project's tsconfig alias imports
(src/...) so the module resolver uses the src path mapping; update imports that
currently bring in ToolUseContext, getCompanion, companionUserId, roll,
renderSprite, RARITY_STARS, STAT_NAMES, Companion, CompanionBones,
CompanionSoul, LocalJSXCommandContext, LocalJSXCommandOnDone, getGlobalConfig
and saveGlobalConfig to import from their corresponding src/... paths instead of
'../../...' style paths, ensuring the same exported names are used and that the
tsconfig path mapping resolves correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/settings.local.json:
- Around line 4-51: This local settings file contains over-privileged commands
(e.g., "Bash(git push:*)", "Bash(ssh:*)", broad "Bash(curl:*)", "Bash(git
reset:*)", "Bash(git cherry-pick:*)") and machine-specific absolute paths;
remove or replace these entries with a minimal, least-privilege template (keep
only safe readonly and dev-help entries like "Read(...)", basic "Bash(git
status:*)", "Bash(git add:*)", "Bash(git commit:*)") and strip absolute paths
such as /mnt/user/appdata/...; also add .claude/settings.local.json to
.gitignore and commit a template file (e.g., .claude/settings.template.json)
with placeholder, non-destructive commands and comments instructing users to
keep the real file local.

In @.claude/skills/anthropic-skills:
- Line 1: The submodule update for .claude/skills/anthropic-skills references a
non-existent commit (98669c11ca63e9c81c11501e1437e5c47b556621) and conflicts
with the actual .agents/skills/anthropic-skills commit
(b9db0fb4e1bd2ad57078dd3557ff6e159387f8c7); either remove the
.claude/skills/anthropic-skills submodule change from this PR if it was included
accidentally, or update the submodule entry to point to a valid upstream commit
and update the PR description to justify why this submodule change is part of
the buddy system restoration; ensure you update the gitlink in the commit for
.claude/skills/anthropic-skills to a real commit hash and run git submodule
sync/update before amending the PR commit.

In `@AGENTS.md`:
- Around line 50-53: Update the docs text in AGENTS.md that states "feature()
always returns false" to note the exception for the BUDDY flag: mention that
while most runtime feature() flags are disabled by default, the BUDDY feature is
explicitly enabled in src/entrypoints/cli.tsx (reference the feature() behavior
and the BUDDY flag) so contributors are not misled; adjust wording to either
list the exception or rephrase to "most flags disabled except BUDDY" and include
a short pointer to src/entrypoints/cli.tsx for where this is implemented.

In `@docs/claude-buddy-system/README.md`:
- Around line 91-123: Update the README to clearly distinguish upstream/design
intents from the current Phase 1 local-only implementation by adding an explicit
note inside the "AI 灵魂生成", "宠物评论 (Reaction)", "系统提示注入", and "编译门控" sections that
this PR implements only a local/dev-phase behaviour (no production model-soul
generation, the POST /api/... endpoint is not wired/served here, and GrowthBook
flag is not enabled) and that upstream/phase-2 components remain design intents;
reference the gating symbols feature('BUDDY'), isBuddyLive(), and
tengu_amber_flint when stating which checks are simulated vs actually enforced
in Phase 1 so readers won’t assume runtime/remote behavior.
- Around line 13-107: The README contains multiple fenced code blocks without
language tags (e.g., the src/buddy tree, the deterministic generation snippet
`userId + "friend-2026-401" → FNV-1a...`, the hats ASCII block, and the API POST
snippet), triggering markdownlint MD040; add appropriate language identifiers to
each fence (for example `text` for the file tree and ASCII hats, `text` or
`bash` for the generation snippet, and `http`/`text` for the POST API example)
so all triple-backtick blocks are annotated; update the fences around the
symbols shown (the src/buddy tree block, the deterministic generation block, the
hats block, and the POST /api/organizations/{orgId}/claude_code/buddy_react
block).

In `@docs/claude-buddy-system/src/buddy/buddyReaction.ts`:
- Line 46: The ReactionReason union in buddyReaction.ts includes 'periodic' but
observer.ts lacks support; update observer.ts to match by adding 'periodic' to
its ReactionReason type and implement handling in detectReactionReason and
buildLocalReaction: modify detectReactionReason (function name) to return
'periodic' under the intended periodic-detection condition (e.g., when the
trigger is a scheduled/heartbeat check) and add a corresponding branch in
buildLocalReaction to construct the local reaction payload for 'periodic' cases
(ensuring any required fields are set). Ensure the symbol names ReactionReason,
detectReactionReason, and buildLocalReaction are updated consistently so types
and runtime behavior align.

In `@docs/claude-buddy-system/src/buddy/companion.ts`:
- Around line 348-367: _replace the direct fs read/write in
_saveCompanionToConfig with the shared config helpers: stop using fs/path and
instead call saveGlobalConfig (or saveConfigWithLock) to persist the updated
companion object so file locking, atomic writes, and auth-safety checks are
honored; ensure you run the wouldLoseAuthState guard (from src/utils/config.ts)
before applying changes to avoid wiping tokens, and update only the companion
field on the existing config object via the helper so concurrent processes and
auth state protections are preserved (use the same semantics as
saveGlobalConfig/saveConfigWithLock).
- Around line 211-217: companionUserId currently reads only
process.env.CLAUDE_USER_ID, causing unauthenticated users to all resolve to
'anon'; restore the original logic by reading the config fallback path instead:
call getGlobalConfig() and return config.oauthAccount?.accountUuid ??
config.userID ?? 'anon' (or equivalent accessors used elsewhere in this repo) so
companionUserId() uses oauthAccount.accountUuid first, then config.userID, then
the 'anon' default; update the function companionUserId and import or reference
getGlobalConfig/config as needed to match the original implementation.

In `@docs/claude-buddy-system/src/buddy/useBuddyNotification.ts`:
- Around line 13-25: The comments for isBuddyLive mention first-party auth and
headless checks but the function only enforces the date gate; update isBuddyLive
to either (a) enforce the missing checks by calling getAuthType() and returning
false when it !== 'firstParty', and calling isHeadless() and returning false
when true before applying the existing date logic, or (b) if those runtime
checks are not desired, remove the misleading comment lines so the doc matches
the implemented date-only behavior; locate and modify the isBuddyLive function
to implement one of these two options.

In `@src/buddy/observer.ts`:
- Around line 138-141: Update the declaration in global.d.ts to match the
implementation: change fireCompanionObserver's callback parameter type from
(reaction: unknown) => void to (reaction: string | undefined) => void and change
its return type from void (or Promise) to void; then remove the unnecessary
async modifier from the implementation function fireCompanionObserver in
src/buddy/observer.ts so the function is a plain synchronous function with
signature fireCompanionObserver(messages: unknown[], callback: (reaction: string
| undefined) => void): void.

In `@src/commands/buddy/buddy.ts`:
- Around line 116-140: formatCompanionCard currently always renders the "active"
footer and doesn't accept mute state; change its signature to accept a muted
boolean (e.g., formatCompanionCard(companion: Companion, muted = false)) and use
that flag to swap the two footer lines to muted-friendly copy when muted
(replace the active-presence lines `  ${companion.name} is here · it'll chime in
as you code` and `  say its name to get its take · /buddy pet · /buddy off` with
a muted variant). Update callers (where /buddy builds the card) to pass the
companion's mute flag, and apply the same change to the other companion
formatter referenced in the comment (the formatter around lines 188-195) so both
renderers consult the muted boolean; keep existing usages of STAT_NAMES,
renderSprite, companion.shiny and other symbols intact.

---

Nitpick comments:
In `@docs/claude-buddy-system/src/buddy/buddyCommand.ts`:
- Around line 186-188: Remove the stray console.log in the buddy command where
the prompt is built (the lines using buildSoulPrompt and the local variable
prompt); either delete the console.log('[buddy] Would call AI with prompt:',
prompt) entirely or replace it with a proper debug logger call (e.g., use the
existing module logger or a debug-level function) so that prompt content is not
printed to stdout in normal runs.
- Around line 197-203: The config stubs _getConfig() and _updateConfig(patch)
are no-ops which means commands like /buddy on/off/pet won’t persist state;
update these functions so they either throw a clear runtime error to prevent
accidental use or include a prominent TODO and warning log indicating they must
be implemented against the real config system. Specifically, change _getConfig
to throw (or return a documented sentinel) and change _updateConfig to throw (or
log and throw) until a real config integration is provided; ensure both
functions and their error messages reference the function names _getConfig and
_updateConfig so callers can identify the missing implementation.

In `@docs/claude-buddy-system/src/buddy/buddyReaction.ts`:
- Around line 20-27: The constants REACTION_TIMEOUT, MAX_RETRIES, and
MAX_REACTION_LENGTH are declared but unused; either remove them to avoid
premature abstraction or document/prepare them for future use: if you intend to
keep them, add a brief comment explaining their planned purpose and usage and/or
export them (e.g., export const REACTION_TIMEOUT) so callers can use them;
otherwise delete the declarations to clean up dead code. Ensure you update any
tests or imports if you choose to export rather than remove.

In `@docs/claude-buddy-system/src/buddy/companion.ts`:
- Around line 329-345: The catch-all in _readCompanionFromConfig silently
swallows all exceptions (file IO, permissions, JSON parse, etc.); update the
catch block to log the caught error (including the error object and the computed
configPath) via a logger or console.error before returning undefined so failures
are visible for debugging; ensure you reference _readCompanionFromConfig and the
configPath/companion variables when adding the diagnostic message and keep the
function's behavior of returning undefined on error.

In `@docs/claude-buddy-system/src/buddy/CompanionWidget.ts`:
- Around line 214-221: HATCH_EGG_FRAMES currently has only 3 frames (intact,
cracked, starburst) but comments expect a 5-step sequence; add two intermediate
ASCII frames to smooth the hatch transition (e.g., a subtle hairline crack frame
and a wider-crack/pre-starlight frame) between the existing intact and cracked
and between cracked and starburst frames, updating each frame object in
HATCH_EGG_FRAMES (offset and lines arrays) so the sequence visually progresses
over five frames and preserves consistent art width and alignment.

In `@src/buddy/__tests__/observer.test.ts`:
- Around line 10-17: The test helper resetConfig directly mutates global config
via saveGlobalConfig/getGlobalConfig (in resetConfig), which relies on in-memory
behavior; instead make the config isolation explicit by using mock.module() to
provide a mocked config module or factory for companion/companionMuted for the
tests; replace the saveGlobalConfig(...) call in resetConfig with a
mock.module() setup that returns the desired config shape (including companion
and companionMuted undefined) or mock the module that exposes
getGlobalConfig/saveGlobalConfig so each test gets an isolated fake config
instance.

In `@src/buddy/observer.ts`:
- Around line 138-161: The function fireCompanionObserver is declared async but
contains no awaits; remove the async keyword and change its signature from
"export async function fireCompanionObserver(...): Promise<void>" to a
synchronous "export function fireCompanionObserver(...): void", then update any
call sites that currently await or treat it as a Promise to call it
synchronously (remove awaits or .then handlers); alternatively, if you prefer
forward compatibility, keep the async but add a clear comment explaining it's
intentionally async for future remote calls—ensure the function name
(fireCompanionObserver), its parameters (messages, callback), and any callers
are updated accordingly.

In `@src/commands/buddy/buddy.ts`:
- Around line 6-20: Replace the deep relative imports at the top of the file
with the project's tsconfig alias imports (src/...) so the module resolver uses
the src path mapping; update imports that currently bring in ToolUseContext,
getCompanion, companionUserId, roll, renderSprite, RARITY_STARS, STAT_NAMES,
Companion, CompanionBones, CompanionSoul, LocalJSXCommandContext,
LocalJSXCommandOnDone, getGlobalConfig and saveGlobalConfig to import from their
corresponding src/... paths instead of '../../...' style paths, ensuring the
same exported names are used and that the tsconfig path mapping resolves
correctly.

In `@src/components/StatusLine.tsx`:
- Around line 23-24: The Props type declares a vimMode field that is never used
in the component; either remove vimMode from the Props type and any component
signature/consumers (search for Props and StatusLineInner) or explicitly
document why it is preserved for API compatibility by adding a short comment
above the vimMode declaration and inside StatusLineInner noting it is
intentionally unused; update the Props interface and StatusLineInner parameter
list accordingly so the code and documentation remain consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 28461c38-0df6-47a0-923d-05bad01f4618

📥 Commits

Reviewing files that changed from the base of the PR and between 22ca3a1 and b9db0fb.

⛔ Files ignored due to path filters (1)
  • QQ20260402-192932.png is excluded by !**/*.png
📒 Files selected for processing (23)
  • .agents/skills/anthropic-skills
  • .claude/settings.local.json
  • .claude/skills/anthropic-skills
  • .githooks/pre-commit
  • AGENTS.md
  • docs/claude-buddy-system/IMPLEMENTATION-PLAN.md
  • docs/claude-buddy-system/README.md
  • docs/claude-buddy-system/src/buddy/CompanionWidget.ts
  • docs/claude-buddy-system/src/buddy/buddyCommand.ts
  • docs/claude-buddy-system/src/buddy/buddyReaction.ts
  • docs/claude-buddy-system/src/buddy/companion.ts
  • docs/claude-buddy-system/src/buddy/sprites.ts
  • docs/claude-buddy-system/src/buddy/types.ts
  • docs/claude-buddy-system/src/buddy/useBuddyNotification.ts
  • scripts/dev.ts
  • src/buddy/__tests__/observer.test.ts
  • src/buddy/observer.ts
  • src/commands/buddy/__tests__/buddy.test.ts
  • src/commands/buddy/buddy.ts
  • src/commands/buddy/index.ts
  • src/components/BuiltinStatusLine.tsx
  • src/components/StatusLine.tsx
  • src/components/__tests__/BuiltinStatusLine.test.ts

Comment on lines +4 to +51
"Bash(git rm:*)",
"Bash(git commit:*)",
"Bash(grep -ri buddy /mnt/user/appdata/claude-code/src/ --include=*.ts --include=*.tsx --include=*.js -l)",
"Bash(grep -ri buddy /mnt/user/appdata/claude-code/ --include=*.md --include=*.json -l)",
"Bash(git remote:*)",
"Bash(git fetch:*)",
"Bash(git merge:*)",
"Bash(grep -rn \"getSettings_DEPRECATED\\\\|getSettingsForSource\\\\|useSettings\\\\b\" /mnt/user/appdata/claude-code/src/hooks/useSettings.ts /mnt/user/appdata/claude-code/src/utils/settings/ --include=*.ts --include=*.tsx)",
"Bash(grep -rn extractQuotaStatusFromHeaders src/ --include=*.ts --include=*.tsx)",
"Bash(find src/components -name Usage* -o -name usage*)",
"Bash(find src:*)",
"Bash(grep -rn \"extractQuotaStatusFromHeaders\\\\|extractQuotaStatusFromError\" src/services/api/ --include=*.ts --include=*.tsx)",
"Bash(grep -rn \"getRawUtilization\\\\|checkQuotaStatus\" /mnt/user/appdata/claude-code/src/ --include=*.ts --include=*.tsx)",
"Bash(grep -n TerminalSize /mnt/user/appdata/claude-code/src/ink/components/TerminalSizeContext.ts*)",
"Bash(ls /mnt/user/appdata/claude-code/src/components/BuiltinStatusLine*)",
"Bash(ls /mnt/user/appdata/claude-code/src/components/Builtin*)",
"Bash(grep -r export.*formatCountdown src/components/)",
"Bash(bun test:*)",
"Bash(bun run:*)",
"Bash(npx biome:*)",
"Bash(git status:*)",
"Bash(git add:*)",
"Bash(gh --version)",
"Bash(gh auth:*)",
"Bash(curl -sL https://github.com/cli/cli/releases/latest/download/gh_2.74.1_linux_amd64.tar.gz -o /tmp/gh.tar.gz)",
"Bash(tar -xzf /tmp/gh.tar.gz -C /tmp)",
"Read(//tmp/**)",
"Bash(curl -sI https://github.com/cli/cli/releases/latest)",
"Bash(curl -sL \"https://github.com/cli/cli/releases/download/v2.89.0/gh_2.89.0_linux_amd64.tar.gz\" -o /tmp/gh.tar.gz)",
"Bash(cp /tmp/gh_2.89.0_linux_amd64/bin/gh /usr/local/bin/gh)",
"Bash(chmod +x /usr/local/bin/gh)",
"Bash(git checkout:*)",
"Bash(git cherry-pick:*)",
"Bash(git push:*)",
"Bash(gh pr:*)",
"Bash(gh api:*)",
"Bash(curl:*)",
"Bash(ssh:*)",
"Bash(git reset:*)",
"WebSearch",
"Bash(mkdir -p ~/.claude/skills)",
"Read(//root/.claude/**)",
"Bash(mkdir -p /mnt/user/appdata/claude-code/.claude/skills)",
"Bash(git clone:*)",
"Bash(python3 -m json.tool)",
"Bash(python3 -c \"import sys,json; data=json.load\\(sys.stdin\\); [print\\(f'''' {d[\"\"name\"\"]}/ \\({d[\"\"type\"\"]}\\)''''\\) for d in data]\")",
"Bash(python3 -c \"import sys,json; data=json.load\\(sys.stdin\\); [print\\(f'''' {d[\"\"name\"\"]} \\({d[\"\"type\"\"]}\\)''''\\) for d in data]\")",
"Bash(git pull:*)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not commit an over-privileged local agent policy.

This checked-in local config grants destructive/network-capable permissions (git push, ssh, broad curl, reset/cherry-pick) and hardcodes machine-specific absolute paths. That is a security and portability risk if reused outside one workstation. Please keep this file local-only (gitignored) and commit a minimal least-privilege template instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/settings.local.json around lines 4 - 51, This local settings file
contains over-privileged commands (e.g., "Bash(git push:*)", "Bash(ssh:*)",
broad "Bash(curl:*)", "Bash(git reset:*)", "Bash(git cherry-pick:*)") and
machine-specific absolute paths; remove or replace these entries with a minimal,
least-privilege template (keep only safe readonly and dev-help entries like
"Read(...)", basic "Bash(git status:*)", "Bash(git add:*)", "Bash(git
commit:*)") and strip absolute paths such as /mnt/user/appdata/...; also add
.claude/settings.local.json to .gitignore and commit a template file (e.g.,
.claude/settings.template.json) with placeholder, non-destructive commands and
comments instructing users to keep the real file local.

@@ -0,0 +1 @@
Subproject commit 98669c11ca63e9c81c11501e1437e5c47b556621
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify the submodule commit and check if it relates to buddy system

# Check if the submodule path exists and fetch the commit
if [ -d ".claude/skills/anthropic-skills" ]; then
  cd ".claude/skills/anthropic-skills" || exit 1
  
  # Verify commit exists
  if git cat-file -e 98669c11ca63e9c81c11501e1437e5c47b556621 2>/dev/null; then
    echo "✓ Commit exists"
    
    # Show commit details
    echo -e "\n=== Commit details ==="
    git show --no-patch --format=fuller 98669c11ca63e9c81c11501e1437e5c47b556621
    
    # Check for buddy-related changes in recent history
    echo -e "\n=== Checking for buddy-related changes ==="
    git log --oneline --grep="buddy" -i --all | head -10
    
    # Show files changed in this commit
    echo -e "\n=== Files changed in this commit ==="
    git show --name-status --format="" 98669c11ca63e9c81c11501e1437e5c47b556621
  else
    echo "✗ Commit 98669c11ca63e9c81c11501e1437e5c47b556621 not found"
  fi
else
  echo "✗ Submodule directory not found"
fi

Repository: claude-code-best/claude-code

Length of output: 131


🏁 Script executed:

#!/bin/bash
# Check if .agents/skills/anthropic-skills points to the same commit

if [ -d ".agents/skills/anthropic-skills" ]; then
  cd ".agents/skills/anthropic-skills" || exit 1
  current_commit=$(git rev-parse HEAD)
  echo "Current commit in .agents/skills/anthropic-skills: $current_commit"
  
  if [ "$current_commit" = "98669c11ca63e9c81c11501e1437e5c47b556621" ]; then
    echo "✓ Commits match"
  else
    echo "✗ Commits differ"
  fi
else
  echo "Note: .agents/skills/anthropic-skills not found for comparison"
fi

Repository: claude-code-best/claude-code

Length of output: 180


This submodule update points to a non-existent commit and does not align with the claimed upstream.

The commit 98669c11ca63e9c81c11501e1437e5c47b556621 does not exist in the .claude/skills/anthropic-skills submodule. Additionally, the .agents/skills/anthropic-skills submodule is at b9db0fb4e1bd2ad57078dd3557ff6e159387f8c7, contradicting the claim that the update matches the upstream.

This submodule change appears unrelated to the PR's stated objective (buddy system restoration) and should be either:

  1. Removed from this PR if it was accidentally included, or
  2. Corrected to point to a valid commit and justified in the PR description.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/anthropic-skills at line 1, The submodule update for
.claude/skills/anthropic-skills references a non-existent commit
(98669c11ca63e9c81c11501e1437e5c47b556621) and conflicts with the actual
.agents/skills/anthropic-skills commit
(b9db0fb4e1bd2ad57078dd3557ff6e159387f8c7); either remove the
.claude/skills/anthropic-skills submodule change from this PR if it was included
accidentally, or update the submodule entry to point to a valid upstream commit
and update the PR description to justify why this submodule change is part of
the buddy system restoration; ensure you update the gitlink in the commit for
.claude/skills/anthropic-skills to a real commit hash and run git submodule
sync/update before amending the PR commit.

Comment thread AGENTS.md
Comment on lines +50 to +53
1. **`src/entrypoints/cli.tsx`** — True entrypoint. Injects runtime polyfills at the top:
- `feature()` always returns `false` (all feature flags disabled, skipping unimplemented branches).
- `globalThis.MACRO` — simulates build-time macro injection (VERSION, BUILD_TIME, etc.).
- `BUILD_TARGET`, `BUILD_ENV`, `INTERFACE_TYPE` globals.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update feature-flag docs to reflect current Buddy behavior.

Lines 50-53 say all feature() flags are disabled, but dev now explicitly enables BUDDY. Please clarify this exception (or update the wording) to avoid misleading contributors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 50 - 53, Update the docs text in AGENTS.md that
states "feature() always returns false" to note the exception for the BUDDY
flag: mention that while most runtime feature() flags are disabled by default,
the BUDDY feature is explicitly enabled in src/entrypoints/cli.tsx (reference
the feature() behavior and the BUDDY flag) so contributors are not misled;
adjust wording to either list the exception or rephrase to "most flags disabled
except BUDDY" and include a short pointer to src/entrypoints/cli.tsx for where
this is implemented.

Comment on lines +13 to +107
```
src/buddy/
├── types.ts # 类型定义 & 常量 (物种/稀有度/属性/外观)
├── companion.ts # 核心模块 (PRNG/哈希/骨架生成/Config读写)
├── sprites.ts # ASCII 精灵动画 (18物种×3帧 + 帽子)
├── useBuddyNotification.ts # 可用性检查 & 预告通知
├── buddyCommand.ts # /buddy 斜杠命令入口
├── buddyReaction.ts # 宠物自动评论 (API调用 + 触发逻辑)
└── CompanionWidget.ts # 终端渲染组件 (React + Ink)
```

## 核心机制

### 1. 确定性宠物生成

每个用户的宠物是**确定性的** — 改配置也没用:

```
userId + "friend-2026-401" → FNV-1a hash → Mulberry32 PRNG → 逐项 roll
```

### 2. 18 种物种

| 物种 | 英文 | 特征 |
|------|------|------|
| 🦆 鸭子 | duck | `<(· )___` |
| 🪿 鹅 | goose | `(·>` |
| 🫧 果冻 | blob | 会膨胀缩小 |
| 🐱 猫 | cat | `=·ω·=` |
| 🐉 龙 | dragon | 会喷烟 |
| 🐙 章鱼 | octopus | 触手摆动 |
| 🦉 猫头鹰 | owl | 会眨眼 |
| 🐧 企鹅 | penguin | 会滑行 |
| 🐢 乌龟 | turtle | 龟壳变化 |
| 🐌 蜗牛 | snail | 留痕迹 |
| 👻 幽灵 | ghost | 飘浮波纹 |
| 🦎 六角恐龙 | axolotl | 鳃摆动 |
| 🦫 水豚 | capybara | 最大头 |
| 🌵 仙人掌 | cactus | 手臂变换 |
| 🤖 机器人 | robot | 天线闪烁 |
| 🐰 兔子 | rabbit | 耳朵抖动 |
| 🍄 蘑菇 | mushroom | 帽子变大 |
| 😺 胖猫 | chonk | 尾巴摇 |

### 3. 稀有度系统

| 稀有度 | 概率 | 星级 | 基础属性 | 帽子 |
|--------|------|------|----------|------|
| Common | 60% | ★ | 5 | 无 |
| Uncommon | 25% | ★★ | 15 | 有 |
| Rare | 10% | ★★★ | 25 | 有 |
| Epic | 4% | ★★★★ | 35 | 有 |
| Legendary | 1% | ★★★★★ | 50 | 有 |

**闪光 (Shiny)**: 任何稀有度都有 1% 概率

### 4. 外观系统

**眼睛** (6种): `·` `✦` `×` `◉` `@` `°`

**帽子** (8种):
```
crown: \^^^/ 皇冠
tophat: [___] 高帽
propeller: -+- 螺旋桨帽
halo: ( ) 光环
wizard: /^\ 巫师帽
beanie: (___) 毛线帽
tinyduck: ,> 小鸭子
```

### 5. 属性系统

五项属性: **DEBUGGING** / **PATIENCE** / **CHAOS** / **WISDOM** / **SNARK**

- 随机选 2 个为突出属性 (主属性大幅加成,副属性略低)
- 其余为基础 + 随机

### 6. AI 灵魂生成

孵化时调用 Haiku 模型生成:
- **名字**: 一个词,≤12字符,略带荒诞 (如 Pith, Dusker, Crumb)
- **性格**: 一句话,影响它评论代码的方式

### 7. 宠物评论 (Reaction)

宠物会在以下情况冒出气泡评论:
- **测试失败** — 检测到 "X failed" / "FAIL" 等
- **代码错误** — 检测到 "error:" / "exception" / "traceback"
- **被叫名字** — 用户在消息中提到宠物名字
- **周期性** — 每隔一段时间

评论通过 API 生成:
```
POST /api/organizations/{orgId}/claude_code/buddy_react
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language tags to fenced code blocks (MD040).

Please annotate these fences (e.g., text, bash) so markdownlint warnings are resolved.

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 13-13: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 30-30: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 74-74: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 106-106: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/README.md` around lines 13 - 107, The README
contains multiple fenced code blocks without language tags (e.g., the src/buddy
tree, the deterministic generation snippet `userId + "friend-2026-401" →
FNV-1a...`, the hats ASCII block, and the API POST snippet), triggering
markdownlint MD040; add appropriate language identifiers to each fence (for
example `text` for the file tree and ASCII hats, `text` or `bash` for the
generation snippet, and `http`/`text` for the POST API example) so all
triple-backtick blocks are annotated; update the fences around the symbols shown
(the src/buddy tree block, the deterministic generation block, the hats block,
and the POST /api/organizations/{orgId}/claude_code/buddy_react block).

Comment on lines +91 to +123
### 6. AI 灵魂生成

孵化时调用 Haiku 模型生成:
- **名字**: 一个词,≤12字符,略带荒诞 (如 Pith, Dusker, Crumb)
- **性格**: 一句话,影响它评论代码的方式

### 7. 宠物评论 (Reaction)

宠物会在以下情况冒出气泡评论:
- **测试失败** — 检测到 "X failed" / "FAIL" 等
- **代码错误** — 检测到 "error:" / "exception" / "traceback"
- **被叫名字** — 用户在消息中提到宠物名字
- **周期性** — 每隔一段时间

评论通过 API 生成:
```
POST /api/organizations/{orgId}/claude_code/buddy_react
```

### 8. 系统提示注入

宠物激活后,会在 Claude 的系统提示中注入一段 `<system-reminder>`,
告诉 Claude "有一个叫 {name} 的 {species} 坐在旁边,
用户叫它名字时你要让开"。

## 编译门控

Buddy 系统受三层门控保护:

1. **编译开关**: `feature('BUDDY')` — 构建时决定代码是否包含
2. **运行时检查**: `isBuddyLive()` — firstParty + 日期 ≥ 2026-04-01
3. **远程标志**: `tengu_amber_flint` — GrowthBook A/B 测试

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify “upstream design” vs “current Phase 1 behavior.”

These sections currently read as active runtime behavior (model soul generation, remote reaction API, GrowthBook gating), but this PR is local-only Phase 1. Add an explicit note to prevent expectation drift.

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 106-106: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/README.md` around lines 91 - 123, Update the README
to clearly distinguish upstream/design intents from the current Phase 1
local-only implementation by adding an explicit note inside the "AI 灵魂生成", "宠物评论
(Reaction)", "系统提示注入", and "编译门控" sections that this PR implements only a
local/dev-phase behaviour (no production model-soul generation, the POST
/api/... endpoint is not wired/served here, and GrowthBook flag is not enabled)
and that upstream/phase-2 components remain design intents; reference the gating
symbols feature('BUDDY'), isBuddyLive(), and tengu_amber_flint when stating
which checks are simulated vs actually enforced in Phase 1 so readers won’t
assume runtime/remote behavior.

Comment thread docs/claude-buddy-system/src/buddy/companion.ts
Comment on lines +348 to +367
function _saveCompanionToConfig(soul: CompanionSoul): void {
// 源码: saveGlobalConfig({ ...getGlobalConfig(), companion: soul })
try {
const fs = require('fs');
const path = require('path');
const configPath = path.join(
process.env.HOME || process.env.USERPROFILE || '',
'.claude',
'.claude.json',
);
let config: any = {};
try {
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
} catch {}
config.companion = soul;
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
} catch {
// silent fail
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Raw fs.writeFileSync bypasses config locking and safety guards — risks data loss and auth token corruption.

This implementation:

  1. Has no file locking — concurrent writes from multiple processes can corrupt the file
  2. Bypasses wouldLoseAuthState guard (see src/utils/config.ts:1217-1223) that prevents wiping authentication tokens
  3. Performs read-modify-write without atomicity — another process could write between read and write

The production code uses saveGlobalConfig() with saveConfigWithLock() which handles all these concerns.

 function _saveCompanionToConfig(soul: CompanionSoul): void {
-  // 源码: saveGlobalConfig({ ...getGlobalConfig(), companion: soul })
-  try {
-    const fs = require('fs');
-    const path = require('path');
-    const configPath = path.join(
-      process.env.HOME || process.env.USERPROFILE || '',
-      '.claude',
-      '.claude.json',
-    );
-    let config: any = {};
-    try {
-      config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
-    } catch {}
-    config.companion = soul;
-    fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
-  } catch {
-    // silent fail
-  }
+  // Use the proper config API from src/utils/config.ts:
+  // saveGlobalConfig(current => ({ ...current, companion: soul }))
+  throw new Error('_saveCompanionToConfig: use saveGlobalConfig from src/utils/config.ts')
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/src/buddy/companion.ts` around lines 348 - 367,
_replace the direct fs read/write in _saveCompanionToConfig with the shared
config helpers: stop using fs/path and instead call saveGlobalConfig (or
saveConfigWithLock) to persist the updated companion object so file locking,
atomic writes, and auth-safety checks are honored; ensure you run the
wouldLoseAuthState guard (from src/utils/config.ts) before applying changes to
avoid wiping tokens, and update only the companion field on the existing config
object via the helper so concurrent processes and auth state protections are
preserved (use the same semantics as saveGlobalConfig/saveConfigWithLock).

Comment on lines +13 to +25
* 条件:
* 1. 必须是 firstParty (OAuth 登录的 claude.ai 用户)
* 2. 不能是 headless 模式
* 3. 日期限制: 2026 年 4 月 1 日之后 (April Fools 彩蛋,但保留至今)
* 源码: q.getFullYear() > 2026 || (q.getFullYear() === 2026 && q.getMonth() >= 3)
* 注意: getMonth() 是 0-based,3 = April
*/
export function isBuddyLive(): boolean {
// 源码: if (getAuthType() !== 'firstParty') return false;
// 源码: if (isHeadless()) return false;
const now = new Date();
return now.getFullYear() > 2026 || (now.getFullYear() === 2026 && now.getMonth() >= 3);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Align isBuddyLive() comments with actual logic.

The docs say availability also depends on first-party auth/headless checks, but the function currently only enforces date gating. Either implement the missing checks or trim the comments so behavior is unambiguous.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/claude-buddy-system/src/buddy/useBuddyNotification.ts` around lines 13 -
25, The comments for isBuddyLive mention first-party auth and headless checks
but the function only enforces the date gate; update isBuddyLive to either (a)
enforce the missing checks by calling getAuthType() and returning false when it
!== 'firstParty', and calling isHeadless() and returning false when true before
applying the existing date logic, or (b) if those runtime checks are not
desired, remove the misleading comment lines so the doc matches the implemented
date-only behavior; locate and modify the isBuddyLive function to implement one
of these two options.

Comment thread src/buddy/observer.ts
Comment on lines +138 to +141
export async function fireCompanionObserver(
messages: unknown[],
callback: (reaction: string | undefined) => void,
): Promise<void> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the global.d.ts declaration for fireCompanionObserver
rg -n 'fireCompanionObserver' --type=ts -A5 -B2

Repository: claude-code-best/claude-code

Length of output: 7221


🏁 Script executed:

sed -n '138,162p' src/buddy/observer.ts

Repository: claude-code-best/claude-code

Length of output: 645


Update global.d.ts to match implementation signature and remove unnecessary async modifier.

The implementation in src/buddy/observer.ts (lines 138-141) has type mismatches with its declaration in src/types/global.d.ts (lines 32-35):

  • Callback parameter: implementation specifies (reaction: string | undefined) => void but declaration uses (reaction: unknown) => void
  • Return type: implementation returns Promise<void> but declaration specifies void

Additionally, fireCompanionObserver is declared as async but contains no await statements—it's entirely synchronous and could be a regular function.

Update global.d.ts to:

declare function fireCompanionObserver(
  messages: unknown[],
  callback: (reaction: string | undefined) => void,
): void

And remove the async modifier from the implementation in observer.ts line 138.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/buddy/observer.ts` around lines 138 - 141, Update the declaration in
global.d.ts to match the implementation: change fireCompanionObserver's callback
parameter type from (reaction: unknown) => void to (reaction: string |
undefined) => void and change its return type from void (or Promise) to void;
then remove the unnecessary async modifier from the implementation function
fireCompanionObserver in src/buddy/observer.ts so the function is a plain
synchronous function with signature fireCompanionObserver(messages: unknown[],
callback: (reaction: string | undefined) => void): void.

Comment thread src/commands/buddy/buddy.ts Outdated
Comment on lines +116 to +140
export function formatCompanionCard(companion: Companion): string {
const stars = RARITY_STARS[companion.rarity]
const sprite = renderSprite(companion, 0)
const shinyTag = companion.shiny ? ' ✨ SHINY' : ''

const lines = [
'',
...sprite,
'',
` ${companion.name}`,
` ${companion.species} · ${companion.rarity.toUpperCase()} ${stars}${shinyTag}`,
'',
` ${companion.personality}`,
'',
...STAT_NAMES.map(stat => {
const val = companion.stats[stat]
const filled = Math.floor(val / 10)
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled)
return ` ${stat.padEnd(12)} ${bar} ${val}`
}),
'',
` ${companion.name} is here · it'll chime in as you code`,
` say its name to get its take · /buddy pet · /buddy off`,
]
return lines.join('\n')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Muted companions still read as active in /buddy.

After /buddy off, the default branch still formats the existing card with no mute context, and the formatter always prints the active-presence footer. /buddy will therefore describe a muted companion as if it can still react. Pass the mute flag into the formatter and swap the footer/status copy when muted.

🛠️ Suggested fix
-export function formatCompanionCard(companion: Companion): string {
+export function formatCompanionCard(companion: Companion, muted = false): string {
   const stars = RARITY_STARS[companion.rarity]
   const sprite = renderSprite(companion, 0)
   const shinyTag = companion.shiny ? ' ✨ SHINY' : ''
@@
-    `  ${companion.name} is here · it'll chime in as you code`,
-    `  say its name to get its take · /buddy pet · /buddy off`,
+    muted
+      ? `  ${companion.name} is muted · it won't chime in right now`
+      : `  ${companion.name} is here · it'll chime in as you code`,
+    muted
+      ? '  /buddy on to wake it back up · /buddy pet also unmutes'
+      : `  say its name to get its take · /buddy pet · /buddy off`,
   ]
   return lines.join('\n')
 }
@@
       const existing = getCompanion()
       if (existing) {
-        onDone(formatCompanionCard(existing), { display: 'system' })
+        onDone(
+          formatCompanionCard(existing, Boolean(getGlobalConfig().companionMuted)),
+          { display: 'system' },
+        )
       } else {

Also applies to: 188-195

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/buddy.ts` around lines 116 - 140, formatCompanionCard
currently always renders the "active" footer and doesn't accept mute state;
change its signature to accept a muted boolean (e.g.,
formatCompanionCard(companion: Companion, muted = false)) and use that flag to
swap the two footer lines to muted-friendly copy when muted (replace the
active-presence lines `  ${companion.name} is here · it'll chime in as you code`
and `  say its name to get its take · /buddy pet · /buddy off` with a muted
variant). Update callers (where /buddy builds the card) to pass the companion's
mute flag, and apply the same change to the other companion formatter referenced
in the comment (the formatter around lines 188-195) so both renderers consult
the muted boolean; keep existing usages of STAT_NAMES, renderSprite,
companion.shiny and other symbols intact.

unraid and others added 2 commits April 2, 2026 22:55
- Adopt upstream dev.ts feature flag system (BUDDY + TRANSCRIPT_CLASSIFIER
  defaults, FEATURE_* env var support)
- Adopt upstream buddy.ts (type: 'local', LocalCommandCall, generateSeed,
  rollWithSeed, hatch/rehatch/pet/mute/unmute)
- Add /buddy off and /buddy on as aliases for mute/unmute (matches original
  decompiled source)
- Keep observer.ts (local template-based reactions) — upstream has none
- Update .gitignore: exclude .claude/settings.local.json, .claude/skills/,
  .agents/skills/, QQ*.png
- Rewrite tests to match upstream LocalCommandCall signature

29 tests, 57 assertions, 0 failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The yolo-classifier-prompts txt files don't exist in this repo yet,
so enabling TRANSCRIPT_CLASSIFIER at dev startup causes a module
resolution error. Keep only BUDDY in defaults; TRANSCRIPT_CLASSIFIER
can be enabled via FEATURE_TRANSCRIPT_CLASSIFIER=1 when the prompts
are added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude-code-best
Copy link
Copy Markdown
Owner

@amDosion 请清理一下代码哈, 清理完成 @我合入

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/commands/buddy/buddy.ts (1)

90-102: ⚠️ Potential issue | 🟡 Minor

Muted companions are still rendered as active in /buddy card output.

The card footer remains “active” even when muted, so the status messaging is misleading.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/buddy.ts` around lines 90 - 102, The card footer always
shows active commands; update the construction of the lines array so it reflects
a muted companion by checking companion.muted (or equivalent flag) and altering
the footer string accordingly. Locate the lines array creation in buddy.ts and
change the last element (currently the 'Commands: /buddy ...' entry) to
conditionally show a muted status (for example "Status: Muted" or "Muted —
Commands disabled") when companion.muted is true, otherwise keep the existing
commands text; ensure you reference companion.muted and preserve other entries
like sprite, speciesLabel, shiny, stars, and renderStats.
🧹 Nitpick comments (1)
src/commands/buddy/__tests__/buddy.test.ts (1)

102-108: Add an assertion that /buddy pet auto-unmutes.

This test only checks hearts text; it should also verify companionMuted flips to false after petting.

✅ Suggested test extension
   test('/buddy pet with companion shows hearts', async () => {
     await call('hatch', {} as any)
+    saveGlobalConfig(c => ({ ...c, companionMuted: true }))
     const name = getGlobalConfig().companion!.name
     const result = await call('pet', {} as any)
     expect(result.value).toContain(name)
     expect(result.value).toContain('♥')
+    expect(getGlobalConfig().companionMuted).toBe(false)
   })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/__tests__/buddy.test.ts` around lines 102 - 108, The test
for '/buddy pet with companion shows hearts' currently checks the pet text but
not that petting auto-unmutes the companion; after calling call('pet', {} as
any) add an assertion that getGlobalConfig().companionMuted is false (e.g.
expect(getGlobalConfig().companionMuted).toBe(false)) so the test verifies the
side‑effect of unmuting the companion alongside the existing checks for name and
'♥'.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/buddy/__tests__/buddy.test.ts`:
- Around line 2-3: Replace relative import paths in the test with the project's
tsconfig alias: change imports of getGlobalConfig and saveGlobalConfig from
'../../../utils/config.js' to 'src/utils/config' and change getCompanion from
'../../../buddy/companion.js' to 'src/buddy/companion'; update the import
specifiers in src/commands/buddy/__tests__/buddy.test.ts so the test uses the
src/... path aliases for getGlobalConfig, saveGlobalConfig and getCompanion.

In `@src/commands/buddy/buddy.ts`:
- Around line 1-13: Update the import statements at the top of this module to
use the tsconfig path alias (src/...) instead of relative paths; specifically
replace the current relative imports that bring in getCompanion, rollWithSeed,
generateSeed (from '../../buddy/companion.js'), StoredCompanion, RARITY_STARS,
STAT_NAMES (from '../../buddy/types.js'), renderSprite (from
'../../buddy/sprites.js'), getGlobalConfig, saveGlobalConfig (from
'../../utils/config.js') and LocalCommandCall (from '../../types/command.js')
with their corresponding src/... alias imports so the module consistently uses
the project's src path mapping.
- Around line 149-173: The pet branch updates companionPetAt but doesn't unmute
the buddy, so modify the AppState update in the /buddy pet handler (the
setAppState call inside the try block) to also clear companionMuted (set
companionMuted: false) when petting; keep the existing try/catch behavior for
non-interactive mode and ensure the same state key names (companionPetAt,
companionMuted) are used so getCompanion/renderSprite still work as expected.
- Around line 109-112: Update the hatch-tip text returned by the buddy command
so it references the correct reroll command; change the message string in the
returned object (the 'value' field in the response inside buddy.ts where it
returns type: 'text') to mention "/buddy rehatch" instead of "/buddy hatch
again" so users get the accurate instruction.

---

Duplicate comments:
In `@src/commands/buddy/buddy.ts`:
- Around line 90-102: The card footer always shows active commands; update the
construction of the lines array so it reflects a muted companion by checking
companion.muted (or equivalent flag) and altering the footer string accordingly.
Locate the lines array creation in buddy.ts and change the last element
(currently the 'Commands: /buddy ...' entry) to conditionally show a muted
status (for example "Status: Muted" or "Muted — Commands disabled") when
companion.muted is true, otherwise keep the existing commands text; ensure you
reference companion.muted and preserve other entries like sprite, speciesLabel,
shiny, stars, and renderStats.

---

Nitpick comments:
In `@src/commands/buddy/__tests__/buddy.test.ts`:
- Around line 102-108: The test for '/buddy pet with companion shows hearts'
currently checks the pet text but not that petting auto-unmutes the companion;
after calling call('pet', {} as any) add an assertion that
getGlobalConfig().companionMuted is false (e.g.
expect(getGlobalConfig().companionMuted).toBe(false)) so the test verifies the
side‑effect of unmuting the companion alongside the existing checks for name and
'♥'.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b32c5fd7-4cc7-4abf-bb38-6748c35a059c

📥 Commits

Reviewing files that changed from the base of the PR and between b9db0fb and ea505f9.

📒 Files selected for processing (7)
  • .gitignore
  • scripts/dev.ts
  • src/buddy/companion.ts
  • src/buddy/types.ts
  • src/commands/buddy/__tests__/buddy.test.ts
  • src/commands/buddy/buddy.ts
  • src/commands/buddy/index.ts
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/commands/buddy/index.ts
  • scripts/dev.ts

Comment on lines +2 to +3
import { getGlobalConfig, saveGlobalConfig } from '../../../utils/config.js'
import { getCompanion } from '../../../buddy/companion.js'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Switch test imports to src/ aliases.

These relative imports should use the configured src/... alias to match project conventions.

As per coding guidelines, "Import src/ path alias via tsconfig mapping instead of relative paths in imports."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/__tests__/buddy.test.ts` around lines 2 - 3, Replace
relative import paths in the test with the project's tsconfig alias: change
imports of getGlobalConfig and saveGlobalConfig from '../../../utils/config.js'
to 'src/utils/config' and change getCompanion from '../../../buddy/companion.js'
to 'src/buddy/companion'; update the import specifiers in
src/commands/buddy/__tests__/buddy.test.ts so the test uses the src/... path
aliases for getGlobalConfig, saveGlobalConfig and getCompanion.

Comment on lines +1 to +13
import {
getCompanion,
rollWithSeed,
generateSeed,
} from '../../buddy/companion.js'
import {
type StoredCompanion,
RARITY_STARS,
STAT_NAMES,
} from '../../buddy/types.js'
import { renderSprite } from '../../buddy/sprites.js'
import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'
import type { LocalCommandCall } from '../../types/command.js'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use src/ alias imports in this command module.

The new file uses relative import paths; switch these to src/... alias imports for consistency with repo rules.

As per coding guidelines, "Import src/ path alias via tsconfig mapping instead of relative paths in imports."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/buddy.ts` around lines 1 - 13, Update the import
statements at the top of this module to use the tsconfig path alias (src/...)
instead of relative paths; specifically replace the current relative imports
that bring in getCompanion, rollWithSeed, generateSeed (from
'../../buddy/companion.js'), StoredCompanion, RARITY_STARS, STAT_NAMES (from
'../../buddy/types.js'), renderSprite (from '../../buddy/sprites.js'),
getGlobalConfig, saveGlobalConfig (from '../../utils/config.js') and
LocalCommandCall (from '../../types/command.js') with their corresponding
src/... alias imports so the module consistently uses the project's src path
mapping.

Comment on lines +109 to +112
return {
type: 'text',
value: `You already have a companion! Use /buddy to see it.\n(Tip: /buddy hatch again will re-roll a new one.)`,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hatch tip text is inaccurate.

The response says “/buddy hatch again will re-roll,” but rerolling is handled by /buddy rehatch.

✏️ Proposed text fix
-        value: `You already have a companion! Use /buddy to see it.\n(Tip: /buddy hatch again will re-roll a new one.)`,
+        value: `You already have a companion! Use /buddy to see it.\n(Tip: use /buddy rehatch to roll a new one.)`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
type: 'text',
value: `You already have a companion! Use /buddy to see it.\n(Tip: /buddy hatch again will re-roll a new one.)`,
}
return {
type: 'text',
value: `You already have a companion! Use /buddy to see it.\n(Tip: use /buddy rehatch to roll a new one.)`,
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/buddy.ts` around lines 109 - 112, Update the hatch-tip
text returned by the buddy command so it references the correct reroll command;
change the message string in the returned object (the 'value' field in the
response inside buddy.ts where it returns type: 'text') to mention "/buddy
rehatch" instead of "/buddy hatch again" so users get the accurate instruction.

Comment on lines +149 to +173
if (sub === 'pet') {
const companion = getCompanion()
if (!companion) {
return {
type: 'text',
value:
"You don't have a companion yet! Use /buddy hatch to get one.",
}
}

try {
const { setAppState } = await import('../../state/AppStateStore.js')
setAppState(prev => ({
...prev,
companionPetAt: Date.now(),
}))
} catch {
// non-interactive mode — AppState not available
}

return {
type: 'text',
value: ` ${renderSprite(companion, 0).join('\n')}\n\n ${companion.name} purrs happily! ♥`,
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

/buddy pet does not auto-unmute the companion.

This branch updates companionPetAt but never clears companionMuted, so muted behavior remains active after petting.

🐛 Proposed fix
   if (sub === 'pet') {
     const companion = getCompanion()
     if (!companion) {
@@
     try {
       const { setAppState } = await import('../../state/AppStateStore.js')
       setAppState(prev => ({
         ...prev,
         companionPetAt: Date.now(),
       }))
     } catch {
       // non-interactive mode — AppState not available
     }
+
+    // Petting should wake the companion up.
+    saveGlobalConfig(cfg => ({ ...cfg, companionMuted: false }))
 
     return {
       type: 'text',
       value: `  ${renderSprite(companion, 0).join('\n')}\n\n  ${companion.name} purrs happily! ♥`,
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (sub === 'pet') {
const companion = getCompanion()
if (!companion) {
return {
type: 'text',
value:
"You don't have a companion yet! Use /buddy hatch to get one.",
}
}
try {
const { setAppState } = await import('../../state/AppStateStore.js')
setAppState(prev => ({
...prev,
companionPetAt: Date.now(),
}))
} catch {
// non-interactive mode — AppState not available
}
return {
type: 'text',
value: ` ${renderSprite(companion, 0).join('\n')}\n\n ${companion.name} purrs happily! ♥`,
}
}
if (sub === 'pet') {
const companion = getCompanion()
if (!companion) {
return {
type: 'text',
value:
"You don't have a companion yet! Use /buddy hatch to get one.",
}
}
try {
const { setAppState } = await import('../../state/AppStateStore.js')
setAppState(prev => ({
...prev,
companionPetAt: Date.now(),
}))
} catch {
// non-interactive mode — AppState not available
}
// Petting should wake the companion up.
saveGlobalConfig(cfg => ({ ...cfg, companionMuted: false }))
return {
type: 'text',
value: ` ${renderSprite(companion, 0).join('\n')}\n\n ${companion.name} purrs happily! ♥`,
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/buddy/buddy.ts` around lines 149 - 173, The pet branch updates
companionPetAt but doesn't unmute the buddy, so modify the AppState update in
the /buddy pet handler (the setAppState call inside the try block) to also clear
companionMuted (set companionMuted: false) when petting; keep the existing
try/catch behavior for non-interactive mode and ensure the same state key names
(companionPetAt, companionMuted) are used so getCompanion/renderSprite still
work as expected.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/dev.ts`:
- Around line 20-24: The envFeatures extraction only checks keys and ignores
values, so FEATURE_PROACTIVE=0 will still enable features; update the logic
around envFeatures (which reads process.env) to also check the environment
variable value (e.g., only accept "1", "true", or "yes" — case-insensitive)
before mapping the key to the feature name; modify the filter to use both key
and value (e.g., .filter(([k, v]) => k.startsWith("FEATURE_") && typeof v ===
"string" && ["1","true","yes"].includes(v.toLowerCase())) ) so only intended
feature flags are enabled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3f80f6ef-3e21-4b92-9ba2-4ee91702464f

📥 Commits

Reviewing files that changed from the base of the PR and between ea505f9 and e63c837.

📒 Files selected for processing (1)
  • scripts/dev.ts

Comment thread scripts/dev.ts
Comment on lines +20 to +24
// Any env var matching FEATURE_<NAME>=1 will also enable that feature.
// e.g. FEATURE_PROACTIVE=1 bun run dev
const envFeatures = Object.entries(process.env)
.filter(([k]) => k.startsWith("FEATURE_"))
.map(([k]) => k.replace("FEATURE_", ""));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Environment variable value is not checked.

The comment states FEATURE_<NAME>=1 enables the feature, but the filter only checks the key prefix and ignores the value. Setting FEATURE_PROACTIVE=0 or FEATURE_PROACTIVE=false would still enable the feature.

🛠️ Proposed fix to respect the env var value
 const envFeatures = Object.entries(process.env)
-    .filter(([k]) => k.startsWith("FEATURE_"))
+    .filter(([k, v]) => k.startsWith("FEATURE_") && v === "1")
     .map(([k]) => k.replace("FEATURE_", ""));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/dev.ts` around lines 20 - 24, The envFeatures extraction only checks
keys and ignores values, so FEATURE_PROACTIVE=0 will still enable features;
update the logic around envFeatures (which reads process.env) to also check the
environment variable value (e.g., only accept "1", "true", or "yes" —
case-insensitive) before mapping the key to the feature name; modify the filter
to use both key and value (e.g., .filter(([k, v]) => k.startsWith("FEATURE_") &&
typeof v === "string" && ["1","true","yes"].includes(v.toLowerCase())) ) so only
intended feature flags are enabled.

@amDosion
Copy link
Copy Markdown
Collaborator Author

amDosion commented Apr 2, 2026

@amDosion 请清理一下代码哈, 清理完成 @我合入

你直接拒绝吧,我看你那边的有一点优势,我们这里都是研究的官方cli的反向编译出来的,最大化保持官方一致, 你们有这个了,我就不弄了

@amDosion amDosion closed this Apr 2, 2026
@claude-code-best
Copy link
Copy Markdown
Owner

@amDosion thanks

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.

2 participants