@morebeans/cli 2.0.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/index.ts +382 -0
- package/package.json +46 -0
package/index.ts
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* BEANS CLI - Setup and configure BEANS for your project
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bunx beans init # Initialize BEANS in current project
|
|
7
|
+
* bunx beans config # Configure API keys interactively
|
|
8
|
+
* bunx beans config --valyu # Set Valyu API key
|
|
9
|
+
* bunx beans doctor # Check installation status
|
|
10
|
+
* bunx beans upgrade # Update to latest version
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { $ } from "bun";
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
15
|
+
import { join, resolve } from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
|
|
18
|
+
const VERSION = "2.0.0";
|
|
19
|
+
const BEANS_HOME = join(homedir(), ".beans");
|
|
20
|
+
const BEANS_CONFIG = join(BEANS_HOME, "config.json");
|
|
21
|
+
|
|
22
|
+
// Colors
|
|
23
|
+
const c = {
|
|
24
|
+
reset: "\x1b[0m",
|
|
25
|
+
bold: "\x1b[1m",
|
|
26
|
+
red: "\x1b[31m",
|
|
27
|
+
green: "\x1b[32m",
|
|
28
|
+
yellow: "\x1b[33m",
|
|
29
|
+
blue: "\x1b[34m",
|
|
30
|
+
cyan: "\x1b[36m",
|
|
31
|
+
dim: "\x1b[2m",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function log(msg: string) { console.log(msg); }
|
|
35
|
+
function info(msg: string) { console.log(`${c.blue}ℹ${c.reset} ${msg}`); }
|
|
36
|
+
function success(msg: string) { console.log(`${c.green}✓${c.reset} ${msg}`); }
|
|
37
|
+
function warn(msg: string) { console.log(`${c.yellow}⚠${c.reset} ${msg}`); }
|
|
38
|
+
function error(msg: string) { console.log(`${c.red}✗${c.reset} ${msg}`); }
|
|
39
|
+
|
|
40
|
+
interface BeansConfig {
|
|
41
|
+
valyu_api_key?: string;
|
|
42
|
+
github_token?: string;
|
|
43
|
+
default_model?: string;
|
|
44
|
+
installed_at?: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function loadConfig(): BeansConfig {
|
|
48
|
+
if (!existsSync(BEANS_CONFIG)) return {};
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(readFileSync(BEANS_CONFIG, "utf-8"));
|
|
51
|
+
} catch {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function saveConfig(config: BeansConfig) {
|
|
57
|
+
mkdirSync(BEANS_HOME, { recursive: true });
|
|
58
|
+
writeFileSync(BEANS_CONFIG, JSON.stringify(config, null, 2));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function maskKey(key: string): string {
|
|
62
|
+
if (!key || key.length < 8) return "***";
|
|
63
|
+
return key.slice(0, 4) + "..." + key.slice(-4);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function prompt(question: string, hidden = false): Promise<string> {
|
|
67
|
+
process.stdout.write(`${c.cyan}?${c.reset} ${question} `);
|
|
68
|
+
|
|
69
|
+
if (hidden) {
|
|
70
|
+
// For hidden input, we'd need readline or similar
|
|
71
|
+
// For now, just read normally with a note
|
|
72
|
+
process.stdout.write(`${c.dim}(input hidden)${c.reset} `);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const reader = Bun.stdin.stream().getReader();
|
|
76
|
+
const { value } = await reader.read();
|
|
77
|
+
reader.releaseLock();
|
|
78
|
+
|
|
79
|
+
return new TextDecoder().decode(value).trim();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
83
|
+
// COMMANDS
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
85
|
+
|
|
86
|
+
async function cmdInit() {
|
|
87
|
+
log(`\n${c.bold}${c.blue}🫘 BEANS Setup${c.reset}\n`);
|
|
88
|
+
|
|
89
|
+
const cwd = process.cwd();
|
|
90
|
+
const config = loadConfig();
|
|
91
|
+
|
|
92
|
+
// Check if already initialized
|
|
93
|
+
if (existsSync(join(cwd, ".claude/plugins/beans"))) {
|
|
94
|
+
warn("BEANS already installed in this project");
|
|
95
|
+
info("Run 'beans doctor' to check status");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Create directories
|
|
100
|
+
info("Creating directories...");
|
|
101
|
+
const dirs = [
|
|
102
|
+
".claude/plugins",
|
|
103
|
+
".claude/agents",
|
|
104
|
+
".beads/analysis",
|
|
105
|
+
".beads/context",
|
|
106
|
+
".beads/cache",
|
|
107
|
+
".beads/logs",
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
for (const dir of dirs) {
|
|
111
|
+
mkdirSync(join(cwd, dir), { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
success("Directories created");
|
|
114
|
+
|
|
115
|
+
// Find plugin source
|
|
116
|
+
const pluginSources = [
|
|
117
|
+
join(cwd, "submodules/beans/plugin"),
|
|
118
|
+
join(homedir(), ".beans/plugin"),
|
|
119
|
+
join(import.meta.dir, "../plugin"),
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
let pluginSource: string | null = null;
|
|
123
|
+
for (const src of pluginSources) {
|
|
124
|
+
if (existsSync(src)) {
|
|
125
|
+
pluginSource = src;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!pluginSource) {
|
|
131
|
+
error("Could not find BEANS plugin source");
|
|
132
|
+
info("Clone the beans repo first:");
|
|
133
|
+
log(` git clone https://github.com/shinyobjectz/beans.git ~/.beans`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Symlink plugin
|
|
138
|
+
info("Installing plugin...");
|
|
139
|
+
const pluginDest = join(cwd, ".claude/plugins/beans");
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
await $`ln -sf ${pluginSource} ${pluginDest}`;
|
|
143
|
+
success(`Plugin linked from ${pluginSource}`);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
error(`Failed to link plugin: ${e}`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Copy agents
|
|
150
|
+
info("Installing agents...");
|
|
151
|
+
const agentsSrc = join(pluginSource, "agents");
|
|
152
|
+
if (existsSync(agentsSrc)) {
|
|
153
|
+
await $`cp -r ${agentsSrc}/* ${join(cwd, ".claude/agents/")}`.nothrow();
|
|
154
|
+
success("Agents installed");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Track installation
|
|
158
|
+
config.installed_at = config.installed_at || [];
|
|
159
|
+
if (!config.installed_at.includes(cwd)) {
|
|
160
|
+
config.installed_at.push(cwd);
|
|
161
|
+
}
|
|
162
|
+
saveConfig(config);
|
|
163
|
+
|
|
164
|
+
// Check for API keys
|
|
165
|
+
log("");
|
|
166
|
+
if (!config.valyu_api_key) {
|
|
167
|
+
warn("Valyu API key not configured (optional, for research)");
|
|
168
|
+
info("Run 'beans config --valyu' to set it");
|
|
169
|
+
} else {
|
|
170
|
+
success(`Valyu API key: ${maskKey(config.valyu_api_key)}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
log(`\n${c.green}${c.bold}✅ BEANS initialized!${c.reset}\n`);
|
|
174
|
+
log("Next steps:");
|
|
175
|
+
log(` ${c.cyan}beans doctor${c.reset} # Verify setup`);
|
|
176
|
+
log(` ${c.cyan}beans config${c.reset} # Configure API keys`);
|
|
177
|
+
log(` ${c.cyan}/beans${c.reset} # In Claude Code: start building`);
|
|
178
|
+
log("");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function cmdConfig(args: string[]) {
|
|
182
|
+
log(`\n${c.bold}${c.blue}🔧 BEANS Configuration${c.reset}\n`);
|
|
183
|
+
|
|
184
|
+
const config = loadConfig();
|
|
185
|
+
|
|
186
|
+
if (args.includes("--valyu") || args.includes("-v")) {
|
|
187
|
+
log("Enter your Valyu API key (get one at https://valyu.ai):");
|
|
188
|
+
const key = await prompt("Valyu API Key:", true);
|
|
189
|
+
if (key) {
|
|
190
|
+
config.valyu_api_key = key;
|
|
191
|
+
saveConfig(config);
|
|
192
|
+
success(`Valyu API key saved: ${maskKey(key)}`);
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (args.includes("--github") || args.includes("-g")) {
|
|
198
|
+
log("Enter your GitHub token (for PR creation):");
|
|
199
|
+
const key = await prompt("GitHub Token:", true);
|
|
200
|
+
if (key) {
|
|
201
|
+
config.github_token = key;
|
|
202
|
+
saveConfig(config);
|
|
203
|
+
success(`GitHub token saved: ${maskKey(key)}`);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (args.includes("--show")) {
|
|
209
|
+
log("Current configuration:");
|
|
210
|
+
log(` Config file: ${BEANS_CONFIG}`);
|
|
211
|
+
log(` Valyu API Key: ${config.valyu_api_key ? maskKey(config.valyu_api_key) : c.dim + "(not set)" + c.reset}`);
|
|
212
|
+
log(` GitHub Token: ${config.github_token ? maskKey(config.github_token) : c.dim + "(not set)" + c.reset}`);
|
|
213
|
+
log(` Installed at: ${(config.installed_at || []).length} projects`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Interactive config
|
|
218
|
+
log("Configure BEANS (press Enter to skip):\n");
|
|
219
|
+
|
|
220
|
+
// Valyu
|
|
221
|
+
const currentValyu = config.valyu_api_key ? ` [${maskKey(config.valyu_api_key)}]` : "";
|
|
222
|
+
log(`Valyu API Key${currentValyu}:`);
|
|
223
|
+
const valyu = await prompt("", true);
|
|
224
|
+
if (valyu) config.valyu_api_key = valyu;
|
|
225
|
+
|
|
226
|
+
// GitHub
|
|
227
|
+
const currentGithub = config.github_token ? ` [${maskKey(config.github_token)}]` : "";
|
|
228
|
+
log(`GitHub Token${currentGithub}:`);
|
|
229
|
+
const github = await prompt("", true);
|
|
230
|
+
if (github) config.github_token = github;
|
|
231
|
+
|
|
232
|
+
saveConfig(config);
|
|
233
|
+
success("Configuration saved");
|
|
234
|
+
|
|
235
|
+
log(`\nConfig stored at: ${c.dim}${BEANS_CONFIG}${c.reset}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function cmdDoctor() {
|
|
239
|
+
log(`\n${c.bold}${c.blue}🩺 BEANS Doctor${c.reset}\n`);
|
|
240
|
+
|
|
241
|
+
const cwd = process.cwd();
|
|
242
|
+
const config = loadConfig();
|
|
243
|
+
let issues = 0;
|
|
244
|
+
|
|
245
|
+
// Check CLI tools
|
|
246
|
+
log(`${c.bold}CLI Tools${c.reset}`);
|
|
247
|
+
const tools = ["bd", "ast-grep", "repomix"];
|
|
248
|
+
for (const tool of tools) {
|
|
249
|
+
try {
|
|
250
|
+
await $`which ${tool}`.quiet();
|
|
251
|
+
success(tool);
|
|
252
|
+
} catch {
|
|
253
|
+
warn(`${tool} not found (optional)`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Check directories
|
|
258
|
+
log(`\n${c.bold}Directories${c.reset}`);
|
|
259
|
+
const dirs = [".claude/plugins/beans", ".beads", ".claude/agents"];
|
|
260
|
+
for (const dir of dirs) {
|
|
261
|
+
if (existsSync(join(cwd, dir))) {
|
|
262
|
+
success(dir);
|
|
263
|
+
} else {
|
|
264
|
+
error(dir);
|
|
265
|
+
issues++;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Check config
|
|
270
|
+
log(`\n${c.bold}Configuration${c.reset}`);
|
|
271
|
+
log(` Config: ${BEANS_CONFIG}`);
|
|
272
|
+
if (config.valyu_api_key) {
|
|
273
|
+
success(`Valyu API Key: ${maskKey(config.valyu_api_key)}`);
|
|
274
|
+
} else {
|
|
275
|
+
warn("Valyu API Key: not set (run 'beans config --valyu')");
|
|
276
|
+
}
|
|
277
|
+
if (config.github_token) {
|
|
278
|
+
success(`GitHub Token: ${maskKey(config.github_token)}`);
|
|
279
|
+
} else {
|
|
280
|
+
warn("GitHub Token: not set (run 'beans config --github')");
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Check plugin
|
|
284
|
+
log(`\n${c.bold}Plugin${c.reset}`);
|
|
285
|
+
const pluginPath = join(cwd, ".claude/plugins/beans");
|
|
286
|
+
if (existsSync(pluginPath)) {
|
|
287
|
+
const pluginJson = join(pluginPath, "plugin.json");
|
|
288
|
+
if (existsSync(pluginJson)) {
|
|
289
|
+
try {
|
|
290
|
+
const plugin = JSON.parse(readFileSync(pluginJson, "utf-8"));
|
|
291
|
+
success(`BEANS v${plugin.version || "unknown"}`);
|
|
292
|
+
} catch {
|
|
293
|
+
warn("plugin.json invalid");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Count components
|
|
298
|
+
const commands = join(pluginPath, "commands");
|
|
299
|
+
const agents = join(pluginPath, "agents");
|
|
300
|
+
if (existsSync(commands)) {
|
|
301
|
+
const bdCount = existsSync(join(commands, "bd")) ?
|
|
302
|
+
(await $`ls ${join(commands, "bd")}`.text()).split("\n").filter(Boolean).length : 0;
|
|
303
|
+
const ralphCount = existsSync(join(commands, "ralph")) ?
|
|
304
|
+
(await $`ls ${join(commands, "ralph")}`.text()).split("\n").filter(Boolean).length : 0;
|
|
305
|
+
log(` Commands: ${bdCount} bd, ${ralphCount} ralph`);
|
|
306
|
+
}
|
|
307
|
+
if (existsSync(agents)) {
|
|
308
|
+
const agentCount = (await $`ls ${agents}`.text()).split("\n").filter(Boolean).length;
|
|
309
|
+
log(` Agents: ${agentCount}`);
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
error("Plugin not installed");
|
|
313
|
+
issues++;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
log("");
|
|
317
|
+
if (issues === 0) {
|
|
318
|
+
log(`${c.green}${c.bold}✅ BEANS is healthy!${c.reset}`);
|
|
319
|
+
} else {
|
|
320
|
+
log(`${c.yellow}⚠ ${issues} issue(s) found. Run 'beans init' to fix.${c.reset}`);
|
|
321
|
+
}
|
|
322
|
+
log("");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function cmdHelp() {
|
|
326
|
+
log(`
|
|
327
|
+
${c.bold}${c.blue}🫘 BEANS CLI v${VERSION}${c.reset}
|
|
328
|
+
|
|
329
|
+
${c.bold}Usage:${c.reset}
|
|
330
|
+
beans <command> [options]
|
|
331
|
+
|
|
332
|
+
${c.bold}Commands:${c.reset}
|
|
333
|
+
${c.cyan}init${c.reset} Initialize BEANS in current project
|
|
334
|
+
${c.cyan}config${c.reset} Configure API keys interactively
|
|
335
|
+
${c.cyan}config --valyu${c.reset} Set Valyu API key
|
|
336
|
+
${c.cyan}config --github${c.reset} Set GitHub token
|
|
337
|
+
${c.cyan}config --show${c.reset} Show current configuration
|
|
338
|
+
${c.cyan}doctor${c.reset} Check installation status
|
|
339
|
+
${c.cyan}help${c.reset} Show this help message
|
|
340
|
+
|
|
341
|
+
${c.bold}In Claude Code:${c.reset}
|
|
342
|
+
${c.cyan}/beans${c.reset} List ready issues
|
|
343
|
+
${c.cyan}/beans "Add feature"${c.reset} Full autonomous flow
|
|
344
|
+
${c.cyan}/beans task-001${c.reset} Build existing issue
|
|
345
|
+
${c.cyan}/bd:create "Bug"${c.reset} Create beads issue
|
|
346
|
+
${c.cyan}/ralph:start${c.reset} Start spec workflow
|
|
347
|
+
|
|
348
|
+
${c.bold}Environment:${c.reset}
|
|
349
|
+
Config stored at: ${c.dim}~/.beans/config.json${c.reset}
|
|
350
|
+
|
|
351
|
+
${c.bold}More info:${c.reset}
|
|
352
|
+
https://github.com/shinyobjectz/beans
|
|
353
|
+
`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
357
|
+
// MAIN
|
|
358
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
359
|
+
|
|
360
|
+
const [command, ...args] = process.argv.slice(2);
|
|
361
|
+
|
|
362
|
+
switch (command) {
|
|
363
|
+
case "init":
|
|
364
|
+
await cmdInit();
|
|
365
|
+
break;
|
|
366
|
+
case "config":
|
|
367
|
+
await cmdConfig(args);
|
|
368
|
+
break;
|
|
369
|
+
case "doctor":
|
|
370
|
+
await cmdDoctor();
|
|
371
|
+
break;
|
|
372
|
+
case "help":
|
|
373
|
+
case "--help":
|
|
374
|
+
case "-h":
|
|
375
|
+
case undefined:
|
|
376
|
+
await cmdHelp();
|
|
377
|
+
break;
|
|
378
|
+
default:
|
|
379
|
+
error(`Unknown command: ${command}`);
|
|
380
|
+
await cmdHelp();
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@morebeans/cli",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "BEANS CLI - Setup and configure autonomous development for Claude Code",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"beans": "./index.ts"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "bun run index.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"index.ts",
|
|
15
|
+
"package.json"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"beans",
|
|
19
|
+
"claude-code",
|
|
20
|
+
"claude",
|
|
21
|
+
"ai",
|
|
22
|
+
"autonomous",
|
|
23
|
+
"development",
|
|
24
|
+
"beads",
|
|
25
|
+
"ralph",
|
|
26
|
+
"cli"
|
|
27
|
+
],
|
|
28
|
+
"author": "Project.Social",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/shinyobjectz/beans.git",
|
|
33
|
+
"directory": "cli"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/shinyobjectz/beans#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/shinyobjectz/beans/issues"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18",
|
|
41
|
+
"bun": ">=1.0"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
}
|
|
46
|
+
}
|