diff --git a/src/commands.ts b/src/commands.ts
index f0fc6675a..98abcc23b 100644
--- a/src/commands.ts
+++ b/src/commands.ts
@@ -155,6 +155,11 @@ const buddy = feature('BUDDY')
require('./commands/buddy/index.js') as typeof import('./commands/buddy/index.js')
).default
: null
+const pokemonBattle = feature('BUDDY')
+ ? (
+ require('./commands/pokemon-battle/index.js') as typeof import('./commands/pokemon-battle/index.js')
+ ).default
+ : null
const poor = feature('POOR')
? (
require('./commands/poor/index.js') as typeof import('./commands/poor/index.js')
@@ -364,6 +369,7 @@ const COMMANDS = memoize((): Command[] => [
...(webCmd ? [webCmd] : []),
...(forkCmd ? [forkCmd] : []),
...(buddy ? [buddy] : []),
+ ...(pokemonBattle ? [pokemonBattle] : []),
...(poor ? [poor] : []),
...(proactive ? [proactive] : []),
...(monitorCmd ? [monitorCmd] : []),
diff --git a/src/commands/buddy/BuddyPanel.tsx b/src/commands/buddy/BuddyPanel.tsx
index 962fdeb6a..3438215bf 100644
--- a/src/commands/buddy/BuddyPanel.tsx
+++ b/src/commands/buddy/BuddyPanel.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { useState, useRef } from 'react';
+import { useState } from 'react';
import { Box, Text, Pane, Tab, Tabs, useInput, type Color } from '@anthropic/ink';
import { useSetAppState } from '../../state/AppState.js';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
@@ -21,8 +21,6 @@ import { getXpProgress } from '@claude-code-best/pokemon';
import { getGenderSymbol } from '@claude-code-best/pokemon';
import { StatBar, SpriteAnimator, getFallbackSprite, loadSprite } from '@claude-code-best/pokemon';
-import { BattleFlow, loadBuddyData } from '@claude-code-best/pokemon';
-import type { BattleFlowHandle } from '@claude-code-best/pokemon';
import type { LocalJSXCommandOnDone } from '../../types/command.js';
const CYAN: Color = 'ansi:cyan';
@@ -93,13 +91,6 @@ export function BuddyPanel({ buddyData, spriteLines, onClose }: BuddyPanelProps)
onClose={() => onClose('buddy panel closed')}
/>
,
-
-
- ,
,
@@ -622,43 +613,6 @@ function DexTab({
);
}
-// ─── Battle Tab ──────────────────────────────────────────
-
-function BattleTab({
- buddyData,
- isActive,
- onUpdate,
-}: {
- buddyData: BuddyData;
- isActive: boolean;
- onUpdate: (data: BuddyData) => void;
-}) {
- const [battleKey, setBattleKey] = useState(0);
- const inputRef = useRef(null);
-
- // Handle input here (in main app's Ink context) and forward to BattleFlow via ref
- useInput((input, key) => {
- if (!isActive) return;
- inputRef.current?.handleInput(input, key);
- });
-
- const handleClose = async () => {
- const updated = await loadBuddyData();
- onUpdate(updated);
- setBattleKey(k => k + 1);
- };
-
- return (
-
- );
-}
-
// ─── Egg Tab ──────────────────────────────────────────
function EggTab({ buddyData }: { buddyData: BuddyData }) {
diff --git a/src/commands/pokemon-battle/index.ts b/src/commands/pokemon-battle/index.ts
new file mode 100644
index 000000000..d2acd7662
--- /dev/null
+++ b/src/commands/pokemon-battle/index.ts
@@ -0,0 +1,15 @@
+import type { Command } from '../../commands.js'
+import { isBuddyLive } from '../../buddy/useBuddyNotification.js'
+
+const pokemonBattle = {
+ type: 'local-jsx',
+ name: 'pokemon-battle',
+ description: 'Start a Pokémon battle',
+ immediate: true,
+ get isHidden() {
+ return !isBuddyLive()
+ },
+ load: () => import('./pokemon-battle.js'),
+} satisfies Command
+
+export default pokemonBattle
diff --git a/src/commands/pokemon-battle/pokemon-battle.ts b/src/commands/pokemon-battle/pokemon-battle.ts
new file mode 100644
index 000000000..d4cddbadc
--- /dev/null
+++ b/src/commands/pokemon-battle/pokemon-battle.ts
@@ -0,0 +1,71 @@
+import React, { useState, useRef } from 'react'
+import { useInput } from '@anthropic/ink'
+import {
+ loadBuddyData,
+ saveBuddyData,
+ getActiveCreature,
+ BattleFlow,
+ type BuddyData,
+ type BattleFlowHandle,
+} from '@claude-code-best/pokemon'
+import type { ToolUseContext } from '../../Tool.js'
+import type {
+ LocalJSXCommandContext,
+ LocalJSXCommandOnDone,
+} from '../../types/command.js'
+
+async function getOrInitBuddyData(): Promise {
+ let data = await loadBuddyData()
+ if (!getActiveCreature(data)) {
+ data = await loadBuddyData()
+ }
+ return data
+}
+
+export async function call(
+ onDone: LocalJSXCommandOnDone,
+ _context: ToolUseContext & LocalJSXCommandContext,
+ _args: string,
+): Promise {
+ const data = await getOrInitBuddyData()
+
+ if (!getActiveCreature(data)) {
+ onDone('No companion yet · run /buddy first', { display: 'system' })
+ return null
+ }
+
+ return React.createElement(BattlePanel, {
+ buddyData: data,
+ onClose: () => {
+ onDone('battle closed', { display: 'system' })
+ },
+ })
+}
+
+function BattlePanel({
+ buddyData,
+ onClose,
+}: {
+ buddyData: BuddyData
+ onClose: () => void
+}) {
+ const [battleKey, setBattleKey] = useState(0)
+ const inputRef = useRef(null)
+
+ useInput((input, key) => {
+ inputRef.current?.handleInput(input, key)
+ })
+
+ const handleClose = async () => {
+ const updated = await loadBuddyData()
+ setBattleKey(k => k + 1)
+ }
+
+ return React.createElement(BattleFlow, {
+ key: battleKey,
+ buddyData,
+ onClose,
+ isActive: true,
+ inputRef,
+ })
+}