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/cli.js ADDED
@@ -0,0 +1,669 @@
1
+ /**
2
+ * CLI command registration — all commander subcommands.
3
+ */
4
+ import { execSync } from 'node:child_process';
5
+ import { readFileSync, existsSync, mkdirSync, copyFileSync, writeFileSync, readdirSync, statSync, cpSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import chalk from 'chalk';
8
+ import { setConfigValue, getConfigValue, listConfig, ensureConfigDir, getConfigDir, resolveKnowledgeDir, loadConfig, } from './config.js';
9
+ import { listKnowledgeFiles } from './knowledge.js';
10
+ import { listSkills, setSkillEnabled, initSkills, importSkills } from './skills.js';
11
+ import { initProject } from './project.js';
12
+ import { printError, printSuccess, printLine, printWarning } from './ui.js';
13
+ import { registerKnowledgeCommand } from './knowledge-commands.js';
14
+ import { registerRulesCommand } from './rules-commands.js';
15
+ import { registerStructuredSkillsCommand } from './structured-skills-commands.js';
16
+ import { registerStructuredTemplateCommand } from './structured-template-commands.js';
17
+ import { spawnSubAgent, listSubAgents, getSubAgent } from './sub-agents.js';
18
+ import { initHooksTemplate } from './hooks.js';
19
+ import { initMcpServers, shutdownMcpServers, getMcpStatus } from './mcp-client.js';
20
+ import { startWatch } from './watcher.js';
21
+ import { runAutonomous } from './autonomous.js';
22
+ import { runTeamWorkflow, runPlanWorkflow, runVerifyWorkflow, runInterviewWorkflow, runAskWorkflow } from './workflows/index.js';
23
+ import { getAllAgentRoles, getAgentsByLane, AGENT_CATALOG } from './agents/catalog.js';
24
+ import { recoverContext } from './state/recovery.js';
25
+ import { addMemoryEntry, queryMemory, appendNotepad, readNotepad } from './state/project-memory.js';
26
+ import { renderStatusPanel } from './ui/hud.js';
27
+ /**
28
+ * Register all CLI subcommands on the program.
29
+ */
30
+ export function registerCommands(program) {
31
+ // ─── Config subcommand ───────────────────────────────────────
32
+ const configCmd = program.command('config').description('Manage configuration');
33
+ configCmd
34
+ .command('set <key> <value>')
35
+ .description('Set a config value')
36
+ .action((key, value) => {
37
+ try {
38
+ setConfigValue(key, value);
39
+ printSuccess(`${key} = ${key === 'apiKey' ? '***' : value}`);
40
+ }
41
+ catch (e) {
42
+ printError(e.message);
43
+ process.exit(1);
44
+ }
45
+ });
46
+ configCmd
47
+ .command('get <key>')
48
+ .description('Get a config value')
49
+ .action((key) => {
50
+ const value = getConfigValue(key);
51
+ if (value === undefined) {
52
+ printError(`Unknown key: ${key}`);
53
+ process.exit(1);
54
+ }
55
+ console.log(value);
56
+ });
57
+ configCmd
58
+ .command('list')
59
+ .description('Show all configuration')
60
+ .action(() => {
61
+ const config = listConfig();
62
+ for (const [k, v] of Object.entries(config)) {
63
+ console.log(`${chalk.cyan(k)}: ${v}`);
64
+ }
65
+ });
66
+ // ─── Init subcommand ─────────────────────────────────────────
67
+ program
68
+ .command('init')
69
+ .description('Initialize ~/.bobo/ directory and knowledge base')
70
+ .action(() => {
71
+ ensureConfigDir();
72
+ const config = loadConfig();
73
+ const knowledgeDir = resolveKnowledgeDir(config);
74
+ if (!existsSync(knowledgeDir)) {
75
+ mkdirSync(knowledgeDir, { recursive: true });
76
+ }
77
+ // Import __dirname equivalent
78
+ const __dirname = new URL('.', import.meta.url).pathname;
79
+ const bundledDir = join(__dirname, '..', 'knowledge');
80
+ if (existsSync(bundledDir)) {
81
+ const files = readdirSync(bundledDir).filter(f => f.endsWith('.md'));
82
+ for (const file of files) {
83
+ const target = join(knowledgeDir, file);
84
+ const source = join(bundledDir, file);
85
+ if (!existsSync(target)) {
86
+ copyFileSync(source, target);
87
+ printSuccess(`Created ${target}`);
88
+ }
89
+ }
90
+ }
91
+ const memoryDir = join(getConfigDir(), 'memory');
92
+ const learningsDir = join(getConfigDir(), '.learnings');
93
+ const sessionsDir = join(getConfigDir(), 'sessions');
94
+ const agentsDir = join(getConfigDir(), 'agents');
95
+ for (const dir of [memoryDir, learningsDir, sessionsDir, agentsDir]) {
96
+ if (!existsSync(dir)) {
97
+ mkdirSync(dir, { recursive: true });
98
+ printSuccess(`Created ${dir}`);
99
+ }
100
+ }
101
+ initSkills();
102
+ // Copy bundled skills
103
+ const bundledSkillsDir = join(__dirname, '..', 'bundled-skills');
104
+ const userSkillsDir = join(getConfigDir(), 'skills');
105
+ if (existsSync(bundledSkillsDir)) {
106
+ if (!existsSync(userSkillsDir)) {
107
+ mkdirSync(userSkillsDir, { recursive: true });
108
+ }
109
+ let installed = 0;
110
+ for (const skillName of readdirSync(bundledSkillsDir)) {
111
+ const src = join(bundledSkillsDir, skillName);
112
+ const dest = join(userSkillsDir, skillName);
113
+ try {
114
+ if (!statSync(src).isDirectory())
115
+ continue;
116
+ }
117
+ catch (_) { /* intentionally ignored: unreadable installed skill */
118
+ continue;
119
+ }
120
+ if (!existsSync(dest)) {
121
+ cpSync(src, dest, { recursive: true });
122
+ installed++;
123
+ }
124
+ }
125
+ if (installed > 0) {
126
+ const manifestPath = join(getConfigDir(), 'skills-manifest.json');
127
+ let manifest = {};
128
+ try {
129
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
130
+ }
131
+ catch (_) {
132
+ manifest = { version: 1, skills: {} }; /* intentionally ignored: malformed manifest */
133
+ }
134
+ const skills = (manifest.skills || {});
135
+ for (const skillName of readdirSync(userSkillsDir)) {
136
+ if (!skills[skillName]) {
137
+ skills[skillName] = { enabled: true };
138
+ }
139
+ }
140
+ manifest.skills = skills;
141
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
142
+ printSuccess(`${installed} skills installed (all enabled, passive triggering)`);
143
+ }
144
+ }
145
+ // Create BOBO.md template
146
+ const boboMdPath = join(process.cwd(), 'BOBO.md');
147
+ if (!existsSync(boboMdPath)) {
148
+ writeFileSync(boboMdPath, `# Project Instructions
149
+
150
+ <!-- Bobo reads this file at the start of every session. -->
151
+ <!-- Add coding standards, architecture decisions, and project-specific rules here. -->
152
+
153
+ ## Build & Test
154
+ <!-- e.g.: npm run build, npm test -->
155
+
156
+ ## Style Guide
157
+ <!-- e.g.: Use TypeScript strict mode, prefer const over let -->
158
+ `);
159
+ printSuccess('Created BOBO.md (project instructions)');
160
+ }
161
+ printSuccess(`Initialized ${getConfigDir()}`);
162
+ printLine(`Knowledge: ${knowledgeDir}`);
163
+ printWarning('Configure your API key: bobo config set apiKey <your-key>');
164
+ });
165
+ // ─── Doctor subcommand ───────────────────────────────────────
166
+ program
167
+ .command('doctor')
168
+ .description('Check environment dependencies for skills')
169
+ .action(() => {
170
+ printLine(chalk.cyan.bold('\n🩺 Bobo Doctor — Environment Check\n'));
171
+ const checks = [
172
+ { name: 'Node.js', cmd: 'node --version', required: true },
173
+ { name: 'Python 3', cmd: 'python3 --version', required: false },
174
+ { name: 'pip3', cmd: 'pip3 --version', required: false },
175
+ { name: 'Git', cmd: 'git --version', required: true },
176
+ { name: 'ffmpeg', cmd: 'ffmpeg -version', required: false },
177
+ { name: 'npm', cmd: 'npm --version', required: true },
178
+ { name: 'curl', cmd: 'curl --version', required: false },
179
+ ];
180
+ let allGood = true;
181
+ for (const check of checks) {
182
+ try {
183
+ const output = execSync(check.cmd, { timeout: 5000, stdio: 'pipe' }).toString().trim().split('\n')[0];
184
+ printLine(` ${chalk.green('āœ“')} ${check.name.padEnd(12)} ${chalk.dim(output)}`);
185
+ }
186
+ catch (_) {
187
+ /* intentionally ignored: skill metadata read failure */
188
+ const icon = check.required ? chalk.red('āœ—') : chalk.yellow('ā—‹');
189
+ const label = check.required ? chalk.red('MISSING (required)') : chalk.yellow('not found (optional)');
190
+ printLine(` ${icon} ${check.name.padEnd(12)} ${label}`);
191
+ if (check.required)
192
+ allGood = false;
193
+ }
194
+ }
195
+ const config = loadConfig();
196
+ if (config.apiKey) {
197
+ printLine(` ${chalk.green('āœ“')} ${'API Key'.padEnd(12)} ${chalk.dim('configured')}`);
198
+ }
199
+ else {
200
+ printLine(` ${chalk.red('āœ—')} ${'API Key'.padEnd(12)} ${chalk.red('not set — run: bobo config set apiKey <key>')}`);
201
+ allGood = false;
202
+ }
203
+ const boboMd = existsSync(join(process.cwd(), 'BOBO.md'));
204
+ printLine(` ${boboMd ? chalk.green('āœ“') : chalk.yellow('ā—‹')} ${'BOBO.md'.padEnd(12)} ${boboMd ? chalk.dim('found') : chalk.yellow('not found — run: bobo init')}`);
205
+ const skillsDir = join(getConfigDir(), 'skills');
206
+ if (existsSync(skillsDir)) {
207
+ const count = readdirSync(skillsDir).filter(f => {
208
+ try {
209
+ return statSync(join(skillsDir, f)).isDirectory();
210
+ }
211
+ catch (_) {
212
+ return false; /* intentionally ignored: stat failure */
213
+ }
214
+ }).length;
215
+ printLine(` ${chalk.green('āœ“')} ${'Skills'.padEnd(12)} ${chalk.dim(`${count} installed`)}`);
216
+ }
217
+ else {
218
+ printLine(` ${chalk.yellow('ā—‹')} ${'Skills'.padEnd(12)} ${chalk.yellow('none — run: bobo init')}`);
219
+ }
220
+ printLine();
221
+ if (allGood) {
222
+ printSuccess('All required dependencies are available! šŸ•');
223
+ }
224
+ else {
225
+ printWarning('Some required dependencies are missing.');
226
+ }
227
+ printLine();
228
+ });
229
+ // ─── Spawn subcommand ────────────────────────────────────────
230
+ program
231
+ .command('spawn <task>')
232
+ .description('Spawn a background sub-agent to run a task')
233
+ .action((task) => {
234
+ const result = spawnSubAgent(task);
235
+ if (result.error) {
236
+ printError(result.error);
237
+ process.exit(1);
238
+ }
239
+ printSuccess(`Sub-agent ${result.id} spawned!`);
240
+ printLine(chalk.dim(` Task: ${task.slice(0, 80)}${task.length > 80 ? '...' : ''}`));
241
+ printLine(chalk.dim(` Check status: bobo agents`));
242
+ });
243
+ // ─── Agents subcommand ───────────────────────────────────────
244
+ const agentsCmd = program.command('agents').description('Manage sub-agents');
245
+ agentsCmd
246
+ .command('list')
247
+ .description('List all sub-agents')
248
+ .action(() => {
249
+ const agents = listSubAgents();
250
+ if (agents.length === 0) {
251
+ printLine(chalk.dim('No sub-agents. Spawn one with: bobo spawn "task"'));
252
+ return;
253
+ }
254
+ printLine(chalk.cyan.bold('\nšŸ¤– Sub-Agents:\n'));
255
+ for (const a of agents) {
256
+ const icon = a.status === 'completed' ? 'āœ…' : a.status === 'failed' ? 'āŒ' : 'ā³';
257
+ const task = a.task.slice(0, 60) + (a.task.length > 60 ? '...' : '');
258
+ printLine(` ${icon} ${chalk.bold(a.id)} — ${task}`);
259
+ printLine(` ${chalk.dim(a.startedAt)} ${chalk.dim(`[${a.status}]`)}`);
260
+ }
261
+ printLine();
262
+ });
263
+ agentsCmd
264
+ .command('show <id>')
265
+ .description('Show sub-agent result')
266
+ .action((id) => {
267
+ const agent = getSubAgent(id);
268
+ if (!agent) {
269
+ printError(`Sub-agent not found: ${id}`);
270
+ return;
271
+ }
272
+ printLine(chalk.cyan.bold(`\nšŸ¤– Sub-Agent: ${agent.id}\n`));
273
+ printLine(` Status: ${agent.status}`);
274
+ printLine(` Task: ${agent.task}`);
275
+ printLine(` Started: ${agent.startedAt}`);
276
+ if (agent.completedAt)
277
+ printLine(` Done: ${agent.completedAt}`);
278
+ if (agent.result) {
279
+ printLine(`\n${chalk.dim('─'.repeat(50))}\n`);
280
+ printLine(agent.result);
281
+ }
282
+ if (agent.error) {
283
+ printLine(`\n${chalk.red('Error:')} ${agent.error}`);
284
+ }
285
+ printLine();
286
+ });
287
+ agentsCmd.action(() => {
288
+ const agents = listSubAgents();
289
+ if (agents.length === 0) {
290
+ printLine(chalk.dim('No sub-agents. Spawn one with: bobo spawn "task"'));
291
+ return;
292
+ }
293
+ printLine(chalk.cyan.bold('\nšŸ¤– Sub-Agents:\n'));
294
+ for (const a of agents) {
295
+ const icon = a.status === 'completed' ? 'āœ…' : a.status === 'failed' ? 'āŒ' : 'ā³';
296
+ const task = a.task.slice(0, 60) + (a.task.length > 60 ? '...' : '');
297
+ printLine(` ${icon} ${chalk.bold(a.id)} — ${task}`);
298
+ }
299
+ printLine();
300
+ });
301
+ // ─── Knowledge subcommand ────────────────────────────────────
302
+ program
303
+ .command('knowledge')
304
+ .description('Show knowledge base files')
305
+ .action(() => {
306
+ const files = listKnowledgeFiles();
307
+ console.log(chalk.cyan.bold('\nšŸ“š Knowledge Base:\n'));
308
+ for (const f of files) {
309
+ const typeIcon = f.type === 'always' ? 'šŸ”µ' : f.type === 'on-demand' ? '🟔' : '🟢';
310
+ const sourceTag = f.source === 'user' ? chalk.green('user') : chalk.dim('bundled');
311
+ console.log(` ${typeIcon} ${f.file} [${sourceTag}] (${f.type})`);
312
+ }
313
+ console.log(chalk.dim('\n šŸ”µ always-load 🟔 on-demand 🟢 custom\n'));
314
+ });
315
+ // ─── Skill subcommand ────────────────────────────────────────
316
+ const skillCmd = program.command('skill').description('Manage skills');
317
+ skillCmd.command('list').description('List all skills').action(() => {
318
+ const skills = listSkills();
319
+ console.log(chalk.cyan.bold('\n🧩 Skills:\n'));
320
+ for (const s of skills) {
321
+ const icon = s.enabled ? 'āœ…' : 'āŒ';
322
+ const typeTag = s.type === 'builtin' ? chalk.dim('builtin') : chalk.green('custom');
323
+ console.log(` ${icon} ${chalk.bold(s.name)} [${typeTag}] — ${s.description}`);
324
+ }
325
+ console.log();
326
+ });
327
+ skillCmd.command('enable <name>').description('Enable a skill').action((name) => {
328
+ console.log(setSkillEnabled(name, true));
329
+ });
330
+ skillCmd.command('disable <name>').description('Disable a skill').action((name) => {
331
+ console.log(setSkillEnabled(name, false));
332
+ });
333
+ skillCmd.command('import <path>').description('Batch import skills').action((path) => {
334
+ const resolved = path.startsWith('~') ? join(process.env.HOME || '', path.slice(1)) : path;
335
+ console.log(importSkills(resolved));
336
+ });
337
+ // ─── Workflow commands ──────────────────────────────────────
338
+ program
339
+ .command('team <spec> <task>')
340
+ .description('Run a team workflow with multiple agents, e.g. bobo team 3:executor "build REST API"')
341
+ .action(async (spec, task) => {
342
+ const [countRaw, roleRaw] = spec.split(':');
343
+ const teamSize = Number.parseInt(countRaw, 10);
344
+ const roles = getAllAgentRoles();
345
+ if (!Number.isFinite(teamSize) || teamSize <= 0) {
346
+ printError('Invalid team size. Use format: <N>:<role>');
347
+ process.exit(1);
348
+ }
349
+ if (roleRaw && !roles.includes(roleRaw)) {
350
+ printError(`Invalid role: ${roleRaw}. Available: ${roles.join(', ')}`);
351
+ process.exit(1);
352
+ }
353
+ const result = await runTeamWorkflow(task, teamSize, roleRaw);
354
+ printSuccess(result.summary);
355
+ printLine(`Plan: ${result.planPath}`);
356
+ printLine(`PRD: ${result.prdPath}`);
357
+ printLine(`Team report: ${result.teamPath}`);
358
+ if (result.agentIds.length > 0) {
359
+ printLine(chalk.dim(`Spawned ${result.agentIds.length} agents. Check status: bobo agents`));
360
+ }
361
+ });
362
+ program
363
+ .command('plan <task>')
364
+ .description('Generate a structured execution plan using planner agent')
365
+ .action(async (task) => {
366
+ const result = await runPlanWorkflow(task);
367
+ printSuccess(`Plan created with ${result.role} (${result.model})`);
368
+ printLine(`Plan: ${result.path}`);
369
+ if (result.agentId) {
370
+ printLine(chalk.dim(`Planner agent ${result.agentId} running in background. Check: bobo agent ${result.agentId}`));
371
+ }
372
+ });
373
+ program
374
+ .command('verify [target]')
375
+ .description('Run adversarial verification (build/test/lint checks)')
376
+ .action(async (target) => {
377
+ const result = await runVerifyWorkflow(target);
378
+ const icon = result.verdict === 'PASS' ? 'āœ“' : result.verdict === 'FAIL' ? 'āœ—' : '◐';
379
+ printSuccess(`Verification ${icon} ${result.verdict}`);
380
+ printLine(`Report: ${result.path}`);
381
+ });
382
+ program
383
+ .command('interview <topic>')
384
+ .description('Generate Socratic interview questions for a topic')
385
+ .action(async (topic) => {
386
+ const result = await runInterviewWorkflow(topic);
387
+ printSuccess('Interview questions generated');
388
+ printLine(`Report: ${result.path}`);
389
+ printLine(chalk.bold('\nBase questions:'));
390
+ result.questions.forEach((q, i) => printLine(chalk.dim(`${i + 1}.`) + ` ${q}`));
391
+ if (result.agentId) {
392
+ printLine(chalk.dim(`\nAgent ${result.agentId} generating deeper questions. Check: bobo agent ${result.agentId}`));
393
+ }
394
+ });
395
+ program
396
+ .command('ask <model> <prompt>')
397
+ .description('Query a specific AI model (provider name or model ID)')
398
+ .action(async (model, prompt) => {
399
+ const result = await runAskWorkflow(model, prompt);
400
+ if (result.error) {
401
+ printError(`Ask failed: ${result.error}`);
402
+ }
403
+ else {
404
+ printSuccess(`Response from ${result.model}`);
405
+ if (result.response) {
406
+ printLine(chalk.dim('─'.repeat(60)));
407
+ printLine(result.response);
408
+ printLine(chalk.dim('─'.repeat(60)));
409
+ }
410
+ }
411
+ printLine(`Full report: ${result.path}`);
412
+ });
413
+ // ─── Watch command ───────────────────────────────────────────
414
+ program
415
+ .command('watch')
416
+ .description('Watch files for changes and auto-run hooks (daemon-like mode)')
417
+ .option('--ignore <patterns>', 'Additional ignore patterns (comma-separated)', '')
418
+ .action((opts) => {
419
+ const ignore = opts.ignore ? opts.ignore.split(',').map(s => s.trim()) : [];
420
+ startWatch({
421
+ dir: process.cwd(),
422
+ recursive: true,
423
+ ignore,
424
+ });
425
+ });
426
+ // ─── Run command ─────────────────────────────────────────────
427
+ program
428
+ .command('run <task>')
429
+ .description('Autonomous mode: give a task, Bobo runs until done (like Claude Code agent loop)')
430
+ .option('--model <model>', 'Override model')
431
+ .option('--effort <level>', 'Effort level (low/medium/high)', 'high')
432
+ .option('--max-iterations <n>', 'Maximum iterations', '5')
433
+ .option('--log <path>', 'Log file path')
434
+ .action(async (task, opts) => {
435
+ await runAutonomous({
436
+ task,
437
+ model: opts.model,
438
+ effort: (opts.effort || 'high'),
439
+ permissionMode: 'auto',
440
+ maxIterations: parseInt(opts.maxIterations || '5', 10),
441
+ logFile: opts.log,
442
+ });
443
+ });
444
+ // ─── Evolve command ──────────────────────────────────────────
445
+ program
446
+ .command('evolve [focus]')
447
+ .description('Self-improvement: use Claude Code to enhance Bobo CLI itself')
448
+ .option('--dry-run', 'Show what would be improved without making changes')
449
+ .action(async (focus, opts) => {
450
+ const { isClaudeCodeAvailable: ccAvailable, executeClaudeCodeTool: ccExec } = await import('./tools/claude-code.js');
451
+ if (!ccAvailable()) {
452
+ printError('Claude Code not found. Install: npm install -g @anthropic-ai/claude-code');
453
+ return;
454
+ }
455
+ const areas = focus || 'streaming output quality, error handling, test coverage';
456
+ const dryRun = opts.dryRun ? ' List the improvements but DO NOT make changes.' : '';
457
+ printLine(chalk.cyan.bold('\n🧬 Bobo Evolve — Self-Improvement Mode\n'));
458
+ printLine(chalk.dim(` Focus: ${areas}`));
459
+ printLine(chalk.dim(` Mode: ${opts.dryRun ? 'dry-run (preview only)' : 'live (will modify code)'}`));
460
+ printLine(chalk.dim(' Using Claude Code as implementation engine\n'));
461
+ const task = `You are improving Bobo CLI (an AI coding assistant CLI tool).
462
+ The source code is in the current directory.
463
+ Focus on: ${areas}
464
+
465
+ Instructions:
466
+ 1. Read the relevant source files
467
+ 2. Identify specific improvements
468
+ 3. Implement the improvements${dryRun}
469
+ 4. Run \`npm run build\` to verify
470
+ 5. Summarize what you changed and why
471
+
472
+ Keep changes minimal and focused. Do not break existing functionality.`;
473
+ printLine(chalk.dim('Delegating to Claude Code...'));
474
+ const result = ccExec('claude_code', { task, cwd: process.cwd() });
475
+ printLine(result);
476
+ });
477
+ // ─── MCP command ─────────────────────────────────────────────
478
+ const mcpCmd = program.command('mcp').description('Manage MCP (Model Context Protocol) servers');
479
+ mcpCmd
480
+ .command('status')
481
+ .description('Show MCP server status')
482
+ .action(async () => {
483
+ await initMcpServers();
484
+ const status = getMcpStatus();
485
+ if (status.length === 0) {
486
+ printLine(chalk.dim('No MCP servers configured.'));
487
+ printLine(chalk.dim('Create ~/.bobo/mcp.json to add servers.'));
488
+ return;
489
+ }
490
+ printLine(chalk.cyan.bold('\nšŸ”Œ MCP Servers:\n'));
491
+ for (const s of status) {
492
+ const icon = s.ready ? chalk.green('ā—') : chalk.red('ā—');
493
+ printLine(` ${icon} ${chalk.bold(s.name)} [${s.transport}] — ${s.toolCount} tools`);
494
+ }
495
+ printLine();
496
+ shutdownMcpServers();
497
+ });
498
+ mcpCmd
499
+ .command('init')
500
+ .description('Create MCP configuration template')
501
+ .action(() => {
502
+ const configPath = join(getConfigDir(), 'mcp.json');
503
+ if (existsSync(configPath)) {
504
+ printWarning('mcp.json already exists');
505
+ return;
506
+ }
507
+ writeFileSync(configPath, JSON.stringify({
508
+ servers: [
509
+ {
510
+ name: 'example',
511
+ transport: 'stdio',
512
+ command: 'npx',
513
+ args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
514
+ _comment: 'Replace with your MCP server',
515
+ },
516
+ ],
517
+ }, null, 2) + '\n');
518
+ printSuccess(`Created ${configPath}`);
519
+ });
520
+ // ─── Hooks command ───────────────────────────────────────────
521
+ program
522
+ .command('hooks')
523
+ .description('Manage lifecycle hooks')
524
+ .option('--init', 'Create hooks.json template')
525
+ .action((opts) => {
526
+ if (opts.init) {
527
+ const hooksPath = join(getConfigDir(), 'hooks.json');
528
+ if (existsSync(hooksPath)) {
529
+ printWarning('hooks.json already exists');
530
+ return;
531
+ }
532
+ writeFileSync(hooksPath, initHooksTemplate() + '\n');
533
+ printSuccess(`Created ${hooksPath}`);
534
+ printLine(chalk.dim(' Available hooks: pre-edit, post-edit, pre-shell, post-shell, pre-commit, post-commit, session-end'));
535
+ return;
536
+ }
537
+ // Show current hooks
538
+ const hooksPath = join(getConfigDir(), 'hooks.json');
539
+ if (!existsSync(hooksPath)) {
540
+ printLine(chalk.dim('No hooks configured. Run: bobo hooks --init'));
541
+ return;
542
+ }
543
+ try {
544
+ const hooks = JSON.parse(readFileSync(hooksPath, 'utf-8'));
545
+ printLine(chalk.cyan.bold('\nšŸŖ Hooks:\n'));
546
+ for (const [event, cmds] of Object.entries(hooks)) {
547
+ if (event.startsWith('_'))
548
+ continue;
549
+ const arr = Array.isArray(cmds) ? cmds : [cmds];
550
+ if (arr.length === 0)
551
+ continue;
552
+ printLine(` ${chalk.bold(event)}`);
553
+ for (const cmd of arr) {
554
+ printLine(` → ${chalk.dim(String(cmd))}`);
555
+ }
556
+ }
557
+ printLine();
558
+ }
559
+ catch (_) {
560
+ /* intentionally ignored: command best-effort failure */
561
+ printError('Failed to parse hooks.json');
562
+ }
563
+ });
564
+ // ─── Structured knowledge commands ──────────────────────────
565
+ registerKnowledgeCommand(program);
566
+ registerRulesCommand(program);
567
+ registerStructuredSkillsCommand(program);
568
+ registerStructuredTemplateCommand(program);
569
+ // ─── Project subcommand ──────────────────────────────────────
570
+ const projectCmd = program.command('project').description('Manage project configuration');
571
+ projectCmd.command('init').description('Initialize .bobo/ project config').action(() => {
572
+ printSuccess(initProject());
573
+ });
574
+ // ─── Memory subcommand ───────────────────────────────────────
575
+ const memCmd = program.command('memory').description('Project-level memory');
576
+ memCmd.command('list')
577
+ .description('List project memory entries')
578
+ .option('--category <cat>', 'Filter by category')
579
+ .option('--keyword <kw>', 'Search keyword')
580
+ .action((opts) => {
581
+ const entries = queryMemory(opts.category, opts.keyword);
582
+ if (entries.length === 0) {
583
+ printLine(chalk.dim('No memory entries. Add with: bobo memory add <key> <value>'));
584
+ return;
585
+ }
586
+ printLine(chalk.cyan.bold('\nšŸ“ Project Memory:\n'));
587
+ for (const e of entries) {
588
+ printLine(` ${chalk.bold(e.key)} [${chalk.dim(e.category)}]`);
589
+ printLine(` ${e.value}`);
590
+ }
591
+ printLine();
592
+ });
593
+ memCmd.command('add <key> <value>')
594
+ .description('Add or update a memory entry')
595
+ .option('--category <cat>', 'Category: architecture|decision|convention|gotcha|todo', 'convention')
596
+ .action((key, value, opts) => {
597
+ addMemoryEntry({ key, value, category: (opts.category || 'convention') });
598
+ printSuccess(`Memory: ${key} = ${value}`);
599
+ });
600
+ memCmd.action(() => {
601
+ const entries = queryMemory();
602
+ if (entries.length === 0) {
603
+ printLine(chalk.dim('No memory entries. Add with: bobo memory add <key> <value>'));
604
+ return;
605
+ }
606
+ printLine(chalk.cyan.bold('\nšŸ“ Project Memory:\n'));
607
+ for (const e of entries) {
608
+ printLine(` ${chalk.bold(e.key)} [${chalk.dim(e.category)}] — ${e.value.slice(0, 80)}`);
609
+ }
610
+ printLine();
611
+ });
612
+ // ─── Notepad subcommand ──────────────────────────────────────
613
+ program
614
+ .command('note <text>')
615
+ .description('Quick note to .bobo/notepad.md')
616
+ .action((text) => {
617
+ appendNotepad(text);
618
+ printSuccess('Note saved.');
619
+ });
620
+ program
621
+ .command('notes')
622
+ .description('Show notepad')
623
+ .action(() => {
624
+ const content = readNotepad();
625
+ if (!content.trim()) {
626
+ printLine(chalk.dim('Notepad empty. Add with: bobo note "text"'));
627
+ return;
628
+ }
629
+ printLine(content);
630
+ });
631
+ // ─── Status subcommand ───────────────────────────────────────
632
+ program
633
+ .command('status')
634
+ .description('Show current status: agents, workflows, recovery context')
635
+ .action(() => {
636
+ const recovery = recoverContext();
637
+ const state = {
638
+ sessionId: recovery.lastSession?.sessionId ?? 'none',
639
+ model: recovery.lastSession?.model ?? 'unknown',
640
+ effort: recovery.lastSession?.effort ?? 'high',
641
+ activeAgents: [],
642
+ completedTasks: 0,
643
+ totalTasks: 0,
644
+ tokenUsage: recovery.lastSession?.tokenUsage ?? { input: 0, output: 0 },
645
+ };
646
+ printLine(renderStatusPanel(state));
647
+ if (recovery.activeWorkflows.length > 0) {
648
+ printLine(`\n${chalk.yellow('Active workflows:')} ${recovery.activeWorkflows.length}`);
649
+ }
650
+ printLine(`\n${chalk.dim(recovery.summary)}`);
651
+ });
652
+ // ─── Catalog subcommand ──────────────────────────────────────
653
+ program
654
+ .command('catalog')
655
+ .description('Show agent catalog (roles and capabilities)')
656
+ .action(() => {
657
+ const lanes = getAgentsByLane();
658
+ printLine(chalk.cyan.bold('\nšŸ¤– Agent Catalog:\n'));
659
+ for (const [lane, roles] of Object.entries(lanes)) {
660
+ printLine(chalk.bold(` ${lane.toUpperCase()} Lane:`));
661
+ for (const role of roles) {
662
+ const agent = AGENT_CATALOG[role];
663
+ printLine(` ${chalk.cyan(role.padEnd(12))} ${chalk.dim(agent.model.padEnd(8))} ${agent.description}`);
664
+ }
665
+ }
666
+ printLine();
667
+ });
668
+ }
669
+ //# sourceMappingURL=cli.js.map