bashbros 0.1.2 → 0.1.3

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/dist/cli.js CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ DashboardWriter
4
+ } from "./chunk-EYO44OMN.js";
2
5
  import {
3
6
  DashboardDB
4
- } from "./chunk-YUMNBQAY.js";
7
+ } from "./chunk-JYWQT2B4.js";
5
8
  import {
6
9
  formatAllAgentsInfo,
7
10
  formatPermissionsTable
8
- } from "./chunk-SQCP6IYB.js";
11
+ } from "./chunk-WPJJZLT6.js";
9
12
  import {
10
13
  BashBro,
11
14
  BashBros,
@@ -17,14 +20,14 @@ import {
17
20
  UndoStack,
18
21
  gateCommand,
19
22
  getBashgymIntegration
20
- } from "./chunk-XCZMQRSX.js";
23
+ } from "./chunk-2RPTM6EQ.js";
21
24
  import "./chunk-SG752FZC.js";
22
25
  import "./chunk-DLP2O6PN.js";
23
26
  import {
24
27
  findConfig,
25
28
  getDefaultConfig,
26
29
  loadConfig
27
- } from "./chunk-BW6XCOJH.js";
30
+ } from "./chunk-A535VV7N.js";
28
31
  import "./chunk-QWZGB4V3.js";
29
32
  import {
30
33
  allowForSession
@@ -1015,7 +1018,7 @@ var EgressPatternMatcher = class {
1015
1018
  // src/policy/ward/egress.ts
1016
1019
  var DashboardDB2 = null;
1017
1020
  try {
1018
- const dbModule = await import("./db-OBKEXRTP.js");
1021
+ const dbModule = await import("./db-SWJUUSFX.js");
1019
1022
  DashboardDB2 = dbModule.DashboardDB;
1020
1023
  } catch {
1021
1024
  }
@@ -1425,162 +1428,6 @@ ${passed} passed, ${failed} failed. Fix issues above.
1425
1428
  import chalk3 from "chalk";
1426
1429
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1427
1430
  import { parse, stringify as stringify2 } from "yaml";
1428
-
1429
- // src/dashboard/writer.ts
1430
- import { homedir as homedir4 } from "os";
1431
- import { join as join4 } from "path";
1432
- import { mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
1433
- function getDefaultDbPath() {
1434
- const bashbrosDir = join4(homedir4(), ".bashbros");
1435
- if (!existsSync5(bashbrosDir)) {
1436
- mkdirSync2(bashbrosDir, { recursive: true });
1437
- }
1438
- return join4(bashbrosDir, "dashboard.db");
1439
- }
1440
- var DashboardWriter = class {
1441
- db;
1442
- sessionId = null;
1443
- commandCount = 0;
1444
- blockedCount = 0;
1445
- totalRiskScore = 0;
1446
- constructor(dbPath) {
1447
- const path = dbPath ?? getDefaultDbPath();
1448
- this.db = new DashboardDB(path);
1449
- }
1450
- /**
1451
- * Start a new watch session
1452
- */
1453
- startSession(agent, workingDir) {
1454
- this.sessionId = this.db.insertSession({
1455
- agent,
1456
- pid: process.pid,
1457
- workingDir
1458
- });
1459
- this.commandCount = 0;
1460
- this.blockedCount = 0;
1461
- this.totalRiskScore = 0;
1462
- return this.sessionId;
1463
- }
1464
- /**
1465
- * End the current session
1466
- */
1467
- endSession() {
1468
- if (!this.sessionId) return;
1469
- const avgRiskScore = this.commandCount > 0 ? this.totalRiskScore / this.commandCount : 0;
1470
- this.db.updateSession(this.sessionId, {
1471
- endTime: /* @__PURE__ */ new Date(),
1472
- status: "completed",
1473
- commandCount: this.commandCount,
1474
- blockedCount: this.blockedCount,
1475
- avgRiskScore
1476
- });
1477
- this.sessionId = null;
1478
- }
1479
- /**
1480
- * Mark session as crashed (for unexpected exits)
1481
- */
1482
- crashSession() {
1483
- if (!this.sessionId) return;
1484
- const avgRiskScore = this.commandCount > 0 ? this.totalRiskScore / this.commandCount : 0;
1485
- this.db.updateSession(this.sessionId, {
1486
- endTime: /* @__PURE__ */ new Date(),
1487
- status: "crashed",
1488
- commandCount: this.commandCount,
1489
- blockedCount: this.blockedCount,
1490
- avgRiskScore
1491
- });
1492
- this.sessionId = null;
1493
- }
1494
- /**
1495
- * Record a command execution
1496
- */
1497
- recordCommand(command, allowed, riskScore, violations, durationMs) {
1498
- if (!this.sessionId) return null;
1499
- const input = {
1500
- sessionId: this.sessionId,
1501
- command,
1502
- allowed,
1503
- riskScore: riskScore.score,
1504
- riskLevel: riskScore.level,
1505
- riskFactors: riskScore.factors,
1506
- durationMs,
1507
- violations: violations.map((v) => v.message)
1508
- };
1509
- const id = this.db.insertCommand(input);
1510
- this.commandCount++;
1511
- this.totalRiskScore += riskScore.score;
1512
- if (!allowed) {
1513
- this.blockedCount++;
1514
- }
1515
- if (this.commandCount % 10 === 0) {
1516
- const avgRiskScore = this.totalRiskScore / this.commandCount;
1517
- this.db.updateSession(this.sessionId, {
1518
- commandCount: this.commandCount,
1519
- blockedCount: this.blockedCount,
1520
- avgRiskScore
1521
- });
1522
- }
1523
- return id;
1524
- }
1525
- /**
1526
- * Record a Bash Bro AI event
1527
- */
1528
- recordBroEvent(input) {
1529
- const dbInput = {
1530
- sessionId: this.sessionId ?? void 0,
1531
- eventType: input.eventType,
1532
- inputContext: input.inputContext,
1533
- outputSummary: input.outputSummary,
1534
- modelUsed: input.modelUsed,
1535
- latencyMs: input.latencyMs,
1536
- success: input.success
1537
- };
1538
- return this.db.insertBroEvent(dbInput);
1539
- }
1540
- /**
1541
- * Update Bash Bro status
1542
- */
1543
- updateBroStatus(status) {
1544
- const dbInput = {
1545
- ollamaAvailable: status.ollamaAvailable,
1546
- ollamaModel: status.ollamaModel,
1547
- platform: status.platform,
1548
- shell: status.shell,
1549
- projectType: status.projectType
1550
- };
1551
- return this.db.updateBroStatus(dbInput);
1552
- }
1553
- /**
1554
- * Get current session ID
1555
- */
1556
- getSessionId() {
1557
- return this.sessionId;
1558
- }
1559
- /**
1560
- * Get current session stats
1561
- */
1562
- getSessionStats() {
1563
- return {
1564
- commandCount: this.commandCount,
1565
- blockedCount: this.blockedCount,
1566
- avgRiskScore: this.commandCount > 0 ? this.totalRiskScore / this.commandCount : 0
1567
- };
1568
- }
1569
- /**
1570
- * Close database connection
1571
- */
1572
- close() {
1573
- this.db.close();
1574
- }
1575
- /**
1576
- * Get the underlying database instance (for advanced use)
1577
- */
1578
- getDB() {
1579
- return this.db;
1580
- }
1581
- };
1582
-
1583
- // src/watch.ts
1584
1431
  var dashboardWriter = null;
1585
1432
  var riskScorer = null;
1586
1433
  function cleanup() {
@@ -1865,16 +1712,16 @@ import express from "express";
1865
1712
  import { WebSocketServer } from "ws";
1866
1713
  import { createServer } from "http";
1867
1714
  import { fileURLToPath } from "url";
1868
- import { dirname, join as join5 } from "path";
1869
- import { homedir as homedir5 } from "os";
1870
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
1715
+ import { dirname, join as join4 } from "path";
1716
+ import { homedir as homedir4 } from "os";
1717
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
1871
1718
  import { parse as parse3, stringify as stringify4 } from "yaml";
1872
- function getDefaultDbPath2() {
1873
- const bashbrosDir = join5(homedir5(), ".bashbros");
1874
- if (!existsSync6(bashbrosDir)) {
1875
- mkdirSync3(bashbrosDir, { recursive: true });
1719
+ function getDefaultDbPath() {
1720
+ const bashbrosDir = join4(homedir4(), ".bashbros");
1721
+ if (!existsSync5(bashbrosDir)) {
1722
+ mkdirSync2(bashbrosDir, { recursive: true });
1876
1723
  }
1877
- return join5(bashbrosDir, "dashboard.db");
1724
+ return join4(bashbrosDir, "dashboard.db");
1878
1725
  }
1879
1726
  var DashboardServer = class {
1880
1727
  app;
@@ -1887,7 +1734,7 @@ var DashboardServer = class {
1887
1734
  constructor(config = {}) {
1888
1735
  this.port = config.port ?? 17800;
1889
1736
  this.bind = config.bind ?? "127.0.0.1";
1890
- this.db = new DashboardDB(config.dbPath ?? getDefaultDbPath2());
1737
+ this.db = new DashboardDB(config.dbPath ?? getDefaultDbPath());
1891
1738
  this.app = express();
1892
1739
  this.setupMiddleware();
1893
1740
  this.setupRoutes();
@@ -2072,6 +1919,36 @@ var DashboardServer = class {
2072
1919
  res.status(500).json({ error: "Failed to fetch commands" });
2073
1920
  }
2074
1921
  });
1922
+ this.app.get("/api/tools/live", (req, res) => {
1923
+ try {
1924
+ const limit = req.query.limit ? parseInt(req.query.limit, 10) : 50;
1925
+ const tools = this.db.getLiveToolUses(limit);
1926
+ res.json(tools);
1927
+ } catch (error) {
1928
+ res.status(500).json({ error: "Failed to fetch live tool uses" });
1929
+ }
1930
+ });
1931
+ this.app.get("/api/tools", (req, res) => {
1932
+ try {
1933
+ const filter = {};
1934
+ if (req.query.toolName) filter.toolName = req.query.toolName;
1935
+ if (req.query.since) filter.since = new Date(req.query.since);
1936
+ if (req.query.limit) filter.limit = parseInt(req.query.limit, 10);
1937
+ if (req.query.offset) filter.offset = parseInt(req.query.offset, 10);
1938
+ const tools = this.db.getToolUses(filter);
1939
+ res.json(tools);
1940
+ } catch (error) {
1941
+ res.status(500).json({ error: "Failed to fetch tool uses" });
1942
+ }
1943
+ });
1944
+ this.app.get("/api/tools/stats", (_req, res) => {
1945
+ try {
1946
+ const stats = this.db.getToolUseStats();
1947
+ res.json(stats);
1948
+ } catch (error) {
1949
+ res.status(500).json({ error: "Failed to fetch tool use stats" });
1950
+ }
1951
+ });
2075
1952
  this.app.get("/api/bro/status", (_req, res) => {
2076
1953
  try {
2077
1954
  const status = this.db.getLatestBroStatus();
@@ -2116,7 +1993,7 @@ var DashboardServer = class {
2116
1993
  res.status(400).json({ error: "Model name required" });
2117
1994
  return;
2118
1995
  }
2119
- const controlPath = join5(homedir5(), ".bashbros", "model-control.json");
1996
+ const controlPath = join4(homedir4(), ".bashbros", "model-control.json");
2120
1997
  writeFileSync4(controlPath, JSON.stringify({
2121
1998
  model,
2122
1999
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -2128,7 +2005,7 @@ var DashboardServer = class {
2128
2005
  });
2129
2006
  this.app.post("/api/bro/scan", (_req, res) => {
2130
2007
  try {
2131
- const controlPath = join5(homedir5(), ".bashbros", "scan-control.json");
2008
+ const controlPath = join4(homedir4(), ".bashbros", "scan-control.json");
2132
2009
  writeFileSync4(controlPath, JSON.stringify({
2133
2010
  action: "scan",
2134
2011
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -2178,10 +2055,10 @@ var DashboardServer = class {
2178
2055
  });
2179
2056
  const __filename = fileURLToPath(import.meta.url);
2180
2057
  const __dirname = dirname(__filename);
2181
- const staticPath = join5(__dirname, "static");
2058
+ const staticPath = join4(__dirname, "static");
2182
2059
  this.app.use(express.static(staticPath));
2183
2060
  this.app.get("/{*path}", (_req, res) => {
2184
- res.sendFile(join5(staticPath, "index.html"));
2061
+ res.sendFile(join4(staticPath, "index.html"));
2185
2062
  });
2186
2063
  }
2187
2064
  setupWebSocket() {
@@ -2312,7 +2189,7 @@ program.command("scan").description("Scan your system and project environment").
2312
2189
  console.log(bro.getSystemContext());
2313
2190
  console.log();
2314
2191
  console.log(chalk5.bold("\n## Agent Configurations\n"));
2315
- const { formatAgentSummary } = await import("./display-6LZ2HBCU.js");
2192
+ const { formatAgentSummary } = await import("./display-HFIFXOOL.js");
2316
2193
  const agents = await getAllAgentConfigs();
2317
2194
  console.log(formatAgentSummary(agents));
2318
2195
  console.log();
@@ -2559,11 +2436,30 @@ hookCmd.command("status").description("Check Claude Code hook status").action(()
2559
2436
  console.log();
2560
2437
  console.log(` Claude Code: ${status.claudeInstalled ? chalk5.green("installed") : chalk5.yellow("not found")}`);
2561
2438
  console.log(` BashBros hooks: ${status.hooksInstalled ? chalk5.green("active") : chalk5.dim("not installed")}`);
2439
+ console.log(` All-tools recording: ${status.allToolsInstalled ? chalk5.green("active") : chalk5.dim("not installed")}`);
2562
2440
  if (status.hooks.length > 0) {
2563
2441
  console.log(` Active hooks: ${status.hooks.join(", ")}`);
2564
2442
  }
2565
2443
  console.log();
2566
2444
  });
2445
+ hookCmd.command("install-all-tools").description("Install hook to record ALL Claude Code tool uses (not just Bash)").action(() => {
2446
+ const result = ClaudeCodeHooks.installAllTools();
2447
+ if (result.success) {
2448
+ console.log(chalk5.green("\u2713"), result.message);
2449
+ } else {
2450
+ console.log(chalk5.red("\u2717"), result.message);
2451
+ process.exit(1);
2452
+ }
2453
+ });
2454
+ hookCmd.command("uninstall-all-tools").description("Remove all-tools recording hook").action(() => {
2455
+ const result = ClaudeCodeHooks.uninstallAllTools();
2456
+ if (result.success) {
2457
+ console.log(chalk5.green("\u2713"), result.message);
2458
+ } else {
2459
+ console.log(chalk5.red("\u2717"), result.message);
2460
+ process.exit(1);
2461
+ }
2462
+ });
2567
2463
  var moltbotCmd = program.command("moltbot").alias("clawdbot").description("Manage Moltbot/Clawdbot integration");
2568
2464
  moltbotCmd.command("install").description("Install BashBros hooks into Moltbot").action(() => {
2569
2465
  const result = MoltbotHooks.install();
@@ -2671,7 +2567,7 @@ program.command("agent-info [agent]").description("Show detailed info about inst
2671
2567
  return;
2672
2568
  }
2673
2569
  const info = await getAgentConfigInfo(agent);
2674
- const { formatAgentInfo: formatAgentInfo2 } = await import("./display-6LZ2HBCU.js");
2570
+ const { formatAgentInfo: formatAgentInfo2 } = await import("./display-HFIFXOOL.js");
2675
2571
  console.log();
2676
2572
  console.log(formatAgentInfo2(info));
2677
2573
  if (options.raw && info.configExists && info.configPath) {
@@ -2710,7 +2606,7 @@ program.command("gate <command>").description("Check if a command should be allo
2710
2606
  const { allowForSession: allowForSession2 } = await import("./session-Y4MICATZ.js");
2711
2607
  const { readFileSync: readFileSync5, writeFileSync: writeFileSync5 } = await import("fs");
2712
2608
  const { parse: parse4, stringify: stringify5 } = await import("yaml");
2713
- const { findConfig: findConfig2 } = await import("./config-JLLOTFLI.js");
2609
+ const { findConfig: findConfig2 } = await import("./config-43SK6SFI.js");
2714
2610
  console.error();
2715
2611
  console.error(chalk5.red("\u{1F6E1}\uFE0F BashBros blocked a command"));
2716
2612
  console.error();
@@ -2778,6 +2674,60 @@ program.command("gate <command>").description("Check if a command should be allo
2778
2674
  }
2779
2675
  process.exit(0);
2780
2676
  });
2677
+ program.command("record-tool").description("Record a Claude Code tool execution (used by hooks)").option("--marker <marker>", "Hook marker (ignored, used for identification)").action(async () => {
2678
+ const eventJson = process.env.CLAUDE_HOOK_EVENT || "";
2679
+ if (!eventJson) {
2680
+ return;
2681
+ }
2682
+ try {
2683
+ const event = JSON.parse(eventJson);
2684
+ const events = Array.isArray(event) ? event : [event];
2685
+ const { DashboardWriter: DashboardWriter2 } = await import("./writer-4ZEAKUFD.js");
2686
+ const writer = new DashboardWriter2();
2687
+ for (const evt of events) {
2688
+ const toolName = evt.tool_name || evt.tool || "unknown";
2689
+ const toolInput = evt.tool_input || evt.input || {};
2690
+ const toolOutput = evt.tool_output || evt.output || "";
2691
+ let inputStr;
2692
+ if (typeof toolInput === "string") {
2693
+ inputStr = toolInput;
2694
+ } else if (typeof toolInput === "object") {
2695
+ inputStr = JSON.stringify(toolInput, null, 2);
2696
+ } else {
2697
+ inputStr = String(toolInput);
2698
+ }
2699
+ let outputStr;
2700
+ let exitCode = null;
2701
+ let success = null;
2702
+ if (typeof toolOutput === "object" && toolOutput !== null) {
2703
+ outputStr = (toolOutput.stdout || "") + (toolOutput.stderr || "");
2704
+ exitCode = toolOutput.exit_code ?? toolOutput.exitCode ?? null;
2705
+ if (exitCode !== null) {
2706
+ success = exitCode === 0;
2707
+ }
2708
+ } else {
2709
+ outputStr = String(toolOutput || "");
2710
+ }
2711
+ const repoName = evt.repo?.name ?? null;
2712
+ const repoPath = evt.repo?.path ?? null;
2713
+ writer.recordToolUse({
2714
+ toolName,
2715
+ toolInput: inputStr,
2716
+ toolOutput: outputStr,
2717
+ exitCode,
2718
+ success,
2719
+ cwd: evt.cwd || process.cwd(),
2720
+ repoName,
2721
+ repoPath
2722
+ });
2723
+ const preview = inputStr.substring(0, 40).replace(/\n/g, " ");
2724
+ console.log(`[BashBros] ${toolName}: ${preview}${inputStr.length > 40 ? "..." : ""}`);
2725
+ }
2726
+ writer.close();
2727
+ } catch (e) {
2728
+ console.error(`[BashBros] Error recording tool: ${e instanceof Error ? e.message : e}`);
2729
+ }
2730
+ });
2781
2731
  program.command("record <command>").description("Record a command execution (used by hooks)").option("-o, --output <output>", "Command output").option("-e, --exit-code <code>", "Exit code", "0").action(async (command, options) => {
2782
2732
  if (!metricsCollector) metricsCollector = new MetricsCollector();
2783
2733
  if (!costEstimator) costEstimator = new CostEstimator();