brains-cli 0.1.0 → 1.0.2

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.
package/.env.example ADDED
@@ -0,0 +1,2 @@
1
+ # Get your API key at https://console.anthropic.com/
2
+ ANTHROPIC_API_KEY=sk-ant-your-key-here
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # 🧠 Brains.io
1
+ # brains-cli
2
2
 
3
3
  > **Install the world's greatest brains.** AI-powered dev agents you can browse, install, and run from your terminal.
4
4
 
5
- Brains are intelligent, conversational AI agents that act as specialized team members. Unlike dumb templates or scaffolders, Brains **talk to you**, understand your context, and generate production-grade output tailored to your specific needs.
5
+ Brains are intelligent, conversational AI agents powered by Claude. Unlike dumb templates or scaffolders, Brains **talk to you**, understand your context, and generate production-grade output tailored to your specific needs.
6
6
 
7
7
  ## Quick Start
8
8
 
@@ -10,29 +10,38 @@ Brains are intelligent, conversational AI agents that act as specialized team me
10
10
  # Install globally
11
11
  npm install -g brains-cli
12
12
 
13
+ # Set your Anthropic API key
14
+ brains config set api_key sk-ant-your-key-here
15
+
13
16
  # Explore available brains
14
17
  brains explore
15
18
 
16
- # Install a brain
19
+ # Install and run a brain
17
20
  brains install resume
18
-
19
- # Run it
20
21
  brains run resume
21
22
  ```
22
23
 
23
- ## What are Brains?
24
+ ## How It Works
25
+
26
+ 1. **Browse** the registry of 11 specialized AI agent Brains
27
+ 2. **Install** the ones you need to `~/.brains/`
28
+ 3. **Run** them — they interview you, then generate real output using Claude
29
+
30
+ Every Brain has a unique personality, methodology, and expertise defined by a detailed system prompt. When you run a Brain, it calls the Claude API with that system prompt and your context to generate real, production-grade results.
24
31
 
25
- A Brain is an AI agent with a specific personality, methodology, and expertise. Think of it as hiring a world-class specialist for any dev task — but they live in your terminal.
32
+ ## Brain Categories
26
33
 
27
- ### 🔨 Builder Brains — Generate entire projects
34
+ ### Builder Brains — Generate entire projects
28
35
 
29
36
  | Brain | Description | Price |
30
37
  |-------|-------------|-------|
31
- | `resume` | Build a stunning developer resume/portfolio site | Free |
38
+ | `resume` | Build a complete Next.js resume/portfolio site | Free |
32
39
  | `mvp` | Turn a product idea into a full-stack MVP | $9.99 |
33
40
  | `landing` | Generate high-converting landing pages | Free |
34
41
 
35
- ### 👤 Role Brains Act as team members
42
+ Builder brains ask you questions, then call Claude to generate a complete project with all files written to disk.
43
+
44
+ ### Role Brains — Act as team members
36
45
 
37
46
  | Brain | Description | Price |
38
47
  |-------|-------------|-------|
@@ -40,14 +49,18 @@ A Brain is an AI agent with a specific personality, methodology, and expertise.
40
49
  | `backend` | Backend architect (APIs, databases, infra) | $4.99 |
41
50
  | `architect` | System design, diagrams, and technical decisions | $9.99 |
42
51
 
43
- ### 🔍 Reviewer BrainsAnalyze & improve code
52
+ Role brains enter a conversational REPL chat back and forth with a specialist who maintains full context.
53
+
54
+ ### Reviewer Brains — Analyze & improve code
44
55
 
45
56
  | Brain | Description | Price |
46
57
  |-------|-------------|-------|
47
58
  | `reviewer` | Deep, senior-level code review | Free |
48
59
  | `security` | Find vulnerabilities and harden your app | $4.99 |
49
60
 
50
- ### Domain Brains Deep tech expertise
61
+ Reviewer brains scan your project files, send them to Claude, and output a structured review report.
62
+
63
+ ### Domain Brains — Deep tech expertise
51
64
 
52
65
  | Brain | Description | Price |
53
66
  |-------|-------------|-------|
@@ -55,11 +68,13 @@ A Brain is an AI agent with a specific personality, methodology, and expertise.
55
68
  | `supabase` | Supabase expert (auth, RLS, edge functions) | $4.99 |
56
69
  | `stripe` | Payments, subscriptions, webhooks | $4.99 |
57
70
 
71
+ Domain brains are conversational experts in a specific technology.
72
+
58
73
  ## Commands
59
74
 
60
75
  ### `brains explore`
61
76
 
62
- Browse and discover available Brains with an interactive terminal UI.
77
+ Interactive terminal browser for discovering Brains.
63
78
 
64
79
  ```bash
65
80
  brains explore # Browse all
@@ -69,27 +84,29 @@ brains explore -s "nextjs" # Search
69
84
 
70
85
  ### `brains install <brain>`
71
86
 
72
- Download and configure a Brain.
87
+ Download and save a Brain locally.
73
88
 
74
89
  ```bash
75
- brains install resume # Install resume builder
76
- brains install mvp # Install MVP generator
90
+ brains install resume
77
91
  brains i reviewer # Short alias
78
92
  ```
79
93
 
80
94
  ### `brains run <brain>`
81
95
 
82
- Activate a Brain in conversational mode.
96
+ The core feature. Activates a Brain and connects it to Claude.
83
97
 
84
98
  ```bash
85
- brains run resume # Start resume builder
86
- brains run reviewer # Review current project
99
+ brains run resume # Builder: generates a project
100
+ brains run reviewer # Reviewer: scans and reviews your codebase
101
+ brains run frontend # Role: conversational REPL with a specialist
87
102
  brains run mvp --with stripe supabase # Stack multiple brains
88
103
  ```
89
104
 
105
+ **Brain stacking** merges system prompts so a single agent has combined expertise.
106
+
90
107
  ### `brains list`
91
108
 
92
- Show all installed Brains.
109
+ Show installed Brains.
93
110
 
94
111
  ```bash
95
112
  brains list # or: brains ls
@@ -97,94 +114,60 @@ brains list # or: brains ls
97
114
 
98
115
  ### `brains create [name]`
99
116
 
100
- Author a new Brain from scratch.
117
+ Author a new Brain with an interactive wizard.
101
118
 
102
119
  ```bash
103
120
  brains create my-brain
104
- brains create --template builder # Start from a template
105
121
  ```
106
122
 
107
123
  ### `brains publish`
108
124
 
109
- Publish your Brain to the Brains.io registry.
125
+ Publish your Brain to the registry.
110
126
 
111
127
  ```bash
112
128
  cd my-brain/
113
- brains publish # Public
114
- brains publish --private # Private (team only)
129
+ brains publish
115
130
  ```
116
131
 
117
- ## Brain Stacking
132
+ ### `brains config`
118
133
 
119
- Combine multiple Brains for complex tasks:
134
+ Manage your API key and settings.
120
135
 
121
136
  ```bash
122
- # Build an MVP with Stripe payments and Supabase backend
123
- brains run mvp --with stripe supabase
124
-
125
- # Review code with security focus
126
- brains run reviewer --with security
137
+ brains config # Show all settings
138
+ brains config set api_key sk-ant-... # Save API key
139
+ brains config set model claude-sonnet-4-20250514
140
+ brains config get api_key # Show current key (masked)
141
+ brains config reset # Reset to defaults
127
142
  ```
128
143
 
129
- ## Creating Your Own Brain
144
+ ## Configuration
130
145
 
131
- Every Brain is defined by three files:
132
-
133
- ```
134
- my-brain/
135
- ├── brain.json ← Configuration (name, category, capabilities, price)
136
- ├── BRAIN.md ← System prompt & documentation
137
- └── README.md ← Public readme for the registry
138
- ```
139
-
140
- The **system prompt** in `BRAIN.md` is the core of your Brain. It defines:
141
- - **Personality** — How the brain communicates
142
- - **Methodology** — Step-by-step workflow
143
- - **Principles** — Rules and best practices
144
- - **Output format** — How results are structured
145
-
146
- ```bash
147
- # Scaffold a new brain
148
- brains create my-brain
149
-
150
- # Edit the system prompt
151
- $EDITOR my-brain/BRAIN.md
152
-
153
- # Test locally
154
- brains run my-brain
155
-
156
- # Publish to registry
157
- cd my-brain && brains publish
158
- ```
146
+ API key resolution order:
147
+ 1. `ANTHROPIC_API_KEY` environment variable
148
+ 2. `~/.brains/config.json` (set via `brains config set api_key`)
149
+ 3. Interactive prompt on first run
159
150
 
160
151
  ## Architecture
161
152
 
162
153
  ```
163
- ~/.brains/ ← Global brains directory
164
- ├── installed.json Registry of installed brains
154
+ ~/.brains/
155
+ ├── config.json # API key, model, preferences
156
+ ├── installed.json # Registry of installed brains
157
+ ├── sessions/ # Saved conversation transcripts
165
158
  ├── resume/
166
- │ ├── brain.json Brain config + system prompt
167
- │ └── BRAIN.md Human-readable docs
168
- ├── reviewer/
169
- │ ├── brain.json
170
- │ └── BRAIN.md
159
+ │ ├── brain.json # Brain config + system prompt
160
+ │ └── BRAIN.md # Human-readable docs
171
161
  └── ...
172
162
  ```
173
163
 
174
- ## Pricing
175
-
176
- | Tier | Price | Includes |
177
- |------|-------|----------|
178
- | Free | $0 | 4 free brains (resume, landing, reviewer, nextjs) |
179
- | Pro | $20/mo | All brains + brain stacking + priority |
180
- | Enterprise | Custom | Private registry + team management |
181
-
182
- Brain creators set their own prices. Platform takes 20%.
183
-
184
- ## Contributing
164
+ ## Tech Stack
185
165
 
186
- Want to contribute a Brain to the registry? See [CONTRIBUTING.md](./CONTRIBUTING.md).
166
+ - **Runtime**: Node.js 16+ (CommonJS)
167
+ - **AI**: Claude via `@anthropic-ai/sdk` (streaming)
168
+ - **CLI**: Commander.js, Inquirer.js
169
+ - **UI**: Chalk, Ora, Boxen, Figlet, gradient-string, cli-table3
187
170
 
188
171
  ## License
189
172
 
190
- MIT © Brains.io
173
+ MIT
package/bin/brains.js CHANGED
@@ -10,8 +10,24 @@ const { run } = require('../src/commands/run');
10
10
  const { list } = require('../src/commands/list');
11
11
  const { create } = require('../src/commands/create');
12
12
  const { publish } = require('../src/commands/publish');
13
+ const { config } = require('../src/commands/config');
13
14
  const { showBanner } = require('../src/utils/ui');
14
15
 
16
+ // Graceful shutdown on Ctrl+C
17
+ process.on('SIGINT', () => {
18
+ console.log('\n');
19
+ process.exit(0);
20
+ });
21
+
22
+ // Suppress unhandled rejection noise — errors are handled in commands
23
+ process.on('unhandledRejection', (err) => {
24
+ if (err && err.message !== 'NO_API_KEY') {
25
+ const chalk = require('chalk');
26
+ console.error(`\n ${chalk.red('✗')} Unexpected error: ${err.message || err}\n`);
27
+ }
28
+ process.exit(1);
29
+ });
30
+
15
31
  showBanner();
16
32
 
17
33
  program
@@ -39,7 +55,7 @@ program
39
55
  program
40
56
  .command('run <brain>')
41
57
  .alias('r')
42
- .description('Run an installed Brain in conversational mode')
58
+ .description('Run an installed Brain the core AI experience')
43
59
  .option('-w, --with <brains...>', 'Stack multiple brains together')
44
60
  .option('-d, --dir <directory>', 'Target directory (default: current)')
45
61
  .option('--no-interactive', 'Run without interactive prompts')
@@ -66,6 +82,12 @@ program
66
82
  .option('--private', 'Publish as a private Brain')
67
83
  .action(publish);
68
84
 
85
+ // ─── Config: Manage settings ───
86
+ program
87
+ .command('config [action] [args...]')
88
+ .description('Manage settings (set/get api_key, model, etc.)')
89
+ .action(config);
90
+
69
91
  program.parse(process.argv);
70
92
 
71
93
  // If no args, show help
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brains-cli",
3
- "version": "0.1.0",
3
+ "version": "1.0.2",
4
4
  "description": "🧠 Brains.io — Install AI-powered dev agents. Build, review, architect, and ship with one command.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -24,6 +24,8 @@
24
24
  "author": "Brains.io",
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
+ "@anthropic-ai/sdk": "^0.39.0",
28
+ "openai": "^4.78.0",
27
29
  "chalk": "^4.1.2",
28
30
  "commander": "^11.1.0",
29
31
  "inquirer": "^8.2.6",
package/src/api.js ADDED
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * API Router — delegates to the active provider.
5
+ *
6
+ * All brain commands call streamMessage/sendMessage from here.
7
+ * This module resolves which provider to use and routes accordingly.
8
+ */
9
+
10
+ const { getActiveProvider, getProviderApiKey, getProviderModel, getProviderBaseUrl } = require('./config');
11
+ const { getProviderDef } = require('./providers/registry');
12
+
13
+ /**
14
+ * Stream a response from the active AI provider.
15
+ * Returns the full assistant message text.
16
+ *
17
+ * @param {Object} opts
18
+ * @param {string} opts.systemPrompt
19
+ * @param {Array} opts.messages - [{role, content}]
20
+ * @param {number} [opts.maxTokens]
21
+ * @param {string} [opts.model] - Override model
22
+ * @param {Function} [opts.onText]
23
+ * @param {Function} [opts.onStart]
24
+ * @param {Function} [opts.onEnd]
25
+ * @returns {Promise<string>}
26
+ */
27
+ async function streamMessage(opts) {
28
+ const {
29
+ systemPrompt,
30
+ messages,
31
+ maxTokens = 8096,
32
+ model,
33
+ onText = (text) => process.stdout.write(text),
34
+ onStart = () => {},
35
+ onEnd = () => {},
36
+ } = opts;
37
+
38
+ const providerName = getActiveProvider();
39
+ const providerDef = getProviderDef(providerName);
40
+
41
+ if (!providerDef) {
42
+ throw new Error(`Unknown provider "${providerName}". Run: brains config set provider <name>`);
43
+ }
44
+
45
+ const apiKey = getProviderApiKey(providerName);
46
+ const modelId = model || getProviderModel(providerName);
47
+ const baseURL = getProviderBaseUrl(providerName);
48
+
49
+ if (!apiKey && providerDef.requires_key !== false) {
50
+ throw new Error('NO_API_KEY');
51
+ }
52
+
53
+ const callOpts = {
54
+ apiKey,
55
+ systemPrompt,
56
+ messages,
57
+ maxTokens,
58
+ model: modelId,
59
+ onText,
60
+ onStart,
61
+ onEnd,
62
+ };
63
+
64
+ if (providerDef.type === 'anthropic') {
65
+ const provider = require('./providers/anthropic');
66
+ return provider.streamMessage(callOpts);
67
+ } else {
68
+ const provider = require('./providers/openai-compat');
69
+ callOpts.baseURL = baseURL;
70
+ return provider.streamMessage(callOpts);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Send a single message (non-streaming).
76
+ */
77
+ async function sendMessage(opts) {
78
+ const {
79
+ systemPrompt,
80
+ messages,
81
+ maxTokens = 8096,
82
+ model,
83
+ } = opts;
84
+
85
+ const providerName = getActiveProvider();
86
+ const providerDef = getProviderDef(providerName);
87
+
88
+ if (!providerDef) {
89
+ throw new Error(`Unknown provider "${providerName}".`);
90
+ }
91
+
92
+ const apiKey = getProviderApiKey(providerName);
93
+ const modelId = model || getProviderModel(providerName);
94
+ const baseURL = getProviderBaseUrl(providerName);
95
+
96
+ if (!apiKey && providerDef.requires_key !== false) {
97
+ throw new Error('NO_API_KEY');
98
+ }
99
+
100
+ const callOpts = { apiKey, systemPrompt, messages, maxTokens, model: modelId };
101
+
102
+ if (providerDef.type === 'anthropic') {
103
+ const provider = require('./providers/anthropic');
104
+ return provider.sendMessage(callOpts);
105
+ } else {
106
+ const provider = require('./providers/openai-compat');
107
+ callOpts.baseURL = baseURL;
108
+ return provider.sendMessage(callOpts);
109
+ }
110
+ }
111
+
112
+ module.exports = {
113
+ streamMessage,
114
+ sendMessage,
115
+ };
@@ -0,0 +1,236 @@
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+ const Table = require('cli-table3');
5
+ const {
6
+ loadConfig, saveConfig, setConfigValue,
7
+ getActiveProvider, getProviderApiKey, getProviderModel, getProviderBaseUrl,
8
+ setProviderApiKey, setProviderModel, setActiveProvider,
9
+ maskApiKey, DEFAULTS,
10
+ } = require('../config');
11
+ const { getAllProviders, getProviderDef, getAllProviderNames } = require('../providers/registry');
12
+ const { showSuccess, showError, showInfo, showWarning, ACCENT, DIM, SUCCESS } = require('../utils/ui');
13
+
14
+ async function configCommand(action, args) {
15
+ if (!action) {
16
+ showCurrentConfig();
17
+ return;
18
+ }
19
+
20
+ // ─── brains config set <key> <value> ───
21
+ if (action === 'set') {
22
+ if (args.length < 2) {
23
+ showError('Usage: brains config set <key> <value>');
24
+ console.log('');
25
+ showInfo(`${ACCENT('provider')} — Switch AI provider (groq, anthropic, gemini, openrouter, ollama)`);
26
+ showInfo(`${ACCENT('api_key')} — Set API key for current provider`);
27
+ showInfo(`${ACCENT('model')} — Set model for current provider`);
28
+ showInfo(`${ACCENT('max_tokens')} — Set max tokens`);
29
+ console.log('');
30
+ showInfo(`Example: ${ACCENT('brains config set provider groq')}`);
31
+ showInfo(`Example: ${ACCENT('brains config set api_key gsk_...')}`);
32
+ return;
33
+ }
34
+
35
+ const key = args[0];
36
+ let value = args.slice(1).join(' ');
37
+
38
+ // ── Switch provider ──
39
+ if (key === 'provider') {
40
+ const providerDef = getProviderDef(value);
41
+ if (!providerDef) {
42
+ showError(`Unknown provider "${value}".`);
43
+ showInfo(`Available: ${getAllProviderNames().map(n => ACCENT(n)).join(', ')}`);
44
+ return;
45
+ }
46
+
47
+ setActiveProvider(value);
48
+ showSuccess(`Provider switched to ${chalk.bold(providerDef.name)}`);
49
+
50
+ // Check if key is configured
51
+ const apiKey = getProviderApiKey(value);
52
+ if (!apiKey && providerDef.requires_key !== false) {
53
+ console.log('');
54
+ showWarning(`No API key set for ${providerDef.name}.`);
55
+ showInfo(`Get one at: ${ACCENT(providerDef.key_url)}`);
56
+ showInfo(`Then run: ${ACCENT(`brains config set api_key <your-key>`)}`);
57
+ } else {
58
+ showInfo(`Model: ${DIM(getProviderModel(value))}`);
59
+ }
60
+ console.log('');
61
+ return;
62
+ }
63
+
64
+ // ── Set API key (for current provider) ──
65
+ if (key === 'api_key') {
66
+ const currentProvider = getActiveProvider();
67
+ setProviderApiKey(currentProvider, value);
68
+ showSuccess(`API key saved for ${getProviderDef(currentProvider).name}: ${maskApiKey(value)}`);
69
+ console.log('');
70
+ return;
71
+ }
72
+
73
+ // ── Set model (for current provider) ──
74
+ if (key === 'model') {
75
+ const currentProvider = getActiveProvider();
76
+ setProviderModel(currentProvider, value);
77
+ showSuccess(`Model set for ${getProviderDef(currentProvider).name}: ${value}`);
78
+ console.log('');
79
+ return;
80
+ }
81
+
82
+ // ── Generic config keys ──
83
+ const validKeys = ['max_tokens', 'theme', 'telemetry'];
84
+ if (!validKeys.includes(key)) {
85
+ showError(`Unknown config key "${key}".`);
86
+ showInfo(`Valid keys: provider, api_key, model, ${validKeys.map(k => ACCENT(k)).join(', ')}`);
87
+ return;
88
+ }
89
+
90
+ if (key === 'max_tokens') value = parseInt(value, 10);
91
+ if (key === 'telemetry') value = value === 'true';
92
+
93
+ setConfigValue(key, value);
94
+ showSuccess(`${key} = ${value}`);
95
+ console.log('');
96
+ return;
97
+ }
98
+
99
+ // ─── brains config get <key> ───
100
+ if (action === 'get') {
101
+ if (args.length < 1) {
102
+ showError('Usage: brains config get <key>');
103
+ return;
104
+ }
105
+
106
+ const key = args[0];
107
+
108
+ if (key === 'provider') {
109
+ const p = getActiveProvider();
110
+ const def = getProviderDef(p);
111
+ console.log(`\n ${chalk.bold('provider')} = ${p} (${def ? def.name : 'unknown'})\n`);
112
+ return;
113
+ }
114
+
115
+ if (key === 'api_key') {
116
+ const p = getActiveProvider();
117
+ const k = getProviderApiKey(p);
118
+ console.log(`\n ${chalk.bold('api_key')} [${p}] = ${k ? maskApiKey(k) : chalk.red('(not set)')}\n`);
119
+ return;
120
+ }
121
+
122
+ if (key === 'model') {
123
+ const p = getActiveProvider();
124
+ console.log(`\n ${chalk.bold('model')} [${p}] = ${getProviderModel(p)}\n`);
125
+ return;
126
+ }
127
+
128
+ const config = loadConfig();
129
+ const value = config[key];
130
+ if (value === undefined) {
131
+ showInfo(`${key} is not set (default: ${DEFAULTS[key] || 'none'})`);
132
+ } else {
133
+ console.log(`\n ${chalk.bold(key)} = ${value}\n`);
134
+ }
135
+ return;
136
+ }
137
+
138
+ // ─── brains config providers ───
139
+ if (action === 'providers') {
140
+ showProviderList();
141
+ return;
142
+ }
143
+
144
+ // ─── brains config reset ───
145
+ if (action === 'reset') {
146
+ saveConfig({ ...DEFAULTS, providers: {} });
147
+ showSuccess('Config reset to defaults (provider: groq).');
148
+ console.log('');
149
+ return;
150
+ }
151
+
152
+ showError(`Unknown action "${action}". Use: set, get, providers, reset, or no action to view all.`);
153
+ }
154
+
155
+ // ═══════════════════════════════════════════
156
+ // Display helpers
157
+ // ═══════════════════════════════════════════
158
+
159
+ function showCurrentConfig() {
160
+ const config = loadConfig();
161
+ const currentProvider = getActiveProvider();
162
+ const providerDef = getProviderDef(currentProvider);
163
+ const apiKey = getProviderApiKey(currentProvider);
164
+ const model = getProviderModel(currentProvider);
165
+
166
+ console.log(`\n ${chalk.bold('Brains Configuration')}\n`);
167
+
168
+ const table = new Table({
169
+ style: { head: [], border: ['dim'] },
170
+ colWidths: [20, 46],
171
+ });
172
+
173
+ table.push(
174
+ [chalk.bold('provider'), `${currentProvider} ${DIM(`(${providerDef ? providerDef.name : 'unknown'})`)}`],
175
+ [chalk.bold('api_key'), apiKey ? maskApiKey(apiKey) : (providerDef && providerDef.requires_key === false ? DIM('(not required)') : chalk.red('(not set)'))],
176
+ [chalk.bold('model'), model],
177
+ [chalk.bold('max_tokens'), String(config.max_tokens || DEFAULTS.max_tokens)],
178
+ [chalk.bold('telemetry'), String(config.telemetry ?? DEFAULTS.telemetry)]
179
+ );
180
+
181
+ console.log(table.toString());
182
+
183
+ if (!apiKey && providerDef && providerDef.requires_key !== false) {
184
+ console.log('');
185
+ showInfo(`Set API key: ${ACCENT(`brains config set api_key <your-${currentProvider}-key>`)}`);
186
+ if (providerDef.key_url) {
187
+ showInfo(`Get one at: ${ACCENT(providerDef.key_url)}`);
188
+ }
189
+ }
190
+
191
+ console.log('');
192
+ showInfo(`Switch provider: ${ACCENT('brains config set provider <name>')}`);
193
+ showInfo(`See all providers: ${ACCENT('brains config providers')}`);
194
+ console.log('');
195
+ }
196
+
197
+ function showProviderList() {
198
+ const currentProvider = getActiveProvider();
199
+ const allProviders = getAllProviders();
200
+
201
+ console.log(`\n ${chalk.bold('Available Providers')}\n`);
202
+
203
+ const table = new Table({
204
+ head: [
205
+ chalk.white.bold('Provider'),
206
+ chalk.white.bold('Type'),
207
+ chalk.white.bold('Default Model'),
208
+ chalk.white.bold('Free?'),
209
+ chalk.white.bold('Status'),
210
+ ],
211
+ style: { head: [], border: ['dim'] },
212
+ colWidths: [16, 12, 36, 8, 14],
213
+ });
214
+
215
+ for (const [name, def] of Object.entries(allProviders)) {
216
+ const isActive = name === currentProvider;
217
+ const hasKey = !!getProviderApiKey(name) || def.requires_key === false;
218
+
219
+ table.push([
220
+ isActive ? chalk.hex('#7B2FFF').bold(`▸ ${name}`) : ` ${name}`,
221
+ DIM(def.type),
222
+ DIM(def.default_model),
223
+ def.free ? SUCCESS('Yes') : DIM('Paid'),
224
+ hasKey ? SUCCESS('Ready') : chalk.yellow('Need key'),
225
+ ]);
226
+ }
227
+
228
+ console.log(table.toString());
229
+
230
+ console.log('');
231
+ showInfo(`Active: ${ACCENT(currentProvider)}`);
232
+ showInfo(`Switch: ${ACCENT('brains config set provider <name>')}`);
233
+ console.log('');
234
+ }
235
+
236
+ module.exports = { config: configCommand };
@@ -15,7 +15,9 @@ function getInstalledBrains() {
15
15
  if (fs.existsSync(INSTALLED_FILE)) {
16
16
  return JSON.parse(fs.readFileSync(INSTALLED_FILE, 'utf-8'));
17
17
  }
18
- } catch (e) {}
18
+ } catch (e) {
19
+ // corrupted file, return empty
20
+ }
19
21
  return {};
20
22
  }
21
23