@morebeans/cli 2.4.0 → 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.
Files changed (2) hide show
  1. package/index.ts +181 -16
  2. 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 upgrade # Update to latest version
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.4.0";
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
 
@@ -523,6 +524,164 @@ async function cmdDoctor(args: string[]) {
523
524
  log("");
524
525
  }
525
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
+
526
685
  async function cmdHelp() {
527
686
  log(`
528
687
  ${c.bold}${c.blue}🫘 BEANS CLI v${VERSION}${c.reset}
@@ -532,24 +691,27 @@ ${c.bold}Usage:${c.reset}
532
691
 
533
692
  ${c.bold}Commands:${c.reset}
534
693
  ${c.cyan}init${c.reset} Initialize BEANS in current project
535
- ${c.cyan}config${c.reset} Configure API keys interactively
536
- ${c.cyan}config --valyu${c.reset} Set Valyu API key
537
- ${c.cyan}config --github${c.reset} Set GitHub token
538
- ${c.cyan}config --show${c.reset} Show current configuration
694
+ ${c.cyan}config${c.reset} Configure API keys
539
695
  ${c.cyan}doctor${c.reset} Check installation status
540
- ${c.cyan}doctor --fix${c.reset} Auto-fix issues (install tools, run bd doctor)
541
- ${c.cyan}help${c.reset} Show this help message
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
542
704
 
543
705
  ${c.bold}In Claude Code:${c.reset}
544
- ${c.cyan}/beans${c.reset} List ready issues
545
- ${c.cyan}/beans "Add feature"${c.reset} Full autonomous flow
546
- ${c.cyan}/beans task-001${c.reset} Continue existing issue
547
- ${c.cyan}/beans:status${c.reset} Check progress
548
- ${c.cyan}/beans:land${c.reset} Commit, push, close
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}
549
714
 
550
- ${c.bold}Environment:${c.reset}
551
- Config stored at: ${c.dim}~/.beans/config.json${c.reset}
552
-
553
715
  ${c.bold}More info:${c.reset}
554
716
  https://github.com/shinyobjectz/beans
555
717
  `);
@@ -571,6 +733,9 @@ switch (command) {
571
733
  case "doctor":
572
734
  await cmdDoctor(args);
573
735
  break;
736
+ case "research":
737
+ await cmdResearch(args);
738
+ break;
574
739
  case "help":
575
740
  case "--help":
576
741
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morebeans/cli",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "BEANS CLI - Setup and configure autonomous development for Claude Code",
5
5
  "type": "module",
6
6
  "main": "index.ts",