@morebeans/cli 2.3.5 → 2.5.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 +199 -21
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -7,15 +7,16 @@
|
|
|
7
7
|
* bunx beans config # Configure API keys interactively
|
|
8
8
|
* bunx beans config --valyu # Set Valyu API key
|
|
9
9
|
* bunx beans doctor # Check installation status
|
|
10
|
-
* bunx beans
|
|
10
|
+
* bunx beans research # Manage research database
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { $ } from "bun";
|
|
14
|
+
import { Database } from "bun:sqlite";
|
|
14
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
15
16
|
import { join, resolve } from "path";
|
|
16
17
|
import { homedir } from "os";
|
|
17
18
|
|
|
18
|
-
const VERSION = "2.
|
|
19
|
+
const VERSION = "2.5.0";
|
|
19
20
|
const BEANS_HOME = join(homedir(), ".beans");
|
|
20
21
|
const BEANS_CONFIG = join(BEANS_HOME, "config.json");
|
|
21
22
|
|
|
@@ -221,8 +222,12 @@ async function cmdInit() {
|
|
|
221
222
|
const commandsDir = join(cwd, ".claude/commands");
|
|
222
223
|
mkdirSync(commandsDir, { recursive: true });
|
|
223
224
|
|
|
224
|
-
// Register
|
|
225
|
-
const beansCommands = [
|
|
225
|
+
// Register ALL BEANS commands (core + phase commands)
|
|
226
|
+
const beansCommands = [
|
|
227
|
+
"beans.md", "beans-status.md", "beans-land.md", "beans-agents.md",
|
|
228
|
+
"beans-new.md", "beans-research.md", "beans-requirements.md",
|
|
229
|
+
"beans-design.md", "beans-tasks.md", "beans-implement.md"
|
|
230
|
+
];
|
|
226
231
|
for (const cmd of beansCommands) {
|
|
227
232
|
const src = join(pluginSource, "commands", cmd);
|
|
228
233
|
if (existsSync(src)) {
|
|
@@ -230,13 +235,22 @@ async function cmdInit() {
|
|
|
230
235
|
}
|
|
231
236
|
}
|
|
232
237
|
|
|
238
|
+
// Install beans-loop script to ~/.local/bin
|
|
239
|
+
const loopScript = join(beansHome, "scripts/beans-loop.sh");
|
|
240
|
+
const localBin = join(homedir(), ".local/bin");
|
|
241
|
+
if (existsSync(loopScript)) {
|
|
242
|
+
mkdirSync(localBin, { recursive: true });
|
|
243
|
+
await $`cp ${loopScript} ${join(localBin, "beans-loop")}`.nothrow();
|
|
244
|
+
await $`chmod +x ${join(localBin, "beans-loop")}`.nothrow();
|
|
245
|
+
}
|
|
246
|
+
|
|
233
247
|
// Copy settings.json if not exists
|
|
234
248
|
const settingsSrc = join(pluginSource, "settings.json");
|
|
235
249
|
const settingsDest = join(cwd, ".claude/settings.json");
|
|
236
250
|
if (existsSync(settingsSrc) && !existsSync(settingsDest)) {
|
|
237
251
|
await $`cp ${settingsSrc} ${settingsDest}`.nothrow();
|
|
238
252
|
}
|
|
239
|
-
success("Commands registered (
|
|
253
|
+
success("Commands registered (10 BEANS commands + beans-loop)");
|
|
240
254
|
|
|
241
255
|
// Initialize beads issue tracker (uses .beans directory now)
|
|
242
256
|
info("Initializing issue tracker...");
|
|
@@ -270,9 +284,9 @@ async function cmdInit() {
|
|
|
270
284
|
log(`\n${c.green}${c.bold}✅ BEANS initialized!${c.reset}\n`);
|
|
271
285
|
log("Next steps:");
|
|
272
286
|
log(` ${c.cyan}beans doctor${c.reset} # Verify setup`);
|
|
273
|
-
log(` ${c.cyan}beans
|
|
274
|
-
log(` ${c.cyan}/beans${c.reset} # In Claude Code: start building`);
|
|
287
|
+
log(` ${c.cyan}/beans:new${c.reset} # Create new spec with PRD flow`);
|
|
275
288
|
log(` ${c.cyan}/beans:agents${c.reset} # Browse 127+ specialized subagents`);
|
|
289
|
+
log(` ${c.cyan}beans-loop${c.reset} # Autonomous execution (bash)`);
|
|
276
290
|
log("");
|
|
277
291
|
}
|
|
278
292
|
|
|
@@ -510,6 +524,164 @@ async function cmdDoctor(args: string[]) {
|
|
|
510
524
|
log("");
|
|
511
525
|
}
|
|
512
526
|
|
|
527
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
528
|
+
// RESEARCH DATABASE
|
|
529
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
530
|
+
|
|
531
|
+
function getResearchDb() {
|
|
532
|
+
const dbPath = join(process.cwd(), ".beans/research.db");
|
|
533
|
+
if (!existsSync(join(process.cwd(), ".beans"))) {
|
|
534
|
+
mkdirSync(join(process.cwd(), ".beans"), { recursive: true });
|
|
535
|
+
}
|
|
536
|
+
const db = new Database(dbPath);
|
|
537
|
+
|
|
538
|
+
// Init schema
|
|
539
|
+
db.run(`
|
|
540
|
+
CREATE TABLE IF NOT EXISTS research (
|
|
541
|
+
id TEXT PRIMARY KEY,
|
|
542
|
+
issue_id TEXT,
|
|
543
|
+
query TEXT NOT NULL,
|
|
544
|
+
source TEXT NOT NULL CHECK(source IN ('valyu', 'web', 'codebase')),
|
|
545
|
+
title TEXT NOT NULL,
|
|
546
|
+
content TEXT NOT NULL,
|
|
547
|
+
url TEXT,
|
|
548
|
+
relevance REAL DEFAULT 0.5,
|
|
549
|
+
metadata TEXT DEFAULT '{}',
|
|
550
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
551
|
+
UNIQUE(issue_id, url, title)
|
|
552
|
+
)
|
|
553
|
+
`);
|
|
554
|
+
|
|
555
|
+
db.run(`
|
|
556
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS research_fts USING fts5(
|
|
557
|
+
title, content, query, content='research', content_rowid='rowid'
|
|
558
|
+
)
|
|
559
|
+
`);
|
|
560
|
+
|
|
561
|
+
return db;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async function cmdResearch(args: string[]) {
|
|
565
|
+
const subCmd = args[0];
|
|
566
|
+
const db = getResearchDb();
|
|
567
|
+
|
|
568
|
+
if (subCmd === "list" || !subCmd) {
|
|
569
|
+
const issueId = args.find(a => a.startsWith("--issue="))?.split("=")[1];
|
|
570
|
+
const source = args.find(a => a.startsWith("--source="))?.split("=")[1];
|
|
571
|
+
const limit = parseInt(args.find(a => a.startsWith("--limit="))?.split("=")[1] || "20");
|
|
572
|
+
|
|
573
|
+
let sql = `SELECT id, issue_id, source, title, substr(content, 1, 100) as preview, relevance, created_at FROM research WHERE 1=1`;
|
|
574
|
+
const params: any[] = [];
|
|
575
|
+
|
|
576
|
+
if (issueId) { sql += ` AND issue_id = ?`; params.push(issueId); }
|
|
577
|
+
if (source) { sql += ` AND source = ?`; params.push(source); }
|
|
578
|
+
sql += ` ORDER BY created_at DESC LIMIT ?`;
|
|
579
|
+
params.push(limit);
|
|
580
|
+
|
|
581
|
+
const rows = db.query(sql).all(...params) as any[];
|
|
582
|
+
|
|
583
|
+
log(`\n${c.bold}Research Findings${c.reset} (${rows.length})\n`);
|
|
584
|
+
|
|
585
|
+
if (rows.length === 0) {
|
|
586
|
+
info("No research stored yet. Use Valyu MCP or /beans:research to gather findings.");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
for (const row of rows) {
|
|
591
|
+
const srcColor = row.source === "valyu" ? c.blue : row.source === "web" ? c.green : c.yellow;
|
|
592
|
+
log(`${c.dim}${row.id}${c.reset} ${srcColor}[${row.source}]${c.reset} ${row.title}`);
|
|
593
|
+
if (row.issue_id) log(` ${c.dim}Issue: ${row.issue_id}${c.reset}`);
|
|
594
|
+
log(` ${c.dim}${row.preview}...${c.reset}`);
|
|
595
|
+
log("");
|
|
596
|
+
}
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (subCmd === "search") {
|
|
601
|
+
const query = args.slice(1).join(" ");
|
|
602
|
+
if (!query) {
|
|
603
|
+
error("Usage: beans research search <query>");
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const rows = db.query(`
|
|
608
|
+
SELECT r.id, r.issue_id, r.source, r.title, substr(r.content, 1, 200) as preview
|
|
609
|
+
FROM research r
|
|
610
|
+
JOIN research_fts fts ON r.rowid = fts.rowid
|
|
611
|
+
WHERE research_fts MATCH ?
|
|
612
|
+
ORDER BY rank LIMIT 20
|
|
613
|
+
`).all(query) as any[];
|
|
614
|
+
|
|
615
|
+
log(`\n${c.bold}Search: "${query}"${c.reset} (${rows.length} results)\n`);
|
|
616
|
+
|
|
617
|
+
for (const row of rows) {
|
|
618
|
+
const srcColor = row.source === "valyu" ? c.blue : row.source === "web" ? c.green : c.yellow;
|
|
619
|
+
log(`${c.dim}${row.id}${c.reset} ${srcColor}[${row.source}]${c.reset} ${row.title}`);
|
|
620
|
+
log(` ${c.dim}${row.preview}...${c.reset}\n`);
|
|
621
|
+
}
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (subCmd === "show") {
|
|
626
|
+
const id = args[1];
|
|
627
|
+
if (!id) {
|
|
628
|
+
error("Usage: beans research show <id>");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const row = db.query(`SELECT * FROM research WHERE id = ?`).get(id) as any;
|
|
633
|
+
if (!row) {
|
|
634
|
+
error(`Research ${id} not found`);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
log(`\n${c.bold}${row.title}${c.reset}`);
|
|
639
|
+
log(`${c.dim}ID: ${row.id} | Source: ${row.source} | Relevance: ${row.relevance}${c.reset}`);
|
|
640
|
+
if (row.issue_id) log(`${c.dim}Issue: ${row.issue_id}${c.reset}`);
|
|
641
|
+
if (row.url) log(`${c.dim}URL: ${row.url}${c.reset}`);
|
|
642
|
+
log(`\n${row.content}\n`);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (subCmd === "for") {
|
|
647
|
+
const issueId = args[1];
|
|
648
|
+
if (!issueId) {
|
|
649
|
+
error("Usage: beans research for <issue-id>");
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const rows = db.query(`
|
|
654
|
+
SELECT id, source, title, substr(content, 1, 100) as preview, relevance
|
|
655
|
+
FROM research WHERE issue_id = ? ORDER BY relevance DESC
|
|
656
|
+
`).all(issueId) as any[];
|
|
657
|
+
|
|
658
|
+
log(`\n${c.bold}Research for ${issueId}${c.reset} (${rows.length} findings)\n`);
|
|
659
|
+
|
|
660
|
+
for (const row of rows) {
|
|
661
|
+
log(`${c.dim}${row.id}${c.reset} [${row.source}] ${row.title} (${(row.relevance * 100).toFixed(0)}%)`);
|
|
662
|
+
}
|
|
663
|
+
log("");
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Default help
|
|
668
|
+
log(`
|
|
669
|
+
${c.bold}beans research${c.reset} - Manage research database
|
|
670
|
+
|
|
671
|
+
${c.bold}Commands:${c.reset}
|
|
672
|
+
${c.cyan}list${c.reset} List all research (--issue=X, --source=valyu|web|codebase)
|
|
673
|
+
${c.cyan}search <query>${c.reset} Full-text search
|
|
674
|
+
${c.cyan}show <id>${c.reset} Show full research entry
|
|
675
|
+
${c.cyan}for <issue-id>${c.reset} List research linked to an issue
|
|
676
|
+
|
|
677
|
+
${c.bold}Storage:${c.reset}
|
|
678
|
+
Database at: ${c.dim}.beans/research.db${c.reset}
|
|
679
|
+
|
|
680
|
+
Research is auto-stored by Valyu MCP when you pass issue_id.
|
|
681
|
+
Use research_store tool to manually add web/codebase findings.
|
|
682
|
+
`);
|
|
683
|
+
}
|
|
684
|
+
|
|
513
685
|
async function cmdHelp() {
|
|
514
686
|
log(`
|
|
515
687
|
${c.bold}${c.blue}🫘 BEANS CLI v${VERSION}${c.reset}
|
|
@@ -519,24 +691,27 @@ ${c.bold}Usage:${c.reset}
|
|
|
519
691
|
|
|
520
692
|
${c.bold}Commands:${c.reset}
|
|
521
693
|
${c.cyan}init${c.reset} Initialize BEANS in current project
|
|
522
|
-
${c.cyan}config${c.reset} Configure API keys
|
|
523
|
-
${c.cyan}config --valyu${c.reset} Set Valyu API key
|
|
524
|
-
${c.cyan}config --github${c.reset} Set GitHub token
|
|
525
|
-
${c.cyan}config --show${c.reset} Show current configuration
|
|
694
|
+
${c.cyan}config${c.reset} Configure API keys
|
|
526
695
|
${c.cyan}doctor${c.reset} Check installation status
|
|
527
|
-
${c.cyan}
|
|
528
|
-
${c.cyan}help${c.reset} Show this help
|
|
696
|
+
${c.cyan}research${c.reset} Manage research database
|
|
697
|
+
${c.cyan}help${c.reset} Show this help
|
|
698
|
+
|
|
699
|
+
${c.bold}Research:${c.reset}
|
|
700
|
+
${c.cyan}research list${c.reset} List stored research
|
|
701
|
+
${c.cyan}research search${c.reset} Full-text search findings
|
|
702
|
+
${c.cyan}research for${c.reset} Get research for an issue
|
|
703
|
+
${c.cyan}research show${c.reset} Show full entry
|
|
529
704
|
|
|
530
705
|
${c.bold}In Claude Code:${c.reset}
|
|
531
|
-
${c.cyan}/beans${c.reset}
|
|
532
|
-
${c.cyan}/beans
|
|
533
|
-
${c.cyan}/beans
|
|
534
|
-
|
|
535
|
-
|
|
706
|
+
${c.cyan}/beans "Add feature"${c.reset} Full autonomous flow
|
|
707
|
+
${c.cyan}/beans:status${c.reset} Check progress
|
|
708
|
+
${c.cyan}/beans:land${c.reset} Commit, push, close
|
|
709
|
+
|
|
710
|
+
${c.bold}Data Model:${c.reset}
|
|
711
|
+
Issues → ${c.dim}.beans/ (beads tracker)${c.reset}
|
|
712
|
+
Research → ${c.dim}.beans/research.db (SQLite)${c.reset}
|
|
713
|
+
Config → ${c.dim}~/.beans/config.json${c.reset}
|
|
536
714
|
|
|
537
|
-
${c.bold}Environment:${c.reset}
|
|
538
|
-
Config stored at: ${c.dim}~/.beans/config.json${c.reset}
|
|
539
|
-
|
|
540
715
|
${c.bold}More info:${c.reset}
|
|
541
716
|
https://github.com/shinyobjectz/beans
|
|
542
717
|
`);
|
|
@@ -558,6 +733,9 @@ switch (command) {
|
|
|
558
733
|
case "doctor":
|
|
559
734
|
await cmdDoctor(args);
|
|
560
735
|
break;
|
|
736
|
+
case "research":
|
|
737
|
+
await cmdResearch(args);
|
|
738
|
+
break;
|
|
561
739
|
case "help":
|
|
562
740
|
case "--help":
|
|
563
741
|
case "-h":
|