chenbhao Copilot commited on
Commit
d52e9dd
·
1 Parent(s): e1bb50d

tools: add prompt() to Friend tools to avoid undefined prompt runtime error

Browse files

Ensure tools expose prompt() to satisfy callers that invoke tool.prompt() at runtime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

src/commands/friend/friend.tsx ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * /friend TUI command — manage VRM desktop pet companion.
3
+ *
4
+ * Ink-based terminal UI that shows status and controls for the
5
+ * Friend desktop pet feature.
6
+ */
7
+ import React, { useState, useEffect } from 'react'
8
+ import { Box, Text, useInput } from '../../ink.js'
9
+ import { getPrefs, updatePrefs } from '../../friend/prefs.js'
10
+ import type { LocalJSXCommandOnDone, CommandResultDisplay } from '../../commands.js'
11
+
12
+ const FRIEND_URL = 'http://127.0.0.1:3456/friend/'
13
+
14
+ type Page = 'status' | 'help'
15
+
16
+ export async function call(
17
+ onDone: (result?: string, options?: { display?: CommandResultDisplay }) => void,
18
+ _context: unknown,
19
+ args?: string,
20
+ ): Promise<React.ReactNode> {
21
+ const trimmed = args?.trim().toLowerCase()
22
+
23
+ // Non-interactive modes
24
+ if (trimmed === 'start') {
25
+ updatePrefs({ enabled: true })
26
+ return (
27
+ <Box flexDirection="column">
28
+ <Text>Friend companion enabled.</Text>
29
+ <Text>Start the server with:</Text>
30
+ <Text> bun run src/server/index.ts --port 3456</Text>
31
+ <Text>Then open {FRIEND_URL} in a browser (or Tauri will launch automatically).</Text>
32
+ </Box>
33
+ )
34
+ }
35
+
36
+ if (trimmed === 'stop') {
37
+ updatePrefs({ enabled: false })
38
+ return <Text>Friend companion disabled.</Text>
39
+ }
40
+
41
+ if (trimmed === 'status') {
42
+ const prefs = getPrefs()
43
+ return <FriendStatus prefs={prefs} compact />
44
+ }
45
+
46
+ // Interactive mode (no args or unrecognized)
47
+ return <FriendManager onDone={onDone} />
48
+ }
49
+
50
+ function FriendManager({ onDone }: { onDone: LocalJSXCommandOnDone }) {
51
+ const [page, setPage] = useState<Page>('status')
52
+
53
+ useInput((_input, key) => {
54
+ if (key.escape || key.return) {
55
+ onDone()
56
+ }
57
+ })
58
+
59
+ return (
60
+ <Box flexDirection="column" padding={1}>
61
+ <Box marginBottom={1}>
62
+ <Text bold underline>
63
+ {page === 'status' ? '🄵 Friend VRM Desktop Pet' : 'Help'}
64
+ </Text>
65
+ </Box>
66
+
67
+ {page === 'status' && <FriendStatus prefs={getPrefs()} compact={false} />}
68
+
69
+ <Box marginTop={1} flexDirection="column">
70
+ <Text dimColor>Usage:</Text>
71
+ <Text dimColor> /friend Interactive status &amp; controls</Text>
72
+ <Text dimColor> /friend start Enable &amp; launch friend</Text>
73
+ <Text dimColor> /friend stop Disable friend</Text>
74
+ <Text dimColor> /friend status Quick status overview</Text>
75
+ </Box>
76
+
77
+ <Box marginTop={1}>
78
+ <Text dimColor>Press Esc or Enter to close.</Text>
79
+ </Box>
80
+ </Box>
81
+ )
82
+ }
83
+
84
+ function FriendStatus({
85
+ prefs,
86
+ compact,
87
+ }: {
88
+ prefs: ReturnType<typeof getPrefs>
89
+ compact: boolean
90
+ }) {
91
+ const statusDot = prefs.enabled ? '●' : '○'
92
+ const statusColor = prefs.enabled ? 'green' : 'gray'
93
+
94
+ return (
95
+ <Box flexDirection="column">
96
+ <Box>
97
+ <Text color={statusColor}>{statusDot} Status: </Text>
98
+ <Text bold color={statusColor}>
99
+ {prefs.enabled ? 'Enabled' : 'Disabled'}
100
+ </Text>
101
+ </Box>
102
+ {!compact && (
103
+ <>
104
+ <Box>
105
+ <Text> URL: </Text>
106
+ <Text dimColor>{FRIEND_URL}</Text>
107
+ </Box>
108
+ <Box>
109
+ <Text> Voice: </Text>
110
+ <Text dimColor>{prefs.voice ?? 'default'}</Text>
111
+ </Box>
112
+ <Box>
113
+ <Text> TTS: </Text>
114
+ <Text dimColor>{prefs.ttsEnabled ? 'on' : 'off'}</Text>
115
+ </Box>
116
+ <Box>
117
+ <Text> Tracking: </Text>
118
+ <Text dimColor>{prefs.tracking ?? 'mouse'}</Text>
119
+ </Box>
120
+ </>
121
+ )}
122
+ </Box>
123
+ )
124
+ }
src/commands/friend/index.ts ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Command } from '../../commands.js'
2
+
3
+ const friend = {
4
+ type: 'local-jsx',
5
+ name: 'friend',
6
+ description: 'Manage the VRM desktop pet companion — start/stop the 3D avatar window and adjust settings',
7
+ aliases: ['vrm'],
8
+ load: () => import('./friend.js'),
9
+ } satisfies Command
10
+
11
+ export default friend
src/tools/FriendEmotionTool.ts CHANGED
@@ -70,6 +70,11 @@ export const FriendEmotionTool = buildTool({
70
  `Always include mood_delta — it represents YOUR emotional reaction.`
71
  );
72
  },
 
 
 
 
 
73
  async call({ emotion, intensity, mood_delta }) {
74
  broadcastToVrm({ emotion, emotionIntensity: intensity });
75
 
 
70
  `Always include mood_delta — it represents YOUR emotional reaction.`
71
  );
72
  },
73
+ async prompt() {
74
+ return (
75
+ `FriendEmotionTool: set avatar emotion. Parameters: emotion (one of: ${VALID_EMOTIONS.join(', ')}), intensity (0-1, default 1), mood_delta (int -3..3) — call AFTER your textual reply.`
76
+ );
77
+ },
78
  async call({ emotion, intensity, mood_delta }) {
79
  broadcastToVrm({ emotion, emotionIntensity: intensity });
80
 
src/tools/FriendScreenObserveTool.ts CHANGED
@@ -93,6 +93,11 @@ export const FriendScreenObserveTool = buildTool({
93
  'Remember to call friend_emotion after your reply.'
94
  );
95
  },
 
 
 
 
 
96
  async call() {
97
  broadcastToVrm({ emotion: 'think', emotionIntensity: 0.7 });
98
 
 
93
  'Remember to call friend_emotion after your reply.'
94
  );
95
  },
96
+ async prompt() {
97
+ return (
98
+ 'FriendScreenObserveTool: capture a desktop screenshot and return the saved PNG path. After calling, use the read tool on the returned path to view it and then reply briefly as a companion (1-2 sentences). Do NOT mention that you saw a screenshot.'
99
+ );
100
+ },
101
  async call() {
102
  broadcastToVrm({ emotion: 'think', emotionIntensity: 0.7 });
103