beehaven 0.8.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +201 -0
  3. package/dist/__tests__/_cli-switch.js +105 -0
  4. package/dist/__tests__/agent-wrap-probe.js +136 -0
  5. package/dist/agent-attribution.js +192 -0
  6. package/dist/agent-identity-daemon.js +135 -0
  7. package/dist/agent-onboard.js +156 -0
  8. package/dist/agent-scope.js +79 -0
  9. package/dist/agent-sessions.js +370 -0
  10. package/dist/agent-state.js +482 -0
  11. package/dist/app-manifest.js +221 -0
  12. package/dist/app-signer.js +54 -0
  13. package/dist/app-signing.js +127 -0
  14. package/dist/audio-play.js +98 -0
  15. package/dist/avatar-protocol.js +25 -0
  16. package/dist/avatar-route.js +79 -0
  17. package/dist/bee/cloud-proxy.js +130 -0
  18. package/dist/blocked-actions.js +120 -0
  19. package/dist/build-channel.js +101 -0
  20. package/dist/buildings/daemon-workspace.js +475 -0
  21. package/dist/claude-bridge.js +310 -0
  22. package/dist/claude-credential-store.js +633 -0
  23. package/dist/clearly-sync.js +518 -0
  24. package/dist/clearly.js +616 -0
  25. package/dist/cli-state.js +334 -0
  26. package/dist/cli.js +6973 -0
  27. package/dist/commands/agent-skills.js +302 -0
  28. package/dist/commands/agent.js +1310 -0
  29. package/dist/commands/app.js +1123 -0
  30. package/dist/commands/auth.js +171 -0
  31. package/dist/commands/building-shell.js +392 -0
  32. package/dist/commands/canvas.js +932 -0
  33. package/dist/commands/claude.js +218 -0
  34. package/dist/commands/compliance.js +387 -0
  35. package/dist/commands/compose.js +507 -0
  36. package/dist/commands/composition.js +95 -0
  37. package/dist/commands/connect-session.js +409 -0
  38. package/dist/commands/conversations.js +160 -0
  39. package/dist/commands/costs.js +443 -0
  40. package/dist/commands/d1.js +253 -0
  41. package/dist/commands/daemon-cmd.js +162 -0
  42. package/dist/commands/deploy.js +164 -0
  43. package/dist/commands/design.js +617 -0
  44. package/dist/commands/dev-generate.js +125 -0
  45. package/dist/commands/dev.js +109 -0
  46. package/dist/commands/devices.js +135 -0
  47. package/dist/commands/explain-router.js +24 -0
  48. package/dist/commands/explain.js +882 -0
  49. package/dist/commands/explore.js +264 -0
  50. package/dist/commands/help.js +2418 -0
  51. package/dist/commands/init.js +408 -0
  52. package/dist/commands/install.js +129 -0
  53. package/dist/commands/library.js +229 -0
  54. package/dist/commands/marketplace.js +138 -0
  55. package/dist/commands/mcp-install.js +476 -0
  56. package/dist/commands/mention.js +257 -0
  57. package/dist/commands/omni.js +529 -0
  58. package/dist/commands/secrets.js +323 -0
  59. package/dist/commands/self-update.js +206 -0
  60. package/dist/commands/sheet.js +264 -0
  61. package/dist/commands/shell-output.js +309 -0
  62. package/dist/commands/shell.js +1048 -0
  63. package/dist/commands/shop.js +368 -0
  64. package/dist/commands/skills.js +311 -0
  65. package/dist/commands/slides.js +418 -0
  66. package/dist/commands/swarm.js +341 -0
  67. package/dist/commands/ticket.js +1301 -0
  68. package/dist/commands/tool.js +312 -0
  69. package/dist/commands/trash.js +106 -0
  70. package/dist/commands/treehouse.js +120 -0
  71. package/dist/commands/user.js +240 -0
  72. package/dist/commands/voice-tts.js +319 -0
  73. package/dist/commands/voice.js +166 -0
  74. package/dist/commands/watch-git.js +401 -0
  75. package/dist/commands/webhook.js +159 -0
  76. package/dist/commands/workspace.js +3354 -0
  77. package/dist/composition-repos.js +91 -0
  78. package/dist/daemon-avatar.js +130 -0
  79. package/dist/daemon-claude-code-auto-install.js +52 -0
  80. package/dist/daemon-client.js +363 -0
  81. package/dist/daemon-codex.js +349 -0
  82. package/dist/daemon-entry.js +101 -0
  83. package/dist/daemon-fetch-allowlist.js +194 -0
  84. package/dist/daemon-gemini.js +224 -0
  85. package/dist/daemon-mcp-clearly.js +353 -0
  86. package/dist/daemon-mcp-sandbox.js +127 -0
  87. package/dist/daemon-omni-company.js +338 -0
  88. package/dist/daemon-omni-hooks.js +375 -0
  89. package/dist/daemon-omni-identity.js +229 -0
  90. package/dist/daemon-omni-pipeline.js +251 -0
  91. package/dist/daemon-omni-policies.js +346 -0
  92. package/dist/daemon-omni-principal.js +580 -0
  93. package/dist/daemon-omni-tool-gate.js +116 -0
  94. package/dist/daemon-omni.js +1442 -0
  95. package/dist/daemon-path-transform.js +120 -0
  96. package/dist/daemon-write-sandbox.js +191 -0
  97. package/dist/daemon.js +4471 -0
  98. package/dist/dashboard.js +846 -0
  99. package/dist/dep-installer.js +286 -0
  100. package/dist/dev-mode.js +67 -0
  101. package/dist/doc-indexer.js +169 -0
  102. package/dist/e2e-crypto.js +94 -0
  103. package/dist/feature-flags.js +154 -0
  104. package/dist/file-browser.js +88 -0
  105. package/dist/first-party-hives.js +211 -0
  106. package/dist/folder-sync.js +226 -0
  107. package/dist/health.js +56 -0
  108. package/dist/ipc-auth.js +299 -0
  109. package/dist/ipc-client.js +495 -0
  110. package/dist/ipc-server.js +684 -0
  111. package/dist/keychain.js +224 -0
  112. package/dist/lib/admin-client.js +73 -0
  113. package/dist/lib/mobile-build-parser.js +156 -0
  114. package/dist/local-app-manager.js +660 -0
  115. package/dist/log-redaction.js +89 -0
  116. package/dist/logger.js +108 -0
  117. package/dist/manifest.js +143 -0
  118. package/dist/marketplace.js +793 -0
  119. package/dist/min-version-copy.js +51 -0
  120. package/dist/min-version.js +214 -0
  121. package/dist/public-surface.js +23 -0
  122. package/dist/relay-presets.js +89 -0
  123. package/dist/relay.js +2723 -0
  124. package/dist/repl.js +619 -0
  125. package/dist/setup-relay.js +363 -0
  126. package/dist/setup-service.js +536 -0
  127. package/dist/shared/bee-memory/index.js +6 -0
  128. package/dist/shared/bee-memory/types.js +682 -0
  129. package/dist/shared/claude-code/daemon-types.js +112 -0
  130. package/dist/shared/claude-code/index.js +2 -0
  131. package/dist/shared/claude-code/types.js +1 -0
  132. package/dist/shared/status/schema.js +133 -0
  133. package/dist/shell-session.js +198 -0
  134. package/dist/speak-lock.js +97 -0
  135. package/dist/spinner.js +57 -0
  136. package/dist/telemetry.js +166 -0
  137. package/dist/terminal/block-parser.js +207 -0
  138. package/dist/terminal/shell-integration.js +108 -0
  139. package/dist/transcript-tailer.js +215 -0
  140. package/dist/tts-qwen3.js +547 -0
  141. package/dist/types.js +4 -0
  142. package/dist/utm.js +38 -0
  143. package/dist/verb-surface.js +331 -0
  144. package/dist/version-utils.js +85 -0
  145. package/dist/watcher.js +207 -0
  146. package/package.json +88 -0
  147. package/python/qwen3_tts.py +230 -0
  148. package/scripts/stamp-build-channel.mjs +115 -0
  149. package/scripts/sync-installed.mjs +159 -0
  150. package/scripts/sync-skills.mjs +112 -0
  151. package/skills/README.md +24 -0
  152. package/skills/agent-doodle-space/SKILL.md +118 -0
  153. package/skills/agent-home/SKILL.md +98 -0
  154. package/skills/beehaven-cli/SKILL.md +144 -0
  155. package/skills/claude-shopper/README.md +90 -0
  156. package/skills/claude-shopper/SKILL.md +151 -0
  157. package/skills/clearly-agent/SKILL.md +183 -0
  158. package/skills/clearly-canvas/SKILL.md +222 -0
  159. package/skills/clearly-canvas/authoring.md +160 -0
  160. package/skills/clearly-canvas/communications.md +32 -0
  161. package/skills/clearly-canvas/composition-patterns.md +84 -0
  162. package/skills/clearly-canvas/example.mjs +78 -0
  163. package/skills/clearly-canvas/project-plans.md +156 -0
  164. package/skills/clearly-canvas/review.mjs +68 -0
  165. package/skills/clearly-workspace/SKILL.md +79 -0
  166. package/skills/clearly-workspace/survey.mjs +101 -0
  167. package/skills/codebase-map/SKILL.md +142 -0
  168. package/skills/design-craft/SKILL.md +300 -0
  169. package/skills/index.json +79 -0
  170. package/skills/layout-systems/SKILL.md +184 -0
  171. package/skills/pair-on-canvas/SKILL.md +92 -0
package/dist/repl.js ADDED
@@ -0,0 +1,619 @@
1
+ // ============================================================================
2
+ // Beehaven Dashboard — Terminal UI for the local agent
3
+ // Single-page dashboard using absolute cursor positioning.
4
+ // Layout: header → account/stats → activity log (fills space) → prompt at bottom.
5
+ // ============================================================================
6
+ import { createInterface } from 'readline';
7
+ import { readFileSync } from 'fs';
8
+ import { join, dirname } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { getRecentThoughts } from './bee/cloud-proxy.js';
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ function getVersion() {
13
+ try {
14
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
15
+ return pkg.version || '0.0.0';
16
+ }
17
+ catch {
18
+ return '0.0.0';
19
+ }
20
+ }
21
+ // ── ANSI helpers ──────────────────────────────────────────────────────────
22
+ const NO_COLOR = !!process.env.NO_COLOR;
23
+ const bold = (s) => (NO_COLOR ? s : `\x1b[1m${s}\x1b[0m`);
24
+ const dim = (s) => (NO_COLOR ? s : `\x1b[2m${s}\x1b[0m`);
25
+ const yellow = (s) => (NO_COLOR ? s : `\x1b[33m${s}\x1b[0m`);
26
+ const cyan = (s) => (NO_COLOR ? s : `\x1b[36m${s}\x1b[0m`);
27
+ const green = (s) => (NO_COLOR ? s : `\x1b[32m${s}\x1b[0m`);
28
+ const red = (s) => (NO_COLOR ? s : `\x1b[31m${s}\x1b[0m`);
29
+ // Cursor & screen control
30
+ const CSI = '\x1b[';
31
+ const altScreenEnter = `${CSI}?1049h`;
32
+ const altScreenExit = `${CSI}?1049l`;
33
+ const hideCursor = `${CSI}?25l`;
34
+ const showCursor = `${CSI}?25h`;
35
+ const clearLine = `${CSI}2K`;
36
+ const moveTo = (row, col) => `${CSI}${row};${col}H`;
37
+ // ── Event icons ───────────────────────────────────────────────────────────
38
+ function eventIcon(hookEvent, tool) {
39
+ if (hookEvent === 'SessionStart')
40
+ return '\u{1F680}';
41
+ if (hookEvent === 'SessionEnd')
42
+ return '\u{1F44B}';
43
+ if (hookEvent === 'UserPromptSubmit')
44
+ return '\u{1F4AC}';
45
+ if (hookEvent === 'SubagentStart')
46
+ return '\u{1F41D}';
47
+ if (hookEvent === 'SubagentStop')
48
+ return '\u{2705}';
49
+ if (hookEvent === 'PostToolUseFailure')
50
+ return '\u{274C}';
51
+ if (hookEvent === 'Stop')
52
+ return '\u{1F3A4}';
53
+ if (tool) {
54
+ if (tool === 'Read' || tool === 'Glob' || tool === 'Grep')
55
+ return '\u{1F4D6}';
56
+ if (tool === 'Edit' || tool === 'Write')
57
+ return '\u{270F}\u{FE0F}';
58
+ if (tool === 'Bash')
59
+ return '\u{26A1}';
60
+ if (tool === 'Task')
61
+ return '\u{1F4CB}';
62
+ if (tool === 'WebFetch' || tool === 'WebSearch')
63
+ return '\u{1F310}';
64
+ }
65
+ return '\u{25CF}';
66
+ }
67
+ // ── Utilities ─────────────────────────────────────────────────────────────
68
+ function timeAgo(ts) {
69
+ const seconds = Math.floor((Date.now() - ts) / 1000);
70
+ if (seconds < 5)
71
+ return 'just now';
72
+ if (seconds < 60)
73
+ return `${seconds}s ago`;
74
+ if (seconds < 3600)
75
+ return `${Math.floor(seconds / 60)}m ago`;
76
+ if (seconds < 86400)
77
+ return `${Math.floor(seconds / 3600)}h ago`;
78
+ return `${Math.floor(seconds / 86400)}d ago`;
79
+ }
80
+ /** Strip ANSI escape sequences to get visible character length */
81
+ function visibleLength(s) {
82
+ return s.replace(/\x1b\[[0-9;]*m/g, '').length;
83
+ }
84
+ /** Pad a string to target visible width, accounting for ANSI codes */
85
+ function padVisible(s, width) {
86
+ const visible = visibleLength(s);
87
+ if (visible >= width)
88
+ return s;
89
+ return s + ' '.repeat(width - visible);
90
+ }
91
+ /** Truncate to visible width, preserving ANSI codes */
92
+ function truncVisible(s, maxWidth) {
93
+ let visible = 0;
94
+ let result = '';
95
+ let inEscape = false;
96
+ for (const ch of s) {
97
+ if (ch === '\x1b') {
98
+ inEscape = true;
99
+ result += ch;
100
+ continue;
101
+ }
102
+ if (inEscape) {
103
+ result += ch;
104
+ if (ch === 'm')
105
+ inEscape = false;
106
+ continue;
107
+ }
108
+ if (visible >= maxWidth) {
109
+ result += '\x1b[0m';
110
+ break;
111
+ }
112
+ result += ch;
113
+ visible++;
114
+ }
115
+ return result;
116
+ }
117
+ // ============================================================================
118
+ // DaemonRepl — Single-Page Dashboard
119
+ // ============================================================================
120
+ export class DaemonRepl {
121
+ relay;
122
+ agent;
123
+ shutdownFn;
124
+ rl = null;
125
+ originalLog = console.log;
126
+ originalWarn = console.warn;
127
+ originalError = console.error;
128
+ active = false;
129
+ // Activity log ring buffer (keep last 200 entries)
130
+ activityLog = [];
131
+ static MAX_LOG_BUFFER = 200;
132
+ // Dashboard redraw throttle
133
+ redrawTimer = null;
134
+ static REDRAW_DELAY_MS = 100;
135
+ // Periodic refresh for live stats
136
+ refreshInterval = null;
137
+ // Command output mode — when true, dashboard is hidden and output scrolls
138
+ commandMode = false;
139
+ // Stats tracked locally from raw events
140
+ stats = {
141
+ toolCalls: 0,
142
+ filesRead: 0,
143
+ filesWritten: 0,
144
+ commandsRun: 0,
145
+ errors: 0,
146
+ sessionStartTime: null,
147
+ };
148
+ sessionActive = false;
149
+ constructor(relay, agent, shutdownFn) {
150
+ this.relay = relay;
151
+ this.agent = agent;
152
+ this.shutdownFn = shutdownFn;
153
+ }
154
+ /** Update stats from a raw event */
155
+ trackEvent(event) {
156
+ if (event.hook_event_name === 'SessionStart') {
157
+ this.sessionActive = true;
158
+ this.stats.sessionStartTime = event.timestamp;
159
+ }
160
+ if (event.hook_event_name === 'SessionEnd') {
161
+ this.sessionActive = false;
162
+ }
163
+ if (event.hook_event_name === 'PreToolUse' || event.hook_event_name === 'PostToolUse') {
164
+ this.stats.toolCalls++;
165
+ if (event.tool_name === 'Read' || event.tool_name === 'Glob' || event.tool_name === 'Grep')
166
+ this.stats.filesRead++;
167
+ if (event.tool_name === 'Edit' || event.tool_name === 'Write')
168
+ this.stats.filesWritten++;
169
+ if (event.tool_name === 'Bash')
170
+ this.stats.commandsRun++;
171
+ }
172
+ if (event.hook_event_name === 'PostToolUseFailure') {
173
+ this.stats.errors++;
174
+ }
175
+ }
176
+ /** Reset stats (e.g. on SessionEnd) */
177
+ resetStats() {
178
+ this.stats = {
179
+ toolCalls: 0,
180
+ filesRead: 0,
181
+ filesWritten: 0,
182
+ commandsRun: 0,
183
+ errors: 0,
184
+ sessionStartTime: null,
185
+ };
186
+ }
187
+ // ── Lifecycle ───────────────────────────────────────────────────────────
188
+ start() {
189
+ if (!process.stdin.isTTY) {
190
+ this.printFallbackBanner();
191
+ return;
192
+ }
193
+ this.active = true;
194
+ this.patchConsole();
195
+ // Enter alternate screen buffer (clean slate, restores on exit)
196
+ process.stdout.write(altScreenEnter + hideCursor);
197
+ this.rl = createInterface({
198
+ input: process.stdin,
199
+ output: process.stdout,
200
+ prompt: '',
201
+ terminal: true,
202
+ });
203
+ // Initial draw
204
+ this.drawDashboard();
205
+ this.rl.on('line', (input) => {
206
+ const trimmed = input.trim();
207
+ if (trimmed) {
208
+ this.commandMode = true;
209
+ this.handleCommand(trimmed);
210
+ this.commandMode = false;
211
+ }
212
+ this.drawDashboard();
213
+ });
214
+ this.rl.on('close', () => {
215
+ process.stdout.write(showCursor + altScreenExit);
216
+ this.shutdownFn();
217
+ });
218
+ // Refresh dashboard every 5s for live stats
219
+ this.refreshInterval = setInterval(() => {
220
+ if (!this.commandMode)
221
+ this.scheduleRedraw();
222
+ }, 5000);
223
+ // Redraw on terminal resize
224
+ process.stdout.on('resize', () => {
225
+ if (!this.commandMode)
226
+ this.drawDashboard();
227
+ });
228
+ }
229
+ stop() {
230
+ this.active = false;
231
+ if (this.refreshInterval)
232
+ clearInterval(this.refreshInterval);
233
+ if (this.redrawTimer)
234
+ clearTimeout(this.redrawTimer);
235
+ this.restoreConsole();
236
+ process.stdout.write(showCursor + altScreenExit);
237
+ if (this.rl) {
238
+ this.rl.close();
239
+ this.rl = null;
240
+ }
241
+ }
242
+ // ── Dashboard Drawing ───────────────────────────────────────────────────
243
+ getTermWidth() {
244
+ return process.stdout.columns || 80;
245
+ }
246
+ getTermHeight() {
247
+ return process.stdout.rows || 24;
248
+ }
249
+ drawDashboard() {
250
+ if (!this.active || !this.rl)
251
+ return;
252
+ const w = this.getTermWidth();
253
+ const h = this.getTermHeight();
254
+ const version = getVersion();
255
+ const profile = this.relay.getProfile();
256
+ const syncStatus = this.relay.getSyncStatus();
257
+ const connected = this.relay.isConnected();
258
+ const topLines = [];
259
+ // ── Header ──────────────────────────────────────────────────────────
260
+ const title = `${yellow('\u{1F41D}')} ${bold('Beehaven')} ${dim(`v${version}`)}`;
261
+ const relayDot = connected ? green('\u{25CF}') : red('\u{25CF}');
262
+ const relayLabel = connected ? dim(' connected') : dim(' offline');
263
+ const headerRight = `${relayDot}${relayLabel}`;
264
+ const titleLen = visibleLength(title);
265
+ const rightLen = visibleLength(headerRight);
266
+ const headerPad = Math.max(1, w - 4 - titleLen - rightLen);
267
+ topLines.push(` ${title}${' '.repeat(headerPad)}${headerRight}`);
268
+ topLines.push(` ${'━'.repeat(Math.min(w - 4, 76))}`);
269
+ // ── Two-column: Account | Session ──────────────────────────────────
270
+ const colW = Math.floor((Math.min(w, 80) - 7) / 2);
271
+ const sep = dim(' \u{2502} ');
272
+ const left = [];
273
+ if (profile) {
274
+ const nameLabel = profile.username
275
+ ? `${profile.displayName} ${yellow(`@${profile.username}`)}`
276
+ : profile.displayName;
277
+ left.push(nameLabel);
278
+ if (profile.email)
279
+ left.push(dim(profile.email));
280
+ const planLabel = profile.subscriptionPlan || 'free';
281
+ const statusSuffix = profile.subscriptionStatus ? ` (${profile.subscriptionStatus})` : '';
282
+ left.push(cyan(planLabel + statusSuffix));
283
+ }
284
+ else {
285
+ left.push(dim('Not connected'));
286
+ }
287
+ const right = [];
288
+ const sessionLabel = this.sessionActive ? green('active') : dim('idle');
289
+ right.push(`Session: ${sessionLabel}`);
290
+ const statsLine = `${dim('Tools:')} ${this.stats.toolCalls} ${dim('Files:')} ${this.stats.filesRead + this.stats.filesWritten} ${dim('Errs:')} ${this.stats.errors > 0 ? red(String(this.stats.errors)) : '0'}`;
291
+ right.push(statsLine);
292
+ const syncLine = connected
293
+ ? `${dim('Sync:')} ${syncStatus.sent} sent${syncStatus.lastSyncAt ? dim(` \u{2022} ${timeAgo(syncStatus.lastSyncAt)}`) : ''}`
294
+ : dim('Sync: offline');
295
+ right.push(syncLine);
296
+ const maxCols = Math.max(left.length, right.length);
297
+ for (let i = 0; i < maxCols; i++) {
298
+ const l = padVisible(i < left.length ? left[i] : '', colW);
299
+ const r = i < right.length ? right[i] : '';
300
+ topLines.push(` ${truncVisible(l, colW)}${sep}${truncVisible(r, colW)}`);
301
+ }
302
+ // ── Activity log header ────────────────────────────────────────────
303
+ topLines.push(` ${'─'.repeat(Math.min(w - 4, 76))}`);
304
+ topLines.push(` ${bold('Activity')}`);
305
+ // ── Calculate layout ────────────────────────────────────────────────
306
+ const bottomRows = 4;
307
+ const topRowCount = topLines.length;
308
+ const logAreaRows = Math.max(1, h - topRowCount - bottomRows);
309
+ // ── Build output buffer with absolute positioning ───────────────────
310
+ let buf = hideCursor;
311
+ let row = 1;
312
+ for (const line of topLines) {
313
+ buf += `${moveTo(row, 1)}${clearLine}${truncVisible(line, w)}`;
314
+ row++;
315
+ }
316
+ const visibleLogs = this.activityLog.slice(-logAreaRows);
317
+ const emptyAbove = logAreaRows - visibleLogs.length;
318
+ for (let i = 0; i < emptyAbove; i++) {
319
+ if (i === Math.floor(emptyAbove / 2) && visibleLogs.length === 0) {
320
+ buf += `${moveTo(row, 1)}${clearLine} ${dim('Waiting for Claude Code events...')}`;
321
+ }
322
+ else {
323
+ buf += `${moveTo(row, 1)}${clearLine}`;
324
+ }
325
+ row++;
326
+ }
327
+ for (const entry of visibleLogs) {
328
+ const proj = entry.project ? dim(`[${entry.project}]`) + ' ' : '';
329
+ const logLine = ` ${entry.icon} ${dim(entry.time)} ${proj}${entry.detail}`;
330
+ buf += `${moveTo(row, 1)}${clearLine}${truncVisible(logLine, w)}`;
331
+ row++;
332
+ }
333
+ // ── Footer ──────────────────────────────────────────────────────────
334
+ const footerRow = h - 3;
335
+ buf += `${moveTo(footerRow, 1)}${clearLine} ${'─'.repeat(Math.min(w - 4, 76))}`;
336
+ // Bee status bar
337
+ const bs = this.agent.getStatus();
338
+ const emotionIcons = {
339
+ idle: '\u{1F634}',
340
+ listening: '\u{1F442}',
341
+ thinking: '\u{1F914}',
342
+ speaking: '\u{1F5E3}',
343
+ working: '\u{1F528}',
344
+ happy: '\u{1F60A}',
345
+ sleep: '\u{1F4A4}',
346
+ };
347
+ const eIcon = emotionIcons[bs.emotion] || '\u{1F41D}';
348
+ const moodBar = this.buildMiniBar(bs.moodScore);
349
+ const nrgBar = bs.energy >= 0 ? this.buildMiniBar(bs.energy) : dim('cloud');
350
+ const llmTag = bs.llmProvider === 'cloud'
351
+ ? dim('cloud')
352
+ : bs.llmConfigured
353
+ ? { openrouter: 'or', anthropic: 'an', openai: 'oi', openclaw: 'oc', custom: 'cu' }[bs.llmProvider] || bs.llmProvider.slice(0, 2)
354
+ : '--';
355
+ const llmCost = bs.llmProvider === 'cloud' ? '' : bs.llmConfigured ? `/$${bs.llmDailyCost.toFixed(2)}` : '';
356
+ const bsLine = ` ${eIcon} ${dim('mood')}${moodBar} ${dim('nrg')}${nrgBar} ${dim('phase:')}${bs.rhythmPhase} ${dim('soul:v')}${bs.soulVersion} ${dim('learn:')}${bs.learnings} ${dim('llm:')}${llmTag}${llmCost}`;
357
+ buf += `${moveTo(h - 2, 1)}${clearLine}${truncVisible(bsLine, w)}`;
358
+ const helpHint = dim(`"help" for commands \u{2022} "quit" to stop`);
359
+ buf += `${moveTo(h - 1, 1)}${clearLine} ${helpHint}`;
360
+ // ── Prompt line ─────────────────────────────────────────────────────
361
+ const promptText = NO_COLOR ? 'beehaven> ' : `\x1b[33mbeehaven>\x1b[0m `;
362
+ buf += `${moveTo(h, 1)}${clearLine}${promptText}${showCursor}`;
363
+ process.stdout.write(buf);
364
+ }
365
+ scheduleRedraw() {
366
+ if (this.redrawTimer || this.commandMode)
367
+ return;
368
+ this.redrawTimer = setTimeout(() => {
369
+ this.redrawTimer = null;
370
+ if (this.rl && this.active && !this.commandMode) {
371
+ this.drawDashboard();
372
+ }
373
+ }, DaemonRepl.REDRAW_DELAY_MS);
374
+ }
375
+ printFallbackBanner() {
376
+ const version = getVersion();
377
+ const profile = this.relay.getProfile();
378
+ console.log('');
379
+ console.log(` \u{1F41D} Beehaven Daemon v${version}`);
380
+ console.log(` ${'━'.repeat(30)}`);
381
+ if (profile) {
382
+ console.log(` Account: ${profile.displayName}${profile.username ? ` (@${profile.username})` : ''}`);
383
+ console.log(` Plan: ${profile.subscriptionPlan}`);
384
+ }
385
+ console.log(` Relay: ${this.relay.isConnected() ? 'connected' : 'disconnected'}`);
386
+ console.log('');
387
+ }
388
+ // ── Live Event Display ──────────────────────────────────────────────────
389
+ logEvent(event) {
390
+ const icon = eventIcon(event.hook_event_name, event.tool_name);
391
+ const time = new Date().toLocaleTimeString('en-US', {
392
+ hour12: false,
393
+ hour: '2-digit',
394
+ minute: '2-digit',
395
+ second: '2-digit',
396
+ });
397
+ const project = event.cwd?.replace(/\/+$/, '').split('/').pop() || '';
398
+ let detail = event.hook_event_name;
399
+ if (event.tool_name) {
400
+ detail = event.tool_name;
401
+ if (event.tool_input) {
402
+ if ('file_path' in event.tool_input) {
403
+ detail += `: ${String(event.tool_input.file_path).split('/').pop()}`;
404
+ }
405
+ else if ('command' in event.tool_input) {
406
+ const cmd = String(event.tool_input.command);
407
+ detail += `: ${cmd.length > 50 ? cmd.slice(0, 50) + '...' : cmd}`;
408
+ }
409
+ else if ('pattern' in event.tool_input) {
410
+ detail += `: ${event.tool_input.pattern}`;
411
+ }
412
+ }
413
+ }
414
+ this.activityLog.push({ time, icon, project, detail });
415
+ if (this.activityLog.length > DaemonRepl.MAX_LOG_BUFFER) {
416
+ this.activityLog = this.activityLog.slice(-Math.floor(DaemonRepl.MAX_LOG_BUFFER / 2));
417
+ }
418
+ this.scheduleRedraw();
419
+ }
420
+ logProfileUpdate(profile) {
421
+ const time = new Date().toLocaleTimeString('en-US', {
422
+ hour12: false,
423
+ hour: '2-digit',
424
+ minute: '2-digit',
425
+ second: '2-digit',
426
+ });
427
+ this.activityLog.push({
428
+ time,
429
+ icon: '\u{1F41D}',
430
+ project: '',
431
+ detail: `Account linked: ${bold(profile.displayName)} (${cyan(profile.subscriptionPlan)})`,
432
+ });
433
+ this.scheduleRedraw();
434
+ }
435
+ // ── Console Patching ────────────────────────────────────────────────────
436
+ patchConsole() {
437
+ this.originalLog = console.log.bind(console);
438
+ this.originalWarn = console.warn.bind(console);
439
+ this.originalError = console.error.bind(console);
440
+ const suppressUnlessCommand = (original) => {
441
+ return (...args) => {
442
+ if (this.commandMode) {
443
+ original(...args);
444
+ }
445
+ };
446
+ };
447
+ console.log = suppressUnlessCommand(this.originalLog);
448
+ console.warn = suppressUnlessCommand(this.originalWarn);
449
+ console.error = suppressUnlessCommand(this.originalError);
450
+ }
451
+ restoreConsole() {
452
+ console.log = this.originalLog;
453
+ console.warn = this.originalWarn;
454
+ console.error = this.originalError;
455
+ }
456
+ // ── Command Dispatch ────────────────────────────────────────────────────
457
+ handleCommand(input) {
458
+ const [cmd] = input.split(/\s+/);
459
+ if (!cmd)
460
+ return;
461
+ const parts = input.split(/\s+/);
462
+ switch (cmd.toLowerCase()) {
463
+ case 'status':
464
+ this.cmdStatus();
465
+ break;
466
+ case 'stats':
467
+ this.cmdStats();
468
+ break;
469
+ case 'events':
470
+ this.cmdEvents();
471
+ break;
472
+ case 'sync':
473
+ this.cmdSync();
474
+ break;
475
+ case 'bee':
476
+ this.cmdBee(parts.slice(1));
477
+ break;
478
+ case 'help':
479
+ this.cmdHelp();
480
+ break;
481
+ case 'quit':
482
+ case 'exit':
483
+ process.stdout.write(showCursor + altScreenExit);
484
+ this.shutdownFn();
485
+ break;
486
+ default:
487
+ this.addSystemLog(`Unknown command: ${cmd}. Type "help" for commands.`);
488
+ }
489
+ }
490
+ addSystemLog(msg) {
491
+ const time = new Date().toLocaleTimeString('en-US', {
492
+ hour12: false,
493
+ hour: '2-digit',
494
+ minute: '2-digit',
495
+ second: '2-digit',
496
+ });
497
+ this.activityLog.push({ time, icon: '\u{25B6}', project: '', detail: msg });
498
+ }
499
+ // ── Commands ────────────────────────────────────────────────────────────
500
+ cmdStatus() {
501
+ const profile = this.relay.getProfile();
502
+ const syncStatus = this.relay.getSyncStatus();
503
+ const connected = this.relay.isConnected();
504
+ if (profile) {
505
+ this.addSystemLog(`${bold('Account:')} ${cyan(profile.displayName)}${profile.username ? ` ${yellow(`@${profile.username}`)}` : ''}${profile.email ? ` ${dim(profile.email)}` : ''}`);
506
+ const planLabel = profile.subscriptionPlan || 'free';
507
+ const statusLabel = profile.subscriptionStatus ? ` (${profile.subscriptionStatus})` : '';
508
+ this.addSystemLog(`${bold('Plan:')} ${cyan(planLabel + statusLabel)}`);
509
+ }
510
+ else {
511
+ this.addSystemLog(`${bold('Account:')} ${dim('Not connected')}`);
512
+ }
513
+ this.addSystemLog(`${bold('Relay:')} ${connected ? green('connected') + dim(` (${this.relay.getTier()})`) : red('disconnected')} ${dim('\u{2022}')} ${bold('Sync:')} ${syncStatus.sent} sent`);
514
+ this.addSystemLog(`${bold('Session:')} ${this.sessionActive ? green('active') : dim('idle')}`);
515
+ }
516
+ cmdStats() {
517
+ this.addSystemLog(`${bold('Tools:')} ${this.stats.toolCalls} ${bold('Read:')} ${this.stats.filesRead} ${bold('Write:')} ${this.stats.filesWritten} ${bold('Bash:')} ${this.stats.commandsRun} ${bold('Errors:')} ${this.stats.errors > 0 ? red(String(this.stats.errors)) : '0'}`);
518
+ }
519
+ cmdEvents() {
520
+ const recent = this.activityLog.slice(-10);
521
+ if (recent.length === 0) {
522
+ this.addSystemLog('No events yet. Start a Claude Code session.');
523
+ return;
524
+ }
525
+ for (const e of recent) {
526
+ const proj = e.project ? `[${e.project}] ` : '';
527
+ this.addSystemLog(`${e.icon} ${dim(e.time)} ${dim(proj)}${e.detail}`);
528
+ }
529
+ }
530
+ cmdSync() {
531
+ const status = this.relay.getSyncStatus();
532
+ this.addSystemLog(`${bold('Connected:')} ${status.connected ? green('yes') : red('no')} ${dim('\u{2022}')} ${bold('Sent:')} ${status.sent} ${dim('\u{2022}')} ${bold('Failed:')} ${status.failed > 0 ? red(String(status.failed)) : '0'}`);
533
+ }
534
+ cmdBee(args) {
535
+ const sub = args[0]?.toLowerCase();
536
+ if (!sub) {
537
+ const s = this.agent.getStatus();
538
+ const nrg = s.energy >= 0 ? String(s.energy) : 'cloud';
539
+ const hun = s.hunger >= 0 ? String(s.hunger) : 'cloud';
540
+ const fun = s.fun >= 0 ? String(s.fun) : 'cloud';
541
+ const soc = s.social >= 0 ? String(s.social) : 'cloud';
542
+ this.addSystemLog(`${bold('Emotion:')} ${s.emotion} ${dim('\u{2022}')} ${bold('Mood:')} ${s.mood} (${s.moodScore}) ${dim('\u{2022}')} ${bold('Energy:')} ${nrg} ${dim('\u{2022}')} ${bold('Hunger:')} ${hun}`);
543
+ this.addSystemLog(`${bold('Fun:')} ${fun} ${dim('\u{2022}')} ${bold('Social:')} ${soc} ${dim('\u{2022}')} ${bold('Rhythm:')} ${s.rhythmPhase} ${dim('\u{2022}')} ${bold('Soul:')} v${s.soulVersion}`);
544
+ this.addSystemLog(`${bold('Learnings:')} ${s.learnings} ${dim('\u{2022}')} ${bold('Patterns:')} ${s.patterns}`);
545
+ this.addSystemLog(`${bold('LLM:')} ${green('cloud-managed')}`);
546
+ return;
547
+ }
548
+ switch (sub) {
549
+ case 'think': {
550
+ this.addSystemLog('Requesting reflection from cloud...');
551
+ this.agent.forceReflect().catch((err) => {
552
+ this.addSystemLog(red(`Reflection failed: ${err?.message?.slice(0, 80) || err}`));
553
+ this.scheduleRedraw();
554
+ });
555
+ break;
556
+ }
557
+ case 'config': {
558
+ this.addSystemLog(`${bold('Provider:')} ${green('cloud')} ${dim('\u{2022}')} LLM is managed by the Clearly cloud`);
559
+ break;
560
+ }
561
+ case 'thoughts': {
562
+ const thoughts = getRecentThoughts(5);
563
+ if (thoughts.length === 0) {
564
+ this.addSystemLog(dim('No thoughts yet. Set an LLM API key to enable thinking.'));
565
+ }
566
+ else {
567
+ for (const t of thoughts) {
568
+ this.addSystemLog(dim(t));
569
+ }
570
+ }
571
+ break;
572
+ }
573
+ case 'cost': {
574
+ const usage = this.agent.llm.getUsage();
575
+ this.addSystemLog(`${bold('Today:')} ${usage.callsThisHour} calls this hour ${dim('\u{2022}')} $${usage.dailyCostUsd.toFixed(4)} spent ${dim('\u{2022}')} ${usage.totalCalls} total calls`);
576
+ break;
577
+ }
578
+ default:
579
+ this.addSystemLog(`${bold('bee')} subcommands: ${dim('think, config, thoughts, cost')} (or just "bee" for status)`);
580
+ }
581
+ }
582
+ cmdHelp() {
583
+ this.addSystemLog(`${bold('Commands:')} status, stats, events, sync, help, quit`);
584
+ this.addSystemLog(`${bold('Bee:')} bee, bee think, bee config [set key val], bee thoughts, bee cost`);
585
+ }
586
+ // ── Bee Agent Integration ───────────────────────────────────────────────
587
+ logBeeThought(thought) {
588
+ const time = new Date().toLocaleTimeString('en-US', {
589
+ hour12: false,
590
+ hour: '2-digit',
591
+ minute: '2-digit',
592
+ second: '2-digit',
593
+ });
594
+ const typeIcon = {
595
+ observation: '\u{1F41D}',
596
+ reaction: '\u{1F4AD}',
597
+ curiosity: '\u{2753}',
598
+ help: '\u{26A0}',
599
+ idle: '\u{1F4A4}',
600
+ };
601
+ const icon = typeIcon[thought.type] || '\u{1F41D}';
602
+ this.activityLog.push({
603
+ time,
604
+ icon,
605
+ project: '',
606
+ detail: dim(`[bee] ${thought.content}`),
607
+ });
608
+ if (this.activityLog.length > DaemonRepl.MAX_LOG_BUFFER) {
609
+ this.activityLog = this.activityLog.slice(-DaemonRepl.MAX_LOG_BUFFER + 50);
610
+ }
611
+ this.scheduleRedraw();
612
+ }
613
+ buildMiniBar(value) {
614
+ const filled = Math.round(value / 20);
615
+ const empty = 5 - filled;
616
+ const color = value > 60 ? green : value > 30 ? yellow : red;
617
+ return color('\u{2588}'.repeat(filled)) + dim('\u{2591}'.repeat(empty));
618
+ }
619
+ }