refactor(buddy): align companion system with official CLI

## Summary

Reverse-engineered the official Claude Code CLI (v2.1.91) buddy/companion
system and aligned our implementation to match.

## Changes (7 files)

### Added
- `src/buddy/CompanionCard.tsx` (+109)
  JSX bordered card matching official vc8: rarity header, colored sprite,
  name, personality, 10-bar stats, last reaction in nested border.

- `src/buddy/companionReact.ts` (+156)
  Reaction system matching official ZUK+Dc8: 45s rate limiting, @-mention
  detection, transcript builder (12 msgs, 5000 chars), POST buddy_react API.

### Modified
- `src/commands/buddy/index.ts`
  type: local -> local-jsx, description/argumentHint/immediate/isHidden.

- `src/commands/buddy/buddy.ts`
  LocalCommandCall -> LocalJSXCommandCall signature (onDone, context, args).
  Removed mute/unmute/rehatch (official uses off/on only).
  /buddy show returns CompanionCard JSX instead of plain text.
  Pet auto-unmutes. companionMuted writes globalConfig (matches UI read source).

- `src/screens/REPL.tsx` (line 2808)
  globalThis.fireCompanionObserver -> import triggerCompanionReaction.

- `src/state/AppStateStore.ts` — comment fix.
- `src/types/global.d.ts` — removed fireCompanionObserver declaration.

## Data flow (verified consistent)
- companionMuted: saveGlobalConfig() <-> getGlobalConfig() (6 read sites)
- companionReaction: setAppState() <-> useAppState() (4 sites)
- companionPetAt: setAppState() <-> useAppState() (2 sites)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
unraid
2026-04-03 16:36:22 +08:00
parent 7935bfb4b8
commit 991119491c
7 changed files with 384 additions and 183 deletions

View File

@@ -275,6 +275,7 @@ const WebBrowserPanelModule = feature('WEB_BROWSER_TOOL') ? require('../tools/We
import { IssueFlagBanner } from '../components/PromptInput/IssueFlagBanner.js';
import { useIssueFlagBanner } from '../hooks/useIssueFlagBanner.js';
import { CompanionSprite, CompanionFloatingBubble, MIN_COLS_FOR_FULL_SPRITE } from '../buddy/CompanionSprite.js';
import { triggerCompanionReaction } from '../buddy/companionReact.js';
import { DevBar } from '../components/DevBar.js';
// Session manager removed - using AppState now
import type { RemoteSessionConfig } from '../remote/RemoteSessionManager.js';
@@ -2805,12 +2806,13 @@ export function REPL({
})) {
onQueryEvent(event);
}
// TODO: implement fireCompanionObserver — companion model reaction after each query turn
if (feature('BUDDY') && typeof fireCompanionObserver === 'function') {
void fireCompanionObserver(messagesRef.current, reaction => setAppState(prev => prev.companionReaction === reaction ? prev : {
...prev,
companionReaction: reaction as string | undefined
}));
if (feature('BUDDY')) {
triggerCompanionReaction(messagesRef.current, reaction =>
setAppState(prev => prev.companionReaction === reaction ? prev : {
...prev,
companionReaction: reaction as string | undefined,
})
);
}
queryCheckpoint('query_end');