brains-cli 0.1.0
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/.claude/skills/founder/SKILL.md +316 -0
- package/README.md +190 -0
- package/bin/brains.js +74 -0
- package/package.json +43 -0
- package/src/commands/create.js +171 -0
- package/src/commands/explore.js +137 -0
- package/src/commands/install.js +139 -0
- package/src/commands/list.js +71 -0
- package/src/commands/publish.js +65 -0
- package/src/commands/run.js +487 -0
- package/src/registry.js +659 -0
- package/src/utils/ui.js +144 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const inquirer = require('inquirer');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const { showSuccess, showInfo, ACCENT, DIM } = require('../utils/ui');
|
|
9
|
+
|
|
10
|
+
async function create(name, options) {
|
|
11
|
+
console.log('');
|
|
12
|
+
console.log(chalk.bold(' 🧠 Create a new Brain\n'));
|
|
13
|
+
|
|
14
|
+
const answers = await inquirer.prompt([
|
|
15
|
+
{
|
|
16
|
+
type: 'input',
|
|
17
|
+
name: 'name',
|
|
18
|
+
message: 'Brain name:',
|
|
19
|
+
default: name || 'my-brain',
|
|
20
|
+
validate: (v) => (v.length > 0 ? true : 'Name is required'),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: 'input',
|
|
24
|
+
name: 'description',
|
|
25
|
+
message: 'Short description (one line):',
|
|
26
|
+
validate: (v) => (v.length > 0 ? true : 'Description is required'),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'list',
|
|
30
|
+
name: 'category',
|
|
31
|
+
message: 'Category:',
|
|
32
|
+
choices: [
|
|
33
|
+
{ name: '🔨 Builder — Generates entire projects', value: 'builder' },
|
|
34
|
+
{ name: '👤 Role — Acts as a team member', value: 'role' },
|
|
35
|
+
{ name: '🔍 Reviewer — Analyzes & improves code', value: 'reviewer' },
|
|
36
|
+
{ name: '⚡ Domain — Deep expertise in specific tech', value: 'domain' },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: 'input',
|
|
41
|
+
name: 'stack',
|
|
42
|
+
message: 'Tech stack (comma separated):',
|
|
43
|
+
default: 'TypeScript, Node.js',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: 'list',
|
|
47
|
+
name: 'price',
|
|
48
|
+
message: 'Pricing:',
|
|
49
|
+
choices: [
|
|
50
|
+
{ name: 'Free', value: 0 },
|
|
51
|
+
{ name: '$4.99', value: 4.99 },
|
|
52
|
+
{ name: '$9.99', value: 9.99 },
|
|
53
|
+
{ name: '$19.99', value: 19.99 },
|
|
54
|
+
{ name: '$49.99', value: 49.99 },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'editor',
|
|
59
|
+
name: 'systemPrompt',
|
|
60
|
+
message: 'Write your brain\'s system prompt (this is the core of your brain):',
|
|
61
|
+
default: `You are a [ROLE] Brain.\n\nPERSONALITY:\n- [describe how this brain thinks]\n- [describe its approach]\n\nWORKFLOW:\n1. [first step]\n2. [second step]\n3. [third step]\n\nPRINCIPLES:\n- [key principle 1]\n- [key principle 2]`,
|
|
62
|
+
},
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
const brainId = answers.name.toLowerCase().replace(/\s+/g, '-');
|
|
66
|
+
const brainDir = path.join(process.cwd(), brainId);
|
|
67
|
+
|
|
68
|
+
const spinner = ora({
|
|
69
|
+
text: 'Scaffolding brain...',
|
|
70
|
+
color: 'magenta',
|
|
71
|
+
spinner: 'dots12',
|
|
72
|
+
}).start();
|
|
73
|
+
|
|
74
|
+
// Create brain directory
|
|
75
|
+
fs.mkdirSync(brainDir, { recursive: true });
|
|
76
|
+
|
|
77
|
+
// Create brain.json
|
|
78
|
+
const brainConfig = {
|
|
79
|
+
id: brainId,
|
|
80
|
+
name: answers.name,
|
|
81
|
+
version: '0.1.0',
|
|
82
|
+
category: answers.category,
|
|
83
|
+
description: answers.description,
|
|
84
|
+
stack: answers.stack.split(',').map((s) => s.trim()),
|
|
85
|
+
price: answers.price,
|
|
86
|
+
capabilities: [],
|
|
87
|
+
tags: [],
|
|
88
|
+
systemPrompt: answers.systemPrompt,
|
|
89
|
+
questions: [],
|
|
90
|
+
};
|
|
91
|
+
fs.writeFileSync(path.join(brainDir, 'brain.json'), JSON.stringify(brainConfig, null, 2));
|
|
92
|
+
|
|
93
|
+
// Create BRAIN.md
|
|
94
|
+
const mdContent = `# 🧠 ${answers.name}
|
|
95
|
+
|
|
96
|
+
> ${answers.description}
|
|
97
|
+
|
|
98
|
+
**Category:** ${answers.category}
|
|
99
|
+
**Stack:** ${answers.stack}
|
|
100
|
+
**Price:** ${answers.price === 0 ? 'Free' : `$${answers.price}`}
|
|
101
|
+
|
|
102
|
+
## System Prompt
|
|
103
|
+
|
|
104
|
+
${answers.systemPrompt}
|
|
105
|
+
|
|
106
|
+
## Capabilities
|
|
107
|
+
|
|
108
|
+
_Add your brain's capabilities here._
|
|
109
|
+
|
|
110
|
+
## Questions
|
|
111
|
+
|
|
112
|
+
_Define the questions your brain asks users when it runs._
|
|
113
|
+
|
|
114
|
+
## Changelog
|
|
115
|
+
|
|
116
|
+
### v0.1.0
|
|
117
|
+
- Initial release
|
|
118
|
+
`;
|
|
119
|
+
fs.writeFileSync(path.join(brainDir, 'BRAIN.md'), mdContent);
|
|
120
|
+
|
|
121
|
+
// Create README
|
|
122
|
+
const readmeContent = `# ${answers.name}
|
|
123
|
+
|
|
124
|
+
A **${answers.category}** Brain for [Brains.io](https://brains.io).
|
|
125
|
+
|
|
126
|
+
${answers.description}
|
|
127
|
+
|
|
128
|
+
## Install
|
|
129
|
+
|
|
130
|
+
\`\`\`bash
|
|
131
|
+
brains install ${brainId}
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
## Usage
|
|
135
|
+
|
|
136
|
+
\`\`\`bash
|
|
137
|
+
brains run ${brainId}
|
|
138
|
+
\`\`\`
|
|
139
|
+
|
|
140
|
+
## Stack
|
|
141
|
+
|
|
142
|
+
${answers.stack
|
|
143
|
+
.split(',')
|
|
144
|
+
.map((s) => `- ${s.trim()}`)
|
|
145
|
+
.join('\n')}
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT
|
|
150
|
+
`;
|
|
151
|
+
fs.writeFileSync(path.join(brainDir, 'README.md'), readmeContent);
|
|
152
|
+
|
|
153
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
154
|
+
spinner.succeed('Brain scaffolded');
|
|
155
|
+
|
|
156
|
+
console.log('');
|
|
157
|
+
showSuccess(`Brain "${answers.name}" created at ./${brainId}/\n`);
|
|
158
|
+
|
|
159
|
+
console.log(chalk.dim(` ${brainId}/`));
|
|
160
|
+
console.log(chalk.dim(' ├── brain.json ← Brain configuration'));
|
|
161
|
+
console.log(chalk.dim(' ├── BRAIN.md ← System prompt & docs'));
|
|
162
|
+
console.log(chalk.dim(' └── README.md ← Public readme'));
|
|
163
|
+
|
|
164
|
+
console.log('');
|
|
165
|
+
showInfo(`Edit ${ACCENT('brain.json')} to refine your brain's config.`);
|
|
166
|
+
showInfo(`Edit ${ACCENT('BRAIN.md')} to improve the system prompt.`);
|
|
167
|
+
showInfo(`When ready: ${ACCENT('brains publish')}`);
|
|
168
|
+
console.log('');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = { create };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const inquirer = require('inquirer');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const { getAllBrains, getBrainsByCategory, searchBrains, getBrainById } = require('../registry');
|
|
6
|
+
const { createBrainTable, createDetailView, showInfo, ACCENT, DIM, SUCCESS } = require('../utils/ui');
|
|
7
|
+
|
|
8
|
+
async function explore(options) {
|
|
9
|
+
let brains;
|
|
10
|
+
|
|
11
|
+
if (options.search) {
|
|
12
|
+
brains = searchBrains(options.search);
|
|
13
|
+
console.log(DIM(`\n Searching for "${options.search}"...\n`));
|
|
14
|
+
} else if (options.category) {
|
|
15
|
+
brains = getBrainsByCategory(options.category);
|
|
16
|
+
console.log(DIM(`\n Showing ${options.category} brains...\n`));
|
|
17
|
+
} else {
|
|
18
|
+
brains = getAllBrains();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (brains.length === 0) {
|
|
22
|
+
console.log(chalk.yellow('\n No brains found matching your query.\n'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Show summary stats
|
|
27
|
+
const free = brains.filter((b) => b.price === 0).length;
|
|
28
|
+
const totalInstalls = brains.reduce((sum, b) => sum + b.installs, 0);
|
|
29
|
+
console.log(
|
|
30
|
+
` ${chalk.bold(brains.length)} brains available • ${SUCCESS(
|
|
31
|
+
`${free} free`
|
|
32
|
+
)} • ${DIM(`${(totalInstalls / 1000).toFixed(0)}K+ total installs`)}\n`
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Category quick filters
|
|
36
|
+
const categories = {
|
|
37
|
+
'🔨 Builder': 'builder',
|
|
38
|
+
'👤 Role': 'role',
|
|
39
|
+
'🔍 Reviewer': 'reviewer',
|
|
40
|
+
'⚡ Domain': 'domain',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
console.log(
|
|
44
|
+
` ${Object.keys(categories)
|
|
45
|
+
.map((c) => {
|
|
46
|
+
const cat = categories[c];
|
|
47
|
+
const count = getBrainsByCategory(cat).length;
|
|
48
|
+
return DIM(`${c} (${count})`);
|
|
49
|
+
})
|
|
50
|
+
.join(' ')}\n`
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Show brain table
|
|
54
|
+
console.log(createBrainTable(brains));
|
|
55
|
+
|
|
56
|
+
// Interactive selection
|
|
57
|
+
const { action } = await inquirer.prompt([
|
|
58
|
+
{
|
|
59
|
+
type: 'list',
|
|
60
|
+
name: 'action',
|
|
61
|
+
message: 'What would you like to do?',
|
|
62
|
+
choices: [
|
|
63
|
+
{ name: '📖 View brain details', value: 'detail' },
|
|
64
|
+
{ name: '⬇️ Install a brain', value: 'install' },
|
|
65
|
+
{ name: '🔍 Search brains', value: 'search' },
|
|
66
|
+
{ name: '📂 Filter by category', value: 'category' },
|
|
67
|
+
new inquirer.Separator(),
|
|
68
|
+
{ name: '👋 Exit', value: 'exit' },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
if (action === 'exit') return;
|
|
74
|
+
|
|
75
|
+
if (action === 'detail' || action === 'install') {
|
|
76
|
+
const { brainId } = await inquirer.prompt([
|
|
77
|
+
{
|
|
78
|
+
type: 'list',
|
|
79
|
+
name: 'brainId',
|
|
80
|
+
message: action === 'detail' ? 'Select a brain to view:' : 'Select a brain to install:',
|
|
81
|
+
choices: brains.map((b) => ({
|
|
82
|
+
name: `${b.name} ${DIM(`— ${b.shortDesc}`)}`,
|
|
83
|
+
value: b.id,
|
|
84
|
+
})),
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
const brain = getBrainById(brainId);
|
|
89
|
+
|
|
90
|
+
if (action === 'detail') {
|
|
91
|
+
console.log(createDetailView(brain));
|
|
92
|
+
|
|
93
|
+
const { next } = await inquirer.prompt([
|
|
94
|
+
{
|
|
95
|
+
type: 'confirm',
|
|
96
|
+
name: 'next',
|
|
97
|
+
message: `Install ${brain.name}?`,
|
|
98
|
+
default: true,
|
|
99
|
+
},
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
if (next) {
|
|
103
|
+
const { install } = require('./install');
|
|
104
|
+
await install(brain.id, {});
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
const { install } = require('./install');
|
|
108
|
+
await install(brain.id, {});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (action === 'search') {
|
|
113
|
+
const { query } = await inquirer.prompt([
|
|
114
|
+
{ type: 'input', name: 'query', message: 'Search:' },
|
|
115
|
+
]);
|
|
116
|
+
await explore({ search: query });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (action === 'category') {
|
|
120
|
+
const { cat } = await inquirer.prompt([
|
|
121
|
+
{
|
|
122
|
+
type: 'list',
|
|
123
|
+
name: 'cat',
|
|
124
|
+
message: 'Select category:',
|
|
125
|
+
choices: [
|
|
126
|
+
{ name: '🔨 Builder — Generate entire projects', value: 'builder' },
|
|
127
|
+
{ name: '👤 Role — Act as a team member', value: 'role' },
|
|
128
|
+
{ name: '🔍 Reviewer — Analyze & improve code', value: 'reviewer' },
|
|
129
|
+
{ name: '⚡ Domain — Deep tech expertise', value: 'domain' },
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
]);
|
|
133
|
+
await explore({ category: cat });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { explore };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const { getBrainById, getAllBrains } = require('../registry');
|
|
8
|
+
const { showSuccess, showError, showInfo, showBox, ACCENT, DIM, SUCCESS } = require('../utils/ui');
|
|
9
|
+
|
|
10
|
+
const BRAINS_DIR = path.join(require('os').homedir(), '.brains');
|
|
11
|
+
const INSTALLED_FILE = path.join(BRAINS_DIR, 'installed.json');
|
|
12
|
+
|
|
13
|
+
function getInstalledBrains() {
|
|
14
|
+
try {
|
|
15
|
+
if (fs.existsSync(INSTALLED_FILE)) {
|
|
16
|
+
return JSON.parse(fs.readFileSync(INSTALLED_FILE, 'utf-8'));
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {}
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function saveInstalledBrains(installed) {
|
|
23
|
+
if (!fs.existsSync(BRAINS_DIR)) {
|
|
24
|
+
fs.mkdirSync(BRAINS_DIR, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
fs.writeFileSync(INSTALLED_FILE, JSON.stringify(installed, null, 2));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function install(brainName, options) {
|
|
30
|
+
const brain = getBrainById(brainName);
|
|
31
|
+
|
|
32
|
+
if (!brain) {
|
|
33
|
+
showError(`Brain "${brainName}" not found.`);
|
|
34
|
+
console.log('');
|
|
35
|
+
showInfo('Available brains:');
|
|
36
|
+
console.log('');
|
|
37
|
+
getAllBrains().forEach((b) => {
|
|
38
|
+
console.log(` ${ACCENT(b.id.padEnd(16))} ${DIM(b.shortDesc)}`);
|
|
39
|
+
});
|
|
40
|
+
console.log('');
|
|
41
|
+
showInfo(`Run ${ACCENT('brains explore')} to browse all brains.`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const installed = getInstalledBrains();
|
|
46
|
+
|
|
47
|
+
if (installed[brain.id]) {
|
|
48
|
+
showInfo(`${brain.name} is already installed (v${installed[brain.id].version}).`);
|
|
49
|
+
showInfo(`Run ${ACCENT(`brains run ${brain.id}`)} to use it.`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('');
|
|
54
|
+
showInfo(`Installing ${chalk.bold(brain.name)} ${DIM(`(${brain.category})`)}`);
|
|
55
|
+
|
|
56
|
+
if (brain.price > 0) {
|
|
57
|
+
console.log(` ${chalk.hex('#FF8800')('$')} Price: $${brain.price}`);
|
|
58
|
+
console.log(` ${DIM('(Payment would be processed here in production)')}`);
|
|
59
|
+
} else {
|
|
60
|
+
console.log(` ${SUCCESS('✦')} ${SUCCESS('Free brain')}`);
|
|
61
|
+
}
|
|
62
|
+
console.log('');
|
|
63
|
+
|
|
64
|
+
// Simulate installation steps
|
|
65
|
+
const steps = [
|
|
66
|
+
{ text: 'Resolving brain from registry', time: 400 },
|
|
67
|
+
{ text: 'Downloading brain package', time: 800 },
|
|
68
|
+
{ text: 'Verifying integrity', time: 300 },
|
|
69
|
+
{ text: 'Installing dependencies', time: 600 },
|
|
70
|
+
{ text: 'Configuring brain agent', time: 500 },
|
|
71
|
+
{ text: 'Running setup hooks', time: 300 },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const step of steps) {
|
|
75
|
+
const spinner = ora({
|
|
76
|
+
text: step.text,
|
|
77
|
+
color: 'magenta',
|
|
78
|
+
spinner: 'dots12',
|
|
79
|
+
}).start();
|
|
80
|
+
await new Promise((r) => setTimeout(r, step.time));
|
|
81
|
+
spinner.succeed(chalk.dim(step.text));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Save to installed registry
|
|
85
|
+
installed[brain.id] = {
|
|
86
|
+
name: brain.name,
|
|
87
|
+
category: brain.category,
|
|
88
|
+
version: '0.1.0',
|
|
89
|
+
installedAt: new Date().toISOString(),
|
|
90
|
+
};
|
|
91
|
+
saveInstalledBrains(installed);
|
|
92
|
+
|
|
93
|
+
// Save brain config file
|
|
94
|
+
const brainDir = path.join(BRAINS_DIR, brain.id);
|
|
95
|
+
if (!fs.existsSync(brainDir)) {
|
|
96
|
+
fs.mkdirSync(brainDir, { recursive: true });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Write the brain's system prompt as its config
|
|
100
|
+
const brainConfig = {
|
|
101
|
+
id: brain.id,
|
|
102
|
+
name: brain.name,
|
|
103
|
+
version: '0.1.0',
|
|
104
|
+
category: brain.category,
|
|
105
|
+
capabilities: brain.capabilities,
|
|
106
|
+
stack: brain.stack,
|
|
107
|
+
systemPrompt: brain.systemPrompt,
|
|
108
|
+
questions: brain.questions,
|
|
109
|
+
};
|
|
110
|
+
fs.writeFileSync(path.join(brainDir, 'brain.json'), JSON.stringify(brainConfig, null, 2));
|
|
111
|
+
|
|
112
|
+
// Write the system prompt as a readable markdown file
|
|
113
|
+
const mdContent = `# 🧠 ${brain.name}
|
|
114
|
+
|
|
115
|
+
**Category:** ${brain.category}
|
|
116
|
+
**Stack:** ${brain.stack.join(', ')}
|
|
117
|
+
|
|
118
|
+
## Capabilities
|
|
119
|
+
${brain.capabilities.map((c) => `- ${c}`).join('\n')}
|
|
120
|
+
|
|
121
|
+
## System Prompt
|
|
122
|
+
${brain.systemPrompt}
|
|
123
|
+
`;
|
|
124
|
+
fs.writeFileSync(path.join(brainDir, 'BRAIN.md'), mdContent);
|
|
125
|
+
|
|
126
|
+
console.log('');
|
|
127
|
+
showSuccess(`${chalk.bold(brain.name)} installed successfully!`);
|
|
128
|
+
console.log('');
|
|
129
|
+
showInfo(`Brain location: ${DIM(brainDir)}`);
|
|
130
|
+
showInfo(`Run it with: ${ACCENT(`brains run ${brain.id}`)}`);
|
|
131
|
+
|
|
132
|
+
if (brain.category === 'reviewer') {
|
|
133
|
+
showInfo(`Pro tip: ${DIM(`cd your-project && brains run ${brain.id}`)}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log('');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = { install };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const Table = require('cli-table3');
|
|
7
|
+
const { showInfo, showError, ACCENT, DIM, SUCCESS } = require('../utils/ui');
|
|
8
|
+
|
|
9
|
+
const BRAINS_DIR = path.join(require('os').homedir(), '.brains');
|
|
10
|
+
const INSTALLED_FILE = path.join(BRAINS_DIR, 'installed.json');
|
|
11
|
+
|
|
12
|
+
async function list() {
|
|
13
|
+
let installed;
|
|
14
|
+
try {
|
|
15
|
+
installed = JSON.parse(fs.readFileSync(INSTALLED_FILE, 'utf-8'));
|
|
16
|
+
} catch (e) {
|
|
17
|
+
console.log('');
|
|
18
|
+
showInfo('No brains installed yet.');
|
|
19
|
+
showInfo(`Run ${ACCENT('brains explore')} to discover brains.`);
|
|
20
|
+
console.log('');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const entries = Object.entries(installed);
|
|
25
|
+
if (entries.length === 0) {
|
|
26
|
+
console.log('');
|
|
27
|
+
showInfo('No brains installed yet.');
|
|
28
|
+
showInfo(`Run ${ACCENT('brains explore')} to discover brains.`);
|
|
29
|
+
console.log('');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(`\n ${chalk.bold('Installed Brains')} ${DIM(`(${entries.length})`)}\n`);
|
|
34
|
+
|
|
35
|
+
const categoryColors = {
|
|
36
|
+
builder: chalk.hex('#FF3366'),
|
|
37
|
+
role: chalk.hex('#7B2FFF'),
|
|
38
|
+
reviewer: chalk.hex('#00AAFF'),
|
|
39
|
+
domain: chalk.hex('#00CC88'),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const table = new Table({
|
|
43
|
+
head: [
|
|
44
|
+
chalk.white.bold('Brain'),
|
|
45
|
+
chalk.white.bold('Category'),
|
|
46
|
+
chalk.white.bold('Version'),
|
|
47
|
+
chalk.white.bold('Installed'),
|
|
48
|
+
],
|
|
49
|
+
style: { head: [], border: ['dim'] },
|
|
50
|
+
colWidths: [24, 14, 12, 24],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
entries.forEach(([id, info]) => {
|
|
54
|
+
const catColor = categoryColors[info.category] || chalk.white;
|
|
55
|
+
const date = new Date(info.installedAt).toLocaleDateString();
|
|
56
|
+
|
|
57
|
+
table.push([
|
|
58
|
+
chalk.bold(info.name || id),
|
|
59
|
+
catColor(info.category),
|
|
60
|
+
DIM(`v${info.version}`),
|
|
61
|
+
DIM(date),
|
|
62
|
+
]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
console.log(table.toString());
|
|
66
|
+
console.log('');
|
|
67
|
+
showInfo(`Run a brain: ${ACCENT('brains run <name>')}`);
|
|
68
|
+
console.log('');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { list };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const { showSuccess, showError, showInfo, ACCENT, DIM } = require('../utils/ui');
|
|
8
|
+
|
|
9
|
+
async function publish(options) {
|
|
10
|
+
const brainJsonPath = path.join(process.cwd(), 'brain.json');
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(brainJsonPath)) {
|
|
13
|
+
showError('No brain.json found in current directory.');
|
|
14
|
+
showInfo(`Run ${ACCENT('brains create')} first, or cd into a brain directory.`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const brain = JSON.parse(fs.readFileSync(brainJsonPath, 'utf-8'));
|
|
19
|
+
|
|
20
|
+
console.log('');
|
|
21
|
+
console.log(chalk.bold(` Publishing ${chalk.hex('#7B2FFF')(brain.name)} v${brain.version}\n`));
|
|
22
|
+
|
|
23
|
+
// Validation
|
|
24
|
+
const validationSteps = [
|
|
25
|
+
{ text: 'Validating brain.json schema', time: 400 },
|
|
26
|
+
{ text: 'Checking system prompt quality', time: 600 },
|
|
27
|
+
{ text: 'Validating capabilities list', time: 300 },
|
|
28
|
+
{ text: 'Running safety checks', time: 500 },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
for (const step of validationSteps) {
|
|
32
|
+
const spinner = ora({
|
|
33
|
+
text: step.text,
|
|
34
|
+
color: 'magenta',
|
|
35
|
+
spinner: 'dots12',
|
|
36
|
+
}).start();
|
|
37
|
+
await new Promise((r) => setTimeout(r, step.time));
|
|
38
|
+
spinner.succeed(chalk.dim(step.text));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// "Upload"
|
|
42
|
+
const publishSpinner = ora({
|
|
43
|
+
text: options.private ? 'Publishing as private brain...' : 'Publishing to Brains.io registry...',
|
|
44
|
+
color: 'green',
|
|
45
|
+
spinner: 'dots12',
|
|
46
|
+
}).start();
|
|
47
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
48
|
+
publishSpinner.succeed(chalk.dim('Published'));
|
|
49
|
+
|
|
50
|
+
console.log('');
|
|
51
|
+
showSuccess(`${brain.name} v${brain.version} is now live on Brains.io!`);
|
|
52
|
+
console.log('');
|
|
53
|
+
showInfo(`Registry: ${ACCENT(`https://brains.io/brain/${brain.id}`)}`);
|
|
54
|
+
showInfo(`Install: ${DIM(`brains install ${brain.id}`)}`);
|
|
55
|
+
|
|
56
|
+
if (options.private) {
|
|
57
|
+
showInfo(`Visibility: ${chalk.hex('#FF8800')('Private')} (only you and your team)`);
|
|
58
|
+
} else {
|
|
59
|
+
showInfo(`Visibility: ${chalk.hex('#00CC88')('Public')} (listed in explore)`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { publish };
|