bobo-ai-cli 2.1.0 → 3.0.1

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 (260) hide show
  1. package/README.md +52 -9
  2. package/bundled-skills/CORE_SKILLS.txt +18 -0
  3. package/dist/agent.js +91 -51
  4. package/dist/agent.js.map +1 -1
  5. package/dist/agents/catalog.d.ts +40 -0
  6. package/dist/agents/catalog.js +172 -0
  7. package/dist/agents/catalog.js.map +1 -0
  8. package/dist/agents/index.d.ts +6 -0
  9. package/dist/agents/index.js +4 -0
  10. package/dist/agents/index.js.map +1 -0
  11. package/dist/agents/router.d.ts +43 -0
  12. package/dist/agents/router.js +87 -0
  13. package/dist/agents/router.js.map +1 -0
  14. package/dist/agents/spawn.d.ts +46 -0
  15. package/dist/agents/spawn.js +91 -0
  16. package/dist/agents/spawn.js.map +1 -0
  17. package/dist/autonomous.js +41 -1
  18. package/dist/autonomous.js.map +1 -1
  19. package/dist/cli.d.ts +8 -0
  20. package/dist/cli.js +669 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/compactor.d.ts +49 -4
  23. package/dist/compactor.js +164 -17
  24. package/dist/compactor.js.map +1 -1
  25. package/dist/completer.js +2 -0
  26. package/dist/completer.js.map +1 -1
  27. package/dist/config.js +3 -2
  28. package/dist/config.js.map +1 -1
  29. package/dist/cost-tracker.js +36 -2
  30. package/dist/cost-tracker.js.map +1 -1
  31. package/dist/dream.d.ts +42 -0
  32. package/dist/dream.js +324 -0
  33. package/dist/dream.js.map +1 -0
  34. package/dist/hooks.d.ts +1 -1
  35. package/dist/hooks.js +7 -2
  36. package/dist/hooks.js.map +1 -1
  37. package/dist/index.js +10 -1136
  38. package/dist/index.js.map +1 -1
  39. package/dist/insight.js +4 -11
  40. package/dist/insight.js.map +1 -1
  41. package/dist/knowledge.d.ts +13 -0
  42. package/dist/knowledge.js +16 -4
  43. package/dist/knowledge.js.map +1 -1
  44. package/dist/mcp-client.js +59 -41
  45. package/dist/mcp-client.js.map +1 -1
  46. package/dist/memory.d.ts +4 -0
  47. package/dist/memory.js +30 -18
  48. package/dist/memory.js.map +1 -1
  49. package/dist/project.js +2 -1
  50. package/dist/project.js.map +1 -1
  51. package/dist/providers.js +1 -1
  52. package/dist/providers.js.map +1 -1
  53. package/dist/repl.d.ts +16 -0
  54. package/dist/repl.js +704 -0
  55. package/dist/repl.js.map +1 -0
  56. package/dist/sessions.js +59 -3
  57. package/dist/sessions.js.map +1 -1
  58. package/dist/skill-router.js +2 -2
  59. package/dist/skill-router.js.map +1 -1
  60. package/dist/skills/composer.d.ts +18 -0
  61. package/dist/skills/composer.js +59 -0
  62. package/dist/skills/composer.js.map +1 -0
  63. package/dist/skills/index.d.ts +3 -0
  64. package/dist/skills/index.js +3 -0
  65. package/dist/skills/index.js.map +1 -0
  66. package/dist/skills/loader.d.ts +12 -0
  67. package/dist/skills/loader.js +150 -0
  68. package/dist/skills/loader.js.map +1 -0
  69. package/dist/skills/types.d.ts +28 -0
  70. package/dist/skills/types.js +9 -0
  71. package/dist/skills/types.js.map +1 -0
  72. package/dist/skills.d.ts +1 -0
  73. package/dist/skills.js +10 -8
  74. package/dist/skills.js.map +1 -1
  75. package/dist/state/artifacts.d.ts +71 -0
  76. package/dist/state/artifacts.js +133 -0
  77. package/dist/state/artifacts.js.map +1 -0
  78. package/dist/state/index.d.ts +9 -0
  79. package/dist/state/index.js +7 -0
  80. package/dist/state/index.js.map +1 -0
  81. package/dist/state/manager.d.ts +89 -0
  82. package/dist/state/manager.js +132 -0
  83. package/dist/state/manager.js.map +1 -0
  84. package/dist/state/project-memory.d.ts +24 -0
  85. package/dist/state/project-memory.js +83 -0
  86. package/dist/state/project-memory.js.map +1 -0
  87. package/dist/state/recovery.d.ts +24 -0
  88. package/dist/state/recovery.js +95 -0
  89. package/dist/state/recovery.js.map +1 -0
  90. package/dist/statusbar.d.ts +1 -0
  91. package/dist/statusbar.js +12 -1
  92. package/dist/statusbar.js.map +1 -1
  93. package/dist/structured/loader.js +4 -2
  94. package/dist/structured/loader.js.map +1 -1
  95. package/dist/sub-agent-runner.d.ts +1 -0
  96. package/dist/sub-agent-runner.js +32 -5
  97. package/dist/sub-agent-runner.js.map +1 -1
  98. package/dist/sub-agents.d.ts +19 -1
  99. package/dist/sub-agents.js +142 -5
  100. package/dist/sub-agents.js.map +1 -1
  101. package/dist/tool-governance.d.ts +77 -0
  102. package/dist/tool-governance.js +366 -0
  103. package/dist/tool-governance.js.map +1 -0
  104. package/dist/tools/advanced.js +3 -2
  105. package/dist/tools/advanced.js.map +1 -1
  106. package/dist/tools/browser.js +2 -2
  107. package/dist/tools/browser.js.map +1 -1
  108. package/dist/tools/claude-code.js +2 -2
  109. package/dist/tools/claude-code.js.map +1 -1
  110. package/dist/tools/index.js +36 -12
  111. package/dist/tools/index.js.map +1 -1
  112. package/dist/tools/process-manager.js +2 -2
  113. package/dist/tools/process-manager.js.map +1 -1
  114. package/dist/ui/hud.d.ts +25 -0
  115. package/dist/ui/hud.js +67 -0
  116. package/dist/ui/hud.js.map +1 -0
  117. package/dist/verification-agent.d.ts +46 -0
  118. package/dist/verification-agent.js +533 -0
  119. package/dist/verification-agent.js.map +1 -0
  120. package/dist/watcher.js +2 -2
  121. package/dist/watcher.js.map +1 -1
  122. package/dist/web.js +1 -1
  123. package/dist/web.js.map +1 -1
  124. package/dist/workflows/ask.d.ts +13 -0
  125. package/dist/workflows/ask.js +66 -0
  126. package/dist/workflows/ask.js.map +1 -0
  127. package/dist/workflows/index.d.ts +5 -0
  128. package/dist/workflows/index.js +6 -0
  129. package/dist/workflows/index.js.map +1 -0
  130. package/dist/workflows/interview.d.ts +11 -0
  131. package/dist/workflows/interview.js +36 -0
  132. package/dist/workflows/interview.js.map +1 -0
  133. package/dist/workflows/plan.d.ts +13 -0
  134. package/dist/workflows/plan.js +34 -0
  135. package/dist/workflows/plan.js.map +1 -0
  136. package/dist/workflows/team.d.ts +17 -0
  137. package/dist/workflows/team.js +86 -0
  138. package/dist/workflows/team.js.map +1 -0
  139. package/dist/workflows/verify.d.ts +11 -0
  140. package/dist/workflows/verify.js +21 -0
  141. package/dist/workflows/verify.js.map +1 -0
  142. package/package.json +18 -5
  143. package/bundled-skills/Skill_Seekers/SKILL.md +0 -1722
  144. package/bundled-skills/ab-test-setup/SKILL.md +0 -557
  145. package/bundled-skills/adversarial-verification/SKILL.md +0 -95
  146. package/bundled-skills/agent-sdk-dev/SKILL.md +0 -238
  147. package/bundled-skills/agent-tools/SKILL.md +0 -136
  148. package/bundled-skills/analytics-tracking/SKILL.md +0 -597
  149. package/bundled-skills/artifacts-builder/SKILL.md +0 -89
  150. package/bundled-skills/asana/SKILL.md +0 -13
  151. package/bundled-skills/brand-voice/SKILL.md +0 -481
  152. package/bundled-skills/browser-use/SKILL.md +0 -419
  153. package/bundled-skills/cache-optimization-skill/SKILL.md +0 -179
  154. package/bundled-skills/canvas-design/SKILL.md +0 -147
  155. package/bundled-skills/citation-validator/SKILL.md +0 -203
  156. package/bundled-skills/clangd-lsp/SKILL.md +0 -52
  157. package/bundled-skills/code-simplifier/SKILL.md +0 -13
  158. package/bundled-skills/commit-commands/SKILL.md +0 -258
  159. package/bundled-skills/competitor-alternatives/SKILL.md +0 -795
  160. package/bundled-skills/content-atomizer/SKILL.md +0 -910
  161. package/bundled-skills/content-research-writer/SKILL.md +0 -605
  162. package/bundled-skills/context7/SKILL.md +0 -13
  163. package/bundled-skills/copy-editing/SKILL.md +0 -494
  164. package/bundled-skills/copywriting/SKILL.md +0 -510
  165. package/bundled-skills/csharp-lsp/SKILL.md +0 -40
  166. package/bundled-skills/decision-making-framework/SKILL.md +0 -154
  167. package/bundled-skills/deep-research/SKILL.md +0 -236
  168. package/bundled-skills/developer-growth-analysis/SKILL.md +0 -335
  169. package/bundled-skills/direct-response-copy/SKILL.md +0 -2336
  170. package/bundled-skills/docker-expert/SKILL.md +0 -229
  171. package/bundled-skills/document-skills/SKILL.md +0 -13
  172. package/bundled-skills/documentation-expert/SKILL.md +0 -126
  173. package/bundled-skills/email-sequence/SKILL.md +0 -1061
  174. package/bundled-skills/email-sequences/SKILL.md +0 -910
  175. package/bundled-skills/example-plugin/SKILL.md +0 -72
  176. package/bundled-skills/explanatory-output-style/SKILL.md +0 -82
  177. package/bundled-skills/feature-dev/SKILL.md +0 -458
  178. package/bundled-skills/file-organizer/SKILL.md +0 -466
  179. package/bundled-skills/firebase.disabled/SKILL.md +0 -13
  180. package/bundled-skills/form-cro/SKILL.md +0 -488
  181. package/bundled-skills/free-tool-strategy/SKILL.md +0 -636
  182. package/bundled-skills/frontend-design/SKILL.md +0 -41
  183. package/bundled-skills/frontend-design-offical/SKILL.md +0 -55
  184. package/bundled-skills/gitlab/SKILL.md +0 -13
  185. package/bundled-skills/gopls-lsp/SKILL.md +0 -32
  186. package/bundled-skills/got-controller/SKILL.md +0 -218
  187. package/bundled-skills/greptile/SKILL.md +0 -72
  188. package/bundled-skills/hookify/SKILL.md +0 -376
  189. package/bundled-skills/image-editor/SKILL.md +0 -189
  190. package/bundled-skills/image-enhancer/SKILL.md +0 -109
  191. package/bundled-skills/jdtls-lsp/SKILL.md +0 -49
  192. package/bundled-skills/json-canvas/SKILL.md +0 -654
  193. package/bundled-skills/keyword-research/SKILL.md +0 -559
  194. package/bundled-skills/kotlin-lsp/SKILL.md +0 -28
  195. package/bundled-skills/laravel-boost/SKILL.md +0 -13
  196. package/bundled-skills/launch-strategy/SKILL.md +0 -394
  197. package/bundled-skills/lead-magnet/SKILL.md +0 -393
  198. package/bundled-skills/learning-output-style/SKILL.md +0 -106
  199. package/bundled-skills/linear/SKILL.md +0 -13
  200. package/bundled-skills/lua-lsp/SKILL.md +0 -47
  201. package/bundled-skills/marketing-ideas/SKILL.md +0 -720
  202. package/bundled-skills/marketing-psychology/SKILL.md +0 -534
  203. package/bundled-skills/mcp-builder/SKILL.md +0 -369
  204. package/bundled-skills/meeting-insights-analyzer/SKILL.md +0 -347
  205. package/bundled-skills/memory-evolution-system/SKILL.md +0 -172
  206. package/bundled-skills/multi-lens-thinking/SKILL.md +0 -407
  207. package/bundled-skills/nano-banana-pro/SKILL.md +0 -116
  208. package/bundled-skills/newsletter/SKILL.md +0 -736
  209. package/bundled-skills/notebooklm/SKILL.md +0 -296
  210. package/bundled-skills/obsidian-bases/SKILL.md +0 -634
  211. package/bundled-skills/obsidian-markdown/SKILL.md +0 -651
  212. package/bundled-skills/onboarding-cro/SKILL.md +0 -494
  213. package/bundled-skills/page-cro/SKILL.md +0 -379
  214. package/bundled-skills/paid-ads/SKILL.md +0 -624
  215. package/bundled-skills/paywall-upgrade-cro/SKILL.md +0 -651
  216. package/bundled-skills/php-lsp/SKILL.md +0 -36
  217. package/bundled-skills/playwright/SKILL.md +0 -13
  218. package/bundled-skills/plugin-dev/SKILL.md +0 -434
  219. package/bundled-skills/popup-cro/SKILL.md +0 -520
  220. package/bundled-skills/positioning-angles/SKILL.md +0 -330
  221. package/bundled-skills/pr-review-toolkit/SKILL.md +0 -359
  222. package/bundled-skills/pricing-strategy/SKILL.md +0 -777
  223. package/bundled-skills/proactive-self-improving/SKILL.md +0 -435
  224. package/bundled-skills/programmatic-seo/SKILL.md +0 -714
  225. package/bundled-skills/pyright-lsp/SKILL.md +0 -43
  226. package/bundled-skills/quality-assurance-framework/SKILL.md +0 -168
  227. package/bundled-skills/question-refiner/SKILL.md +0 -160
  228. package/bundled-skills/ralph-loop/SKILL.md +0 -205
  229. package/bundled-skills/refactoring-expert/SKILL.md +0 -103
  230. package/bundled-skills/referral-program/SKILL.md +0 -668
  231. package/bundled-skills/research-executor/SKILL.md +0 -164
  232. package/bundled-skills/review-with-security/SKILL.md +0 -12
  233. package/bundled-skills/rust-analyzer-lsp/SKILL.md +0 -50
  234. package/bundled-skills/schema-markup/SKILL.md +0 -647
  235. package/bundled-skills/security-audit-expert/SKILL.md +0 -124
  236. package/bundled-skills/security-expert/SKILL.md +0 -140
  237. package/bundled-skills/security-guidance/SKILL.md +0 -13
  238. package/bundled-skills/seedance-prompt/SKILL.md +0 -139
  239. package/bundled-skills/self-evolution/SKILL.md +0 -1160
  240. package/bundled-skills/seo-audit/SKILL.md +0 -432
  241. package/bundled-skills/seo-content/SKILL.md +0 -787
  242. package/bundled-skills/serena/SKILL.md +0 -13
  243. package/bundled-skills/signup-flow-cro/SKILL.md +0 -409
  244. package/bundled-skills/skill-manager/SKILL.md +0 -226
  245. package/bundled-skills/skill-share/SKILL.md +0 -98
  246. package/bundled-skills/slack/SKILL.md +0 -13
  247. package/bundled-skills/social-content/SKILL.md +0 -878
  248. package/bundled-skills/spec-flow-skill/SKILL.md +0 -124
  249. package/bundled-skills/stripe/SKILL.md +0 -13
  250. package/bundled-skills/supabase/SKILL.md +0 -13
  251. package/bundled-skills/swift-lsp/SKILL.md +0 -40
  252. package/bundled-skills/synthesizer/SKILL.md +0 -236
  253. package/bundled-skills/template-skill/SKILL.md +0 -16
  254. package/bundled-skills/theme-factory/SKILL.md +0 -72
  255. package/bundled-skills/tiktok-research/SKILL.md +0 -208
  256. package/bundled-skills/typescript-lsp/SKILL.md +0 -36
  257. package/bundled-skills/ui-ux-pro-max/SKILL.md +0 -247
  258. package/bundled-skills/visual-prompt-engineer/SKILL.md +0 -102
  259. package/bundled-skills/webapp-testing/SKILL.md +0 -111
  260. package/bundled-skills/wide-research/SKILL.md +0 -191
package/dist/repl.js ADDED
@@ -0,0 +1,704 @@
1
+ /**
2
+ * REPL mode — interactive conversation loop with slash commands.
3
+ */
4
+ import { createInterface } from 'node:readline';
5
+ import { existsSync, writeFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { execSync } from 'node:child_process';
8
+ import { loadConfig, getConfigDir } from './config.js';
9
+ import { runAgent } from './agent.js';
10
+ import { listKnowledgeFiles } from './knowledge.js';
11
+ import { listSkills } from './skills.js';
12
+ import { getCurrentPlan, resetPlan } from './planner.js';
13
+ import { toolDefinitions } from './tools/index.js';
14
+ import { printWelcome, printError, printSuccess, printLine, printWarning } from './ui.js';
15
+ import { saveSession, listSessions, loadSession, getRecentSession } from './sessions.js';
16
+ import { generateInsight } from './insight.js';
17
+ import { spawnSubAgent, listSubAgents, getSubAgent } from './sub-agents.js';
18
+ import { enableStatusBar, disableStatusBar, updateStatusBar, setupResizeHandler, renderStatusBar } from './statusbar.js';
19
+ import { slashCompleter } from './completer.js';
20
+ import { runHooks } from './hooks.js';
21
+ import { initMcpServers, shutdownMcpServers, getMcpStatus } from './mcp-client.js';
22
+ import { killAllProcesses } from './tools/process-manager.js';
23
+ import { cleanupClaudeSessions } from './tools/claude-code.js';
24
+ import { getCompactStatus, compressHistory } from './compactor.js';
25
+ import { getRouterStats, debugRoute } from './skill-router.js';
26
+ import { formatCostReport } from './cost-tracker.js';
27
+ import { getPreset, listPresets } from './providers.js';
28
+ import { runDream, formatDreamResult, shouldAutoDream } from './dream.js';
29
+ import { runVerification, formatVerificationResult } from './verification-agent.js';
30
+ import chalk from 'chalk';
31
+ /**
32
+ * Start interactive REPL mode.
33
+ */
34
+ export async function startRepl(opts) {
35
+ const config = loadConfig();
36
+ const skills = listSkills();
37
+ const knowledgeFiles = listKnowledgeFiles();
38
+ const sessionStartTime = Date.now();
39
+ const matchedSkills = [];
40
+ // Initialize MCP servers in background
41
+ initMcpServers().catch(() => { });
42
+ // Runtime overrides
43
+ let currentModel = opts.model || config.model;
44
+ let currentEffort = opts.effort || config.effort;
45
+ let currentPermissionMode = opts.permissionMode || config.permissionMode;
46
+ let sessionName = '';
47
+ printWelcome({
48
+ version: opts.version,
49
+ model: currentModel,
50
+ toolCount: toolDefinitions.length,
51
+ skillsActive: skills.filter(s => s.enabled).length,
52
+ skillsTotal: skills.length,
53
+ knowledgeCount: knowledgeFiles.length,
54
+ cwd: process.cwd(),
55
+ });
56
+ // Check BOBO.md
57
+ const boboMdExists = existsSync(join(process.cwd(), 'BOBO.md'));
58
+ if (boboMdExists) {
59
+ printLine(chalk.dim(' 📋 BOBO.md loaded'));
60
+ }
61
+ if (!config.apiKey) {
62
+ printWarning('API key not configured. Run: bobo config set apiKey <your-key>');
63
+ printLine();
64
+ }
65
+ // Restore session
66
+ let history = [];
67
+ if (opts.continueSession) {
68
+ // -c flag: continue most recent session
69
+ const recent = getRecentSession(86400000); // 24 hours
70
+ if (recent && recent.messages.length > 0) {
71
+ history = recent.messages;
72
+ printSuccess(`Continuing session (${history.length} messages, "${recent.firstUserMessage.slice(0, 40)}...")`);
73
+ }
74
+ else {
75
+ printWarning('No recent session found.');
76
+ }
77
+ }
78
+ else if (opts.resumeId) {
79
+ // -r flag: resume specific session
80
+ const session = loadSession(opts.resumeId);
81
+ if (session) {
82
+ history = session.messages;
83
+ printSuccess(`Resumed session ${opts.resumeId} (${history.length} messages)`);
84
+ }
85
+ else {
86
+ printWarning(`Session not found: ${opts.resumeId}`);
87
+ }
88
+ }
89
+ else {
90
+ // Auto-resume prompt
91
+ const recentSession = getRecentSession(3600000);
92
+ if (recentSession && recentSession.messages.length > 0) {
93
+ printLine(chalk.yellow(`💾 Found recent session (${recentSession.messageCount} messages, ${recentSession.firstUserMessage.slice(0, 50)}...)`));
94
+ printLine(chalk.dim(' Resume? (y/n)'));
95
+ const answer = await new Promise((resolve) => {
96
+ const tmpRl = createInterface({ input: process.stdin, output: process.stdout });
97
+ tmpRl.question(chalk.green('> '), (ans) => {
98
+ tmpRl.close();
99
+ resolve(ans.trim().toLowerCase());
100
+ });
101
+ });
102
+ if (answer === 'y' || answer === 'yes') {
103
+ history = recentSession.messages;
104
+ printSuccess(`Resumed session (${history.length} messages)`);
105
+ }
106
+ }
107
+ }
108
+ // Enable status bar
109
+ if (process.stdout.isTTY) {
110
+ setupResizeHandler();
111
+ enableStatusBar({
112
+ model: currentModel,
113
+ thinkingLevel: currentEffort,
114
+ skillsCount: skills.filter(s => s.enabled).length,
115
+ cwd: process.cwd(),
116
+ permissionMode: currentPermissionMode,
117
+ });
118
+ }
119
+ const rl = createInterface({
120
+ input: process.stdin,
121
+ output: process.stdout,
122
+ prompt: chalk.green('> '),
123
+ completer: slashCompleter,
124
+ });
125
+ let abortController = null;
126
+ let lastResponse = '';
127
+ let autoCompactTriggered = false;
128
+ // Wrapper that renders status bar before prompt
129
+ const showPrompt = () => {
130
+ const bar = renderStatusBar();
131
+ if (bar)
132
+ printLine(bar);
133
+ rl.prompt();
134
+ };
135
+ const autoSave = () => {
136
+ if (history.length > 0) {
137
+ const id = saveSession(history, process.cwd());
138
+ printLine(chalk.dim(`\n💾 Session saved: ${id}`));
139
+ }
140
+ };
141
+ rl.on('SIGINT', () => {
142
+ if (abortController) {
143
+ abortController.abort();
144
+ abortController = null;
145
+ printLine(chalk.dim('\n(cancelled)'));
146
+ showPrompt();
147
+ }
148
+ else {
149
+ printLine(chalk.dim('\n(press Ctrl+C again or Ctrl+D to exit)'));
150
+ showPrompt();
151
+ }
152
+ });
153
+ rl.on('close', async () => {
154
+ autoSave();
155
+ // Auto-dream on session end if needed
156
+ if (shouldAutoDream()) {
157
+ printLine(chalk.dim('\n🌙 Consolidating memories...'));
158
+ try {
159
+ const dreamResult = await runDream({ verbose: false });
160
+ if (dreamResult.insights.length > 0) {
161
+ printLine(chalk.green(`✨ Extracted ${dreamResult.insights.length} insights during shutdown`));
162
+ }
163
+ }
164
+ catch (_) {
165
+ /* intentionally ignored: resume session unavailable */
166
+ // Silent failure on shutdown
167
+ }
168
+ }
169
+ runHooks('session-end');
170
+ shutdownMcpServers();
171
+ killAllProcesses();
172
+ cleanupClaudeSessions();
173
+ disableStatusBar();
174
+ printLine(chalk.dim('\nGoodbye! 🐕'));
175
+ process.exit(0);
176
+ });
177
+ showPrompt();
178
+ for await (const line of rl) {
179
+ const input = line.trim();
180
+ if (!input) {
181
+ showPrompt();
182
+ continue;
183
+ }
184
+ // ─── Exit ───
185
+ if (input === '/quit' || input === '/exit') {
186
+ autoSave();
187
+ runHooks('session-end');
188
+ shutdownMcpServers();
189
+ killAllProcesses();
190
+ disableStatusBar();
191
+ printLine(chalk.dim('Goodbye! 🐕'));
192
+ process.exit(0);
193
+ }
194
+ // ─── /new, /clear ───
195
+ if (input === '/clear' || input === '/new') {
196
+ history = [];
197
+ matchedSkills.length = 0;
198
+ lastResponse = '';
199
+ autoCompactTriggered = false;
200
+ resetPlan();
201
+ printSuccess('Conversation cleared');
202
+ showPrompt();
203
+ continue;
204
+ }
205
+ // ─── /model [name] ───
206
+ if (input.startsWith('/model')) {
207
+ const newModel = input.replace('/model', '').trim();
208
+ if (!newModel) {
209
+ printLine(chalk.cyan('Current model: ') + currentModel);
210
+ printLine(chalk.dim('Usage: /model <model-name>'));
211
+ printLine(chalk.dim(' Examples: claude-sonnet-4-20250514, gpt-4o, deepseek-chat'));
212
+ }
213
+ else {
214
+ currentModel = newModel;
215
+ updateStatusBar({ model: currentModel });
216
+ printSuccess(`Model switched to: ${currentModel}`);
217
+ }
218
+ showPrompt();
219
+ continue;
220
+ }
221
+ // ─── /effort [level] ───
222
+ if (input.startsWith('/effort')) {
223
+ const level = input.replace('/effort', '').trim().toLowerCase();
224
+ if (!level) {
225
+ printLine(chalk.cyan('Current effort: ') + currentEffort);
226
+ printLine(chalk.dim(' /effort low — Quick, concise answers'));
227
+ printLine(chalk.dim(' /effort medium — Balanced (default)'));
228
+ printLine(chalk.dim(' /effort high — Deep analysis, thorough'));
229
+ }
230
+ else if (['low', 'medium', 'high'].includes(level)) {
231
+ currentEffort = level;
232
+ updateStatusBar({ thinkingLevel: currentEffort });
233
+ printSuccess(`Effort level: ${currentEffort}`);
234
+ }
235
+ else {
236
+ printError('Invalid effort level. Use: low, medium, high');
237
+ }
238
+ showPrompt();
239
+ continue;
240
+ }
241
+ // ─── /copy [n] ───
242
+ if (input.startsWith('/copy')) {
243
+ const indexStr = input.replace('/copy', '').trim();
244
+ let textToCopy = lastResponse;
245
+ if (indexStr) {
246
+ const idx = parseInt(indexStr, 10);
247
+ const assistantMsgs = history.filter(m => m.role === 'assistant' && typeof m.content === 'string');
248
+ if (idx > 0 && idx <= assistantMsgs.length) {
249
+ textToCopy = assistantMsgs[assistantMsgs.length - idx].content;
250
+ }
251
+ }
252
+ if (!textToCopy) {
253
+ printWarning('Nothing to copy.');
254
+ }
255
+ else {
256
+ // Try platform clipboard
257
+ try {
258
+ const clipCmd = process.platform === 'darwin' ? 'pbcopy'
259
+ : process.platform === 'win32' ? 'clip'
260
+ : 'xclip -selection clipboard';
261
+ execSync(clipCmd, { input: textToCopy, timeout: 3000 });
262
+ printSuccess('Copied to clipboard!');
263
+ }
264
+ catch (_) {
265
+ /* intentionally ignored: compact state parse failure */
266
+ // Fallback: write to file
267
+ const copyPath = join(getConfigDir(), 'last-copy.txt');
268
+ writeFileSync(copyPath, textToCopy);
269
+ printWarning(`Clipboard unavailable. Saved to ${copyPath}`);
270
+ }
271
+ }
272
+ showPrompt();
273
+ continue;
274
+ }
275
+ // ─── /context ───
276
+ if (input === '/context') {
277
+ const msgCount = history.length;
278
+ let totalChars = 0;
279
+ let toolResultChars = 0;
280
+ const roleCounts = {};
281
+ for (const msg of history) {
282
+ const content = typeof msg.content === 'string' ? msg.content : '';
283
+ totalChars += content.length;
284
+ roleCounts[msg.role] = (roleCounts[msg.role] || 0) + 1;
285
+ if (msg.role === 'tool')
286
+ toolResultChars += content.length;
287
+ }
288
+ const estTokens = Math.ceil(totalChars / 3.5);
289
+ const maxContext = 200000; // approximate
290
+ const usage = (estTokens / maxContext * 100).toFixed(1);
291
+ printLine(chalk.cyan.bold('\n📊 Context Analysis\n'));
292
+ printLine(` Messages: ${msgCount}`);
293
+ printLine(` Est. Tokens: ~${estTokens.toLocaleString()} / ${maxContext.toLocaleString()} (${usage}%)`);
294
+ printLine('');
295
+ for (const [role, count] of Object.entries(roleCounts)) {
296
+ printLine(` ${role.padEnd(12)} ${count} messages`);
297
+ }
298
+ if (toolResultChars > totalChars * 0.6) {
299
+ printLine(chalk.yellow('\n ⚠ Tool results are >60% of context. Consider /compact to free space.'));
300
+ }
301
+ if (estTokens > maxContext * 0.75) {
302
+ printLine(chalk.red('\n 🔴 Context usage >75%. Run /compact soon!'));
303
+ }
304
+ else if (estTokens > maxContext * 0.5) {
305
+ printLine(chalk.yellow('\n 🟡 Context usage >50%. Keep an eye on it.'));
306
+ }
307
+ else {
308
+ printLine(chalk.green('\n 🟢 Context usage healthy.'));
309
+ }
310
+ printLine();
311
+ showPrompt();
312
+ continue;
313
+ }
314
+ // ─── /rename <name> ───
315
+ if (input.startsWith('/rename')) {
316
+ const name = input.replace('/rename', '').trim();
317
+ if (!name) {
318
+ printLine(chalk.dim(`Current name: ${sessionName || '(unnamed)'}`));
319
+ printLine(chalk.dim('Usage: /rename <name>'));
320
+ }
321
+ else {
322
+ sessionName = name;
323
+ printSuccess(`Session renamed: ${sessionName}`);
324
+ }
325
+ showPrompt();
326
+ continue;
327
+ }
328
+ // ─── /history ───
329
+ if (input === '/history') {
330
+ printLine(`Turns: ${history.filter(m => m.role === 'user').length}`);
331
+ showPrompt();
332
+ continue;
333
+ }
334
+ // ─── /resume ───
335
+ if (input === '/resume') {
336
+ const sessions = listSessions(10);
337
+ if (sessions.length === 0) {
338
+ printWarning('No saved sessions.');
339
+ showPrompt();
340
+ continue;
341
+ }
342
+ printLine(chalk.cyan.bold('\n💾 Recent Sessions:\n'));
343
+ for (let i = 0; i < sessions.length; i++) {
344
+ const s = sessions[i];
345
+ const date = s.startedAt ? new Date(s.startedAt).toLocaleString() : 'unknown';
346
+ printLine(` ${chalk.bold(String(i + 1).padStart(2))} ${chalk.dim(date)} — ${s.firstUserMessage.slice(0, 50)} (${s.messageCount} msgs)`);
347
+ }
348
+ printLine(chalk.dim('\n Enter number to restore, or press Enter to cancel:'));
349
+ const pick = await new Promise((resolve) => {
350
+ const tmpRl = createInterface({ input: process.stdin, output: process.stdout });
351
+ tmpRl.question(chalk.green('> '), (ans) => {
352
+ tmpRl.close();
353
+ resolve(ans.trim());
354
+ });
355
+ });
356
+ const idx = parseInt(pick, 10) - 1;
357
+ if (idx >= 0 && idx < sessions.length) {
358
+ const session = loadSession(sessions[idx].id);
359
+ if (session) {
360
+ history = session.messages;
361
+ printSuccess(`Restored session (${history.length} messages)`);
362
+ }
363
+ else {
364
+ printError('Failed to load session.');
365
+ }
366
+ }
367
+ showPrompt();
368
+ continue;
369
+ }
370
+ // ─── /insight ───
371
+ if (input === '/insight') {
372
+ printLine(generateInsight(history, sessionStartTime, [...new Set(matchedSkills)]));
373
+ showPrompt();
374
+ continue;
375
+ }
376
+ // ─── /agents, /bg ───
377
+ if (input === '/agents' || input === '/bg') {
378
+ const agents = listSubAgents(10);
379
+ if (agents.length === 0) {
380
+ printLine(chalk.dim('No sub-agents. Use: /spawn <task>'));
381
+ }
382
+ else {
383
+ printLine(chalk.cyan.bold('\n🤖 Sub-Agents:\n'));
384
+ for (const a of agents) {
385
+ const icon = a.status === 'completed' ? '✅' : a.status === 'failed' ? '❌' : '⏳';
386
+ const task = a.task.slice(0, 60) + (a.task.length > 60 ? '...' : '');
387
+ printLine(` ${icon} ${chalk.bold(a.id)} — ${task} ${chalk.dim(`[${a.status}]`)}`);
388
+ }
389
+ }
390
+ printLine();
391
+ showPrompt();
392
+ continue;
393
+ }
394
+ if (input.startsWith('/agents show ')) {
395
+ const id = input.replace('/agents show ', '').trim();
396
+ const agent = getSubAgent(id);
397
+ if (!agent) {
398
+ printError(`Sub-agent not found: ${id}`);
399
+ }
400
+ else {
401
+ printLine(chalk.cyan.bold(`\n🤖 ${agent.id} [${agent.status}]`));
402
+ printLine(chalk.dim(`Task: ${agent.task}`));
403
+ if (agent.result)
404
+ printLine(`\n${agent.result}`);
405
+ if (agent.error)
406
+ printLine(chalk.red(`Error: ${agent.error}`));
407
+ }
408
+ printLine();
409
+ showPrompt();
410
+ continue;
411
+ }
412
+ if (input.startsWith('/spawn ')) {
413
+ const task = input.replace('/spawn ', '').trim();
414
+ if (!task) {
415
+ printWarning('Usage: /spawn <task description>');
416
+ }
417
+ else {
418
+ const result = spawnSubAgent(task);
419
+ if (result.error) {
420
+ printError(result.error);
421
+ }
422
+ else {
423
+ printSuccess(`Sub-agent ${result.id} spawned! Check with /agents`);
424
+ }
425
+ }
426
+ showPrompt();
427
+ continue;
428
+ }
429
+ // ─── /compact ───
430
+ if (input === '/compact') {
431
+ const userCount = history.filter(m => m.role === 'user').length;
432
+ if (userCount > 4) {
433
+ printLine(chalk.dim('Compacting context...'));
434
+ abortController = new AbortController();
435
+ try {
436
+ const compactResult = await runAgent('Perform a nine-section context compression. Analyze the conversation so far and produce a structured summary covering: ' +
437
+ '1. Main requests/intent 2. Key technical concepts 3. Files and code 4. Errors and fixes 5. Problem resolution ' +
438
+ '6. All user messages 7. Pending tasks 8. Current work state 9. Next steps (with citations). ' +
439
+ 'Output the summary directly, do not call any tools.', history, { signal: abortController.signal, model: currentModel, effort: currentEffort });
440
+ history = [
441
+ { role: 'user', content: 'Below is a compressed summary of our prior conversation. Continue from here.' },
442
+ { role: 'assistant', content: compactResult.response },
443
+ ];
444
+ autoCompactTriggered = false;
445
+ printSuccess('Context compacted (nine-section summary)');
446
+ }
447
+ catch (e) {
448
+ if (e.message !== 'Aborted') {
449
+ history = history.slice(-8);
450
+ printSuccess('Context compacted (truncated)');
451
+ }
452
+ }
453
+ abortController = null;
454
+ }
455
+ else {
456
+ printWarning('Conversation too short to compact');
457
+ }
458
+ showPrompt();
459
+ continue;
460
+ }
461
+ // ─── /dream (KAIROS memory consolidation) ───
462
+ if (input === '/dream') {
463
+ printLine(chalk.dim('🌙 Running KAIROS dream mode...'));
464
+ try {
465
+ const dreamResult = await runDream({ verbose: true });
466
+ printLine(formatDreamResult(dreamResult));
467
+ }
468
+ catch (e) {
469
+ printError(`Dream mode failed: ${e.message}`);
470
+ }
471
+ showPrompt();
472
+ continue;
473
+ }
474
+ // ─── /verify (verification agent) ───
475
+ if (input.startsWith('/verify')) {
476
+ const task = input.replace('/verify', '').trim() || 'Verify current project state';
477
+ printLine(chalk.dim('🔍 Running verification agent...'));
478
+ try {
479
+ const verifyResult = await runVerification(task, lastResponse || '', {
480
+ cwd: process.cwd(),
481
+ });
482
+ printLine(formatVerificationResult(verifyResult));
483
+ // If verification failed, suggest fixes
484
+ if (verifyResult.verdict === 'FAIL' && verifyResult.suggestedFixes) {
485
+ printLine(chalk.yellow('\n💡 Suggested next steps:'));
486
+ for (const fix of verifyResult.suggestedFixes) {
487
+ printLine(chalk.dim(` • ${fix}`));
488
+ }
489
+ }
490
+ }
491
+ catch (e) {
492
+ printError(`Verification failed: ${e.message}`);
493
+ }
494
+ showPrompt();
495
+ continue;
496
+ }
497
+ // ─── /status ───
498
+ if (input === '/status') {
499
+ const turns = history.filter(m => m.role === 'user').length;
500
+ const mcpServers = getMcpStatus();
501
+ const compactInfo = getCompactStatus(history);
502
+ printLine(chalk.cyan('📊 Session Status:'));
503
+ printLine(` Model: ${currentModel}`);
504
+ printLine(` Effort: ${currentEffort}`);
505
+ printLine(` Permission: ${currentPermissionMode}`);
506
+ printLine(` Turns: ${turns}`);
507
+ printLine(` Messages: ${history.length}`);
508
+ printLine(` Tokens: ~${compactInfo.tokens.toLocaleString()} (${compactInfo.urgency})`);
509
+ printLine(` CWD: ${process.cwd()}`);
510
+ if (sessionName)
511
+ printLine(` Name: ${sessionName}`);
512
+ if (mcpServers.length > 0) {
513
+ printLine(` MCP: ${mcpServers.filter(s => s.ready).length}/${mcpServers.length} servers (${mcpServers.reduce((a, s) => a + s.toolCount, 0)} tools)`);
514
+ }
515
+ printLine(chalk.dim(' ── Cost ──'));
516
+ printLine(` ${formatCostReport(currentModel).split('\n').join('\n ')}`);
517
+ showPrompt();
518
+ continue;
519
+ }
520
+ if (input === '/plan') {
521
+ printLine(getCurrentPlan());
522
+ showPrompt();
523
+ continue;
524
+ }
525
+ if (input === '/knowledge') {
526
+ const files = listKnowledgeFiles();
527
+ for (const f of files) {
528
+ const icon = f.type === 'always' ? '🔵' : f.type === 'on-demand' ? '🟡' : '🟢';
529
+ printLine(` ${icon} ${f.file} (${f.type})`);
530
+ }
531
+ showPrompt();
532
+ continue;
533
+ }
534
+ if (input === '/skills') {
535
+ const sklls = listSkills();
536
+ for (const s of sklls) {
537
+ const icon = s.enabled ? '✅' : '❌';
538
+ printLine(` ${icon} ${s.name} — ${s.description}`);
539
+ }
540
+ showPrompt();
541
+ continue;
542
+ }
543
+ // ─── /mcp ───
544
+ if (input === '/mcp') {
545
+ const mcpServers = getMcpStatus();
546
+ if (mcpServers.length === 0) {
547
+ printLine(chalk.dim('No MCP servers. Configure in ~/.bobo/mcp.json'));
548
+ printLine(chalk.dim('Run: bobo mcp init'));
549
+ }
550
+ else {
551
+ printLine(chalk.cyan.bold('\n🔌 MCP Servers:\n'));
552
+ for (const s of mcpServers) {
553
+ const icon = s.ready ? chalk.green('●') : chalk.red('●');
554
+ printLine(` ${icon} ${chalk.bold(s.name)} [${s.transport}] — ${s.toolCount} tools`);
555
+ }
556
+ }
557
+ printLine();
558
+ showPrompt();
559
+ continue;
560
+ }
561
+ // ─── /cost ───
562
+ if (input === '/cost') {
563
+ printLine(chalk.cyan('💰 API Cost:'));
564
+ printLine(` ${formatCostReport(currentModel).split('\n').join('\n ')}`);
565
+ showPrompt();
566
+ continue;
567
+ }
568
+ // ─── /route (skill router debug) ───
569
+ if (input.startsWith('/route ')) {
570
+ const query = input.slice(7).trim();
571
+ if (query) {
572
+ printLine(chalk.cyan('🔀 Skill Route Debug:'));
573
+ printLine(debugRoute(query));
574
+ }
575
+ else {
576
+ const stats = getRouterStats();
577
+ printLine(chalk.cyan('🔀 Skill Router Stats:'));
578
+ printLine(` Total: ${stats.totalSkills} | Kernel: ${stats.kernel} | Auto: ${stats.auto} | Manual: ${stats.manual}`);
579
+ printLine(` Intent categories: ${stats.intents}`);
580
+ }
581
+ showPrompt();
582
+ continue;
583
+ }
584
+ // ─── /provider ───
585
+ if (input.startsWith('/provider')) {
586
+ const arg = input.slice(9).trim();
587
+ if (!arg) {
588
+ printLine(chalk.cyan('🌐 Available Providers:'));
589
+ printLine(listPresets());
590
+ printLine(chalk.dim('\n Usage: /provider <name> — switch to provider'));
591
+ }
592
+ else {
593
+ const preset = getPreset(arg);
594
+ if (!preset) {
595
+ printError(`Unknown provider: ${arg}`);
596
+ }
597
+ else {
598
+ currentModel = preset.defaultModel;
599
+ // Note: baseUrl and apiKey require config set
600
+ printSuccess(`Switched model to ${preset.defaultModel}`);
601
+ printLine(chalk.dim(` Base URL: ${preset.baseUrl}`));
602
+ printLine(chalk.dim(` Set API key: bobo config set apiKey <key>`));
603
+ printLine(chalk.dim(` Set base URL: bobo config set baseUrl ${preset.baseUrl}`));
604
+ }
605
+ }
606
+ showPrompt();
607
+ continue;
608
+ }
609
+ // ─── /help ───
610
+ if (input === '/help') {
611
+ printLine(chalk.cyan.bold('Commands:'));
612
+ printLine('');
613
+ printLine(chalk.dim(' Session'));
614
+ printLine(' /new Start new conversation');
615
+ printLine(' /clear Clear conversation history');
616
+ printLine(' /compact Compress context (nine-section)');
617
+ printLine(' /resume Restore a previous session');
618
+ printLine(' /rename <n> Rename current session');
619
+ printLine(' /quit Exit');
620
+ printLine('');
621
+ printLine(chalk.dim(' Model & Effort'));
622
+ printLine(' /model <n> Switch model');
623
+ printLine(' /effort <l> Set thinking effort (low/medium/high)');
624
+ printLine('');
625
+ printLine(chalk.dim(' Analysis'));
626
+ printLine(' /insight Session analytics (tokens, tools, skills)');
627
+ printLine(' /context Context usage analysis');
628
+ printLine(' /status Session status');
629
+ printLine(' /copy [n] Copy last response to clipboard');
630
+ printLine(' /plan Show current task plan');
631
+ printLine(' /verify Run verification agent');
632
+ printLine(' /history Show conversation turns');
633
+ printLine('');
634
+ printLine(chalk.dim(' Sub-Agents'));
635
+ printLine(' /spawn <t> Run task in background sub-agent');
636
+ printLine(' /agents List sub-agents');
637
+ printLine(' /agents show <id> Show sub-agent result');
638
+ printLine('');
639
+ printLine(chalk.dim(' Knowledge & Integrations'));
640
+ printLine(' /knowledge List knowledge files');
641
+ printLine(' /skills List skills');
642
+ printLine(' /mcp MCP server status');
643
+ printLine(' /bg Background process list');
644
+ printLine(' /dream Memory consolidation');
645
+ printLine('');
646
+ printLine(chalk.dim(' CLI Commands'));
647
+ printLine(' bobo -p "q" Non-interactive (supports piping)');
648
+ printLine(' bobo -c Continue last conversation');
649
+ printLine(' bobo -r <id> Resume specific session');
650
+ printLine(' bobo --full-auto Auto-approve tool calls');
651
+ printLine(' bobo --yolo No sandbox, no approvals');
652
+ printLine(' bobo watch File watcher (daemon mode)');
653
+ printLine(' bobo run Autonomous agent loop');
654
+ printLine(' bobo mcp MCP server management');
655
+ printLine(' bobo hooks Lifecycle hook management');
656
+ printLine(' bobo doctor Environment check');
657
+ printLine('');
658
+ printLine(chalk.dim(' Debug'));
659
+ printLine(' /cost API cost this session');
660
+ printLine(' /route <msg> Skill router debug');
661
+ printLine(' /provider Switch AI provider');
662
+ showPrompt();
663
+ continue;
664
+ }
665
+ // ─── Run agent ───
666
+ abortController = new AbortController();
667
+ try {
668
+ const result = await runAgent(input, history, {
669
+ signal: abortController.signal,
670
+ matchedSkills,
671
+ model: currentModel,
672
+ effort: currentEffort,
673
+ permissionMode: currentPermissionMode,
674
+ onAutoCompact: () => {
675
+ if (!autoCompactTriggered) {
676
+ autoCompactTriggered = true;
677
+ printLine(chalk.yellow('\n⚠ Context is getting large. Consider running /compact to free space.\n'));
678
+ }
679
+ },
680
+ });
681
+ history = result.history;
682
+ lastResponse = result.response;
683
+ // Auto-compact check
684
+ const compactInfo = getCompactStatus(history);
685
+ if (compactInfo.urgency === 'critical') {
686
+ printWarning(`Context at ${compactInfo.tokens} tokens — auto-compressing...`);
687
+ history = compressHistory(history, 8);
688
+ printSuccess(`Compressed to ${getCompactStatus(history).tokens} tokens`);
689
+ }
690
+ else if (compactInfo.urgency === 'high') {
691
+ printWarning(`⚠ Context at ${compactInfo.tokens} tokens. Run /compact to compress.`);
692
+ }
693
+ }
694
+ catch (e) {
695
+ if (e.message !== 'Aborted') {
696
+ printError(e.message);
697
+ }
698
+ }
699
+ abortController = null;
700
+ printLine();
701
+ showPrompt();
702
+ }
703
+ }
704
+ //# sourceMappingURL=repl.js.map