bashbros 0.1.3 → 0.1.5

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 (79) hide show
  1. package/README.md +734 -271
  2. package/dist/adapters-JAZGGNVP.js +9 -0
  3. package/dist/chunk-25TREQ6V.js +465 -0
  4. package/dist/chunk-25TREQ6V.js.map +1 -0
  5. package/dist/{chunk-A535VV7N.js → chunk-2CI2MRKI.js} +23 -6
  6. package/dist/chunk-2CI2MRKI.js.map +1 -0
  7. package/dist/chunk-4XZ64P4V.js +47 -0
  8. package/dist/chunk-4XZ64P4V.js.map +1 -0
  9. package/dist/chunk-5BBPRDWL.js +186 -0
  10. package/dist/chunk-5BBPRDWL.js.map +1 -0
  11. package/dist/{chunk-2RPTM6EQ.js → chunk-6QVMBCSX.js} +719 -902
  12. package/dist/chunk-6QVMBCSX.js.map +1 -0
  13. package/dist/{chunk-JYWQT2B4.js → chunk-6SLR5WPD.js} +657 -14
  14. package/dist/chunk-6SLR5WPD.js.map +1 -0
  15. package/dist/{chunk-EYO44OMN.js → chunk-AZVT6AZY.js} +78 -17
  16. package/dist/chunk-AZVT6AZY.js.map +1 -0
  17. package/dist/{chunk-WPJJZLT6.js → chunk-C4GZNBFF.js} +3 -2
  18. package/dist/chunk-C4GZNBFF.js.map +1 -0
  19. package/dist/chunk-IUUBCPMV.js +166 -0
  20. package/dist/chunk-IUUBCPMV.js.map +1 -0
  21. package/dist/chunk-J6ONXY6N.js +146 -0
  22. package/dist/chunk-J6ONXY6N.js.map +1 -0
  23. package/dist/{chunk-DLP2O6PN.js → chunk-JOIAG54E.js} +83 -88
  24. package/dist/chunk-JOIAG54E.js.map +1 -0
  25. package/dist/chunk-LJE4EPIU.js +56 -0
  26. package/dist/chunk-LJE4EPIU.js.map +1 -0
  27. package/dist/{chunk-QWZGB4V3.js → chunk-PAZIDRXK.js} +42 -181
  28. package/dist/chunk-PAZIDRXK.js.map +1 -0
  29. package/dist/chunk-PLSHJHHR.js +293 -0
  30. package/dist/chunk-PLSHJHHR.js.map +1 -0
  31. package/dist/chunk-R5I5DEXE.js +228 -0
  32. package/dist/chunk-R5I5DEXE.js.map +1 -0
  33. package/dist/chunk-SDN6TAGD.js +157 -0
  34. package/dist/chunk-SDN6TAGD.js.map +1 -0
  35. package/dist/chunk-T5ONCUHZ.js +198 -0
  36. package/dist/chunk-T5ONCUHZ.js.map +1 -0
  37. package/dist/cli.js +1204 -188
  38. package/dist/cli.js.map +1 -1
  39. package/dist/{config-43SK6SFI.js → config-IXBXMIUA.js} +2 -2
  40. package/dist/copilot-cli-5WJWK5YT.js +9 -0
  41. package/dist/{db-SWJUUSFX.js → db-GJALN3R7.js} +2 -2
  42. package/dist/db-checks-2YOVECD4.js +133 -0
  43. package/dist/db-checks-2YOVECD4.js.map +1 -0
  44. package/dist/{display-HFIFXOOL.js → display-UDIACHTP.js} +3 -3
  45. package/dist/{engine-EGPAS2EX.js → engine-4WNPXVMS.js} +3 -2
  46. package/dist/gemini-cli-3563EELZ.js +9 -0
  47. package/dist/gemini-cli-3563EELZ.js.map +1 -0
  48. package/dist/index.d.ts +205 -101
  49. package/dist/index.js +132 -402
  50. package/dist/index.js.map +1 -1
  51. package/dist/{ollama-HY35OHW4.js → ollama-TNMD5WHW.js} +2 -2
  52. package/dist/ollama-TNMD5WHW.js.map +1 -0
  53. package/dist/opencode-DRCY275R.js +9 -0
  54. package/dist/opencode-DRCY275R.js.map +1 -0
  55. package/dist/profiles-7CLN6TAT.js +9 -0
  56. package/dist/profiles-7CLN6TAT.js.map +1 -0
  57. package/dist/server-3CMTP4W4.js +13 -0
  58. package/dist/server-3CMTP4W4.js.map +1 -0
  59. package/dist/setup-U4R5QJMV.js +124 -0
  60. package/dist/setup-U4R5QJMV.js.map +1 -0
  61. package/dist/static/index.html +4862 -2007
  62. package/dist/store-WJ5Y7MOE.js +9 -0
  63. package/dist/store-WJ5Y7MOE.js.map +1 -0
  64. package/dist/{writer-4ZEAKUFD.js → writer-OMHUMJR5.js} +3 -3
  65. package/dist/writer-OMHUMJR5.js.map +1 -0
  66. package/package.json +78 -68
  67. package/dist/chunk-2RPTM6EQ.js.map +0 -1
  68. package/dist/chunk-A535VV7N.js.map +0 -1
  69. package/dist/chunk-DLP2O6PN.js.map +0 -1
  70. package/dist/chunk-EYO44OMN.js.map +0 -1
  71. package/dist/chunk-JYWQT2B4.js.map +0 -1
  72. package/dist/chunk-QWZGB4V3.js.map +0 -1
  73. package/dist/chunk-WPJJZLT6.js.map +0 -1
  74. /package/dist/{config-43SK6SFI.js.map → adapters-JAZGGNVP.js.map} +0 -0
  75. /package/dist/{db-SWJUUSFX.js.map → config-IXBXMIUA.js.map} +0 -0
  76. /package/dist/{display-HFIFXOOL.js.map → copilot-cli-5WJWK5YT.js.map} +0 -0
  77. /package/dist/{engine-EGPAS2EX.js.map → db-GJALN3R7.js.map} +0 -0
  78. /package/dist/{ollama-HY35OHW4.js.map → display-UDIACHTP.js.map} +0 -0
  79. /package/dist/{writer-4ZEAKUFD.js.map → engine-4WNPXVMS.js.map} +0 -0
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AdapterRegistry
4
+ } from "./chunk-4XZ64P4V.js";
5
+ import "./chunk-7OCVIDC7.js";
6
+ export {
7
+ AdapterRegistry
8
+ };
9
+ //# sourceMappingURL=adapters-JAZGGNVP.js.map
@@ -0,0 +1,465 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/claude-code.ts
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ import { join } from "path";
6
+ import { homedir } from "os";
7
+ var CLAUDE_SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
8
+ var CLAUDE_DIR = join(homedir(), ".claude");
9
+ var BASHBROS_HOOK_MARKER = "# bashbros-managed";
10
+ var BASHBROS_ALL_TOOLS_MARKER = "--marker=bashbros-all-tools";
11
+ var BASHBROS_PROMPT_MARKER = "--marker=bashbros-prompt";
12
+ var ClaudeCodeHooks = class {
13
+ /**
14
+ * Check if Claude Code is installed
15
+ */
16
+ static isClaudeInstalled() {
17
+ return existsSync(CLAUDE_DIR);
18
+ }
19
+ /**
20
+ * Load current Claude settings
21
+ */
22
+ static loadSettings() {
23
+ if (!existsSync(CLAUDE_SETTINGS_PATH)) {
24
+ return {};
25
+ }
26
+ try {
27
+ const content = readFileSync(CLAUDE_SETTINGS_PATH, "utf-8");
28
+ return JSON.parse(content);
29
+ } catch {
30
+ return {};
31
+ }
32
+ }
33
+ /**
34
+ * Save Claude settings
35
+ */
36
+ static saveSettings(settings) {
37
+ if (!existsSync(CLAUDE_DIR)) {
38
+ mkdirSync(CLAUDE_DIR, { recursive: true });
39
+ }
40
+ writeFileSync(
41
+ CLAUDE_SETTINGS_PATH,
42
+ JSON.stringify(settings, null, 2),
43
+ "utf-8"
44
+ );
45
+ }
46
+ /**
47
+ * Install BashBros hooks into Claude Code
48
+ */
49
+ static install() {
50
+ if (!this.isClaudeInstalled()) {
51
+ return {
52
+ success: false,
53
+ message: "Claude Code not found. Install Claude Code first."
54
+ };
55
+ }
56
+ const settings = this.loadSettings();
57
+ if (!settings.hooks) {
58
+ settings.hooks = {};
59
+ }
60
+ if (this.isInstalled(settings)) {
61
+ return {
62
+ success: true,
63
+ message: "BashBros hooks already installed."
64
+ };
65
+ }
66
+ const preToolUseHook = {
67
+ matcher: "Bash",
68
+ hooks: [{
69
+ type: "command",
70
+ command: `bashbros gate "$TOOL_INPUT" ${BASHBROS_HOOK_MARKER}`
71
+ }]
72
+ };
73
+ const postToolUseHook = {
74
+ matcher: "Bash",
75
+ hooks: [{
76
+ type: "command",
77
+ command: `bashbros record "$TOOL_INPUT" "$TOOL_OUTPUT" ${BASHBROS_HOOK_MARKER}`
78
+ }]
79
+ };
80
+ const sessionEndHook = {
81
+ hooks: [{
82
+ type: "command",
83
+ command: `bashbros session-end ${BASHBROS_HOOK_MARKER}`
84
+ }]
85
+ };
86
+ settings.hooks.PreToolUse = [
87
+ ...settings.hooks.PreToolUse || [],
88
+ preToolUseHook
89
+ ];
90
+ settings.hooks.PostToolUse = [
91
+ ...settings.hooks.PostToolUse || [],
92
+ postToolUseHook
93
+ ];
94
+ settings.hooks.SessionEnd = [
95
+ ...settings.hooks.SessionEnd || [],
96
+ sessionEndHook
97
+ ];
98
+ const sessionStartHook = {
99
+ hooks: [{
100
+ type: "command",
101
+ command: `bashbros session-start ${BASHBROS_HOOK_MARKER}`
102
+ }]
103
+ };
104
+ settings.hooks.SessionStart = [
105
+ ...settings.hooks.SessionStart || [],
106
+ sessionStartHook
107
+ ];
108
+ if (!settings.mcpServers) {
109
+ settings.mcpServers = {};
110
+ }
111
+ if (!settings.mcpServers.bashbros) {
112
+ settings.mcpServers.bashbros = {
113
+ command: "npx",
114
+ args: ["bashbros", "mcp"]
115
+ };
116
+ }
117
+ this.saveSettings(settings);
118
+ return {
119
+ success: true,
120
+ message: "BashBros hooks installed successfully."
121
+ };
122
+ }
123
+ /**
124
+ * Uninstall BashBros hooks from Claude Code
125
+ */
126
+ static uninstall() {
127
+ if (!this.isClaudeInstalled()) {
128
+ return {
129
+ success: false,
130
+ message: "Claude Code not found."
131
+ };
132
+ }
133
+ const settings = this.loadSettings();
134
+ if (!settings.hooks) {
135
+ return {
136
+ success: true,
137
+ message: "No hooks to uninstall."
138
+ };
139
+ }
140
+ const filterHooks = (hooks) => {
141
+ if (!hooks) return [];
142
+ return hooks.filter(
143
+ (h) => !h.hooks.some((hook) => hook.command.includes(BASHBROS_HOOK_MARKER))
144
+ );
145
+ };
146
+ settings.hooks.PreToolUse = filterHooks(settings.hooks.PreToolUse);
147
+ settings.hooks.PostToolUse = filterHooks(settings.hooks.PostToolUse);
148
+ settings.hooks.SessionEnd = filterHooks(settings.hooks.SessionEnd);
149
+ settings.hooks.SessionStart = filterHooks(settings.hooks.SessionStart);
150
+ if (settings.hooks.UserPromptSubmit) {
151
+ settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(
152
+ (h) => !h.hooks.some((hook) => hook.command.includes(BASHBROS_PROMPT_MARKER))
153
+ );
154
+ }
155
+ if (settings.hooks.PreToolUse?.length === 0) delete settings.hooks.PreToolUse;
156
+ if (settings.hooks.PostToolUse?.length === 0) delete settings.hooks.PostToolUse;
157
+ if (settings.hooks.SessionEnd?.length === 0) delete settings.hooks.SessionEnd;
158
+ if (settings.hooks.SessionStart?.length === 0) delete settings.hooks.SessionStart;
159
+ if (settings.hooks.UserPromptSubmit?.length === 0) delete settings.hooks.UserPromptSubmit;
160
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
161
+ if (settings.mcpServers?.bashbros) {
162
+ delete settings.mcpServers.bashbros;
163
+ if (Object.keys(settings.mcpServers).length === 0) {
164
+ delete settings.mcpServers;
165
+ }
166
+ }
167
+ this.saveSettings(settings);
168
+ return {
169
+ success: true,
170
+ message: "BashBros hooks uninstalled successfully."
171
+ };
172
+ }
173
+ /**
174
+ * Check if BashBros hooks are installed
175
+ */
176
+ static isInstalled(settings) {
177
+ const s = settings || this.loadSettings();
178
+ if (!s.hooks) return false;
179
+ const hasMarker = (hooks) => {
180
+ if (!hooks) return false;
181
+ return hooks.some(
182
+ (h) => h.hooks.some((hook) => hook.command.includes(BASHBROS_HOOK_MARKER))
183
+ );
184
+ };
185
+ return hasMarker(s.hooks.PreToolUse) || hasMarker(s.hooks.PostToolUse) || hasMarker(s.hooks.SessionEnd) || hasMarker(s.hooks.SessionStart);
186
+ }
187
+ /**
188
+ * Check if MCP server config is installed
189
+ */
190
+ static isMCPInstalled(settings) {
191
+ const s = settings || this.loadSettings();
192
+ return !!s.mcpServers?.bashbros;
193
+ }
194
+ /**
195
+ * Get hook status
196
+ */
197
+ static getStatus() {
198
+ const claudeInstalled = this.isClaudeInstalled();
199
+ const settings = claudeInstalled ? this.loadSettings() : {};
200
+ const hooksInstalled = this.isInstalled(settings);
201
+ const allToolsInstalled = this.isAllToolsInstalled(settings);
202
+ const promptHookInstalled = this.isPromptHookInstalled(settings);
203
+ const mcpInstalled = this.isMCPInstalled(settings);
204
+ const hooks = [];
205
+ if (settings.hooks?.PreToolUse) hooks.push("PreToolUse (gate)");
206
+ if (settings.hooks?.PostToolUse) hooks.push("PostToolUse (record)");
207
+ if (settings.hooks?.SessionEnd) hooks.push("SessionEnd (report)");
208
+ if (settings.hooks?.SessionStart) hooks.push("SessionStart (session-start)");
209
+ if (allToolsInstalled) hooks.push("PostToolUse (all-tools)");
210
+ if (promptHookInstalled) hooks.push("UserPromptSubmit (prompt)");
211
+ if (mcpInstalled) hooks.push("MCP Server (bashbros)");
212
+ return {
213
+ claudeInstalled,
214
+ hooksInstalled,
215
+ allToolsInstalled,
216
+ promptHookInstalled,
217
+ mcpInstalled,
218
+ hooks
219
+ };
220
+ }
221
+ /**
222
+ * Check if all-tools recording is installed
223
+ */
224
+ static isAllToolsInstalled(settings) {
225
+ const s = settings || this.loadSettings();
226
+ if (!s.hooks?.PostToolUse) return false;
227
+ return s.hooks.PostToolUse.some(
228
+ (h) => h.hooks.some(
229
+ (hook) => hook.command.includes(BASHBROS_ALL_TOOLS_MARKER) || hook.command.includes("bashbros-all-tools")
230
+ )
231
+ );
232
+ }
233
+ /**
234
+ * Install all-tools recording hook (records ALL Claude Code tools, not just Bash)
235
+ */
236
+ static installAllTools() {
237
+ if (!this.isClaudeInstalled()) {
238
+ return {
239
+ success: false,
240
+ message: "Claude Code not found. Install Claude Code first."
241
+ };
242
+ }
243
+ const settings = this.loadSettings();
244
+ if (!settings.hooks) {
245
+ settings.hooks = {};
246
+ }
247
+ if (this.isAllToolsInstalled(settings)) {
248
+ return {
249
+ success: true,
250
+ message: "BashBros all-tools recording already installed."
251
+ };
252
+ }
253
+ const allToolsHook = {
254
+ matcher: "",
255
+ // Empty matcher matches ALL tools
256
+ hooks: [{
257
+ type: "command",
258
+ command: `bashbros record-tool ${BASHBROS_ALL_TOOLS_MARKER}`
259
+ }]
260
+ };
261
+ settings.hooks.PostToolUse = [
262
+ allToolsHook,
263
+ ...settings.hooks.PostToolUse || []
264
+ ];
265
+ this.saveSettings(settings);
266
+ return {
267
+ success: true,
268
+ message: "BashBros all-tools recording installed. All Claude Code tools will now be recorded."
269
+ };
270
+ }
271
+ /**
272
+ * Uninstall all-tools recording hook
273
+ */
274
+ static uninstallAllTools() {
275
+ if (!this.isClaudeInstalled()) {
276
+ return {
277
+ success: false,
278
+ message: "Claude Code not found."
279
+ };
280
+ }
281
+ const settings = this.loadSettings();
282
+ if (!settings.hooks?.PostToolUse) {
283
+ return {
284
+ success: true,
285
+ message: "No all-tools hook to uninstall."
286
+ };
287
+ }
288
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(
289
+ (h) => !h.hooks.some(
290
+ (hook) => hook.command.includes(BASHBROS_ALL_TOOLS_MARKER) || hook.command.includes("bashbros-all-tools")
291
+ )
292
+ );
293
+ if (settings.hooks.PostToolUse.length === 0) {
294
+ delete settings.hooks.PostToolUse;
295
+ }
296
+ if (Object.keys(settings.hooks).length === 0) {
297
+ delete settings.hooks;
298
+ }
299
+ this.saveSettings(settings);
300
+ return {
301
+ success: true,
302
+ message: "BashBros all-tools recording uninstalled."
303
+ };
304
+ }
305
+ /**
306
+ * Check if prompt recording hook is installed
307
+ */
308
+ static isPromptHookInstalled(settings) {
309
+ const s = settings || this.loadSettings();
310
+ if (!s.hooks?.UserPromptSubmit) return false;
311
+ return s.hooks.UserPromptSubmit.some(
312
+ (h) => h.hooks.some((hook) => hook.command.includes(BASHBROS_PROMPT_MARKER))
313
+ );
314
+ }
315
+ /**
316
+ * Install prompt recording hook (records user prompt submissions)
317
+ */
318
+ static installPromptHook() {
319
+ if (!this.isClaudeInstalled()) {
320
+ return {
321
+ success: false,
322
+ message: "Claude Code not found. Install Claude Code first."
323
+ };
324
+ }
325
+ const settings = this.loadSettings();
326
+ if (!settings.hooks) {
327
+ settings.hooks = {};
328
+ }
329
+ if (this.isPromptHookInstalled(settings)) {
330
+ return {
331
+ success: true,
332
+ message: "BashBros prompt recording already installed."
333
+ };
334
+ }
335
+ const promptHook = {
336
+ hooks: [{
337
+ type: "command",
338
+ command: `bashbros record-prompt ${BASHBROS_PROMPT_MARKER}`
339
+ }]
340
+ };
341
+ settings.hooks.UserPromptSubmit = [
342
+ ...settings.hooks.UserPromptSubmit || [],
343
+ promptHook
344
+ ];
345
+ this.saveSettings(settings);
346
+ return {
347
+ success: true,
348
+ message: "BashBros prompt recording installed. User prompts will now be recorded."
349
+ };
350
+ }
351
+ /**
352
+ * Uninstall prompt recording hook
353
+ */
354
+ static uninstallPromptHook() {
355
+ if (!this.isClaudeInstalled()) {
356
+ return {
357
+ success: false,
358
+ message: "Claude Code not found."
359
+ };
360
+ }
361
+ const settings = this.loadSettings();
362
+ if (!settings.hooks?.UserPromptSubmit) {
363
+ return {
364
+ success: true,
365
+ message: "No prompt hook to uninstall."
366
+ };
367
+ }
368
+ settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(
369
+ (h) => !h.hooks.some((hook) => hook.command.includes(BASHBROS_PROMPT_MARKER))
370
+ );
371
+ if (settings.hooks.UserPromptSubmit.length === 0) {
372
+ delete settings.hooks.UserPromptSubmit;
373
+ }
374
+ if (Object.keys(settings.hooks).length === 0) {
375
+ delete settings.hooks;
376
+ }
377
+ this.saveSettings(settings);
378
+ return {
379
+ success: true,
380
+ message: "BashBros prompt recording uninstalled."
381
+ };
382
+ }
383
+ };
384
+ async function gateCommand(command) {
385
+ const { PolicyEngine } = await import("./engine-4WNPXVMS.js");
386
+ const { RiskScorer } = await import("./risk-scorer-Y6KF2XCZ.js");
387
+ const { loadConfig } = await import("./config-IXBXMIUA.js");
388
+ const config = loadConfig();
389
+ const engine = new PolicyEngine(config);
390
+ const scorer = new RiskScorer();
391
+ const violations = engine.validate(command);
392
+ const risk = scorer.score(command);
393
+ if (violations.length > 0) {
394
+ return {
395
+ allowed: false,
396
+ reason: violations[0].message,
397
+ riskScore: risk.score
398
+ };
399
+ }
400
+ if (config.riskScoring.enabled) {
401
+ if (risk.score >= config.riskScoring.blockThreshold) {
402
+ return {
403
+ allowed: false,
404
+ reason: `Risk score ${risk.score} >= block threshold ${config.riskScoring.blockThreshold}: ${risk.factors.join(", ")}`,
405
+ riskScore: risk.score
406
+ };
407
+ }
408
+ if (risk.score >= config.riskScoring.warnThreshold) {
409
+ process.stderr.write(`[BashBros] Warning: risk score ${risk.score} (${risk.factors.join(", ")})
410
+ `);
411
+ }
412
+ }
413
+ try {
414
+ const { join: join2 } = await import("path");
415
+ const { homedir: homedir2 } = await import("os");
416
+ const { DashboardDB } = await import("./db-GJALN3R7.js");
417
+ const { checkLoopDetection, checkAnomalyDetection, checkRateLimit } = await import("./db-checks-2YOVECD4.js");
418
+ const dbPath = join2(homedir2(), ".bashbros", "dashboard.db");
419
+ const db = new DashboardDB(dbPath);
420
+ try {
421
+ if (config.loopDetection.enabled) {
422
+ const loop = checkLoopDetection(command, config.loopDetection, db);
423
+ if (loop.violation) {
424
+ db.close();
425
+ return { allowed: false, reason: loop.violation.message, riskScore: risk.score };
426
+ }
427
+ if (loop.warning) {
428
+ process.stderr.write(`[BashBros] ${loop.warning}
429
+ `);
430
+ }
431
+ }
432
+ if (config.anomalyDetection.enabled) {
433
+ const anomaly = checkAnomalyDetection(command, config.anomalyDetection, db);
434
+ if (anomaly.violation) {
435
+ db.close();
436
+ return { allowed: false, reason: anomaly.violation.message, riskScore: risk.score };
437
+ }
438
+ if (anomaly.warning) {
439
+ process.stderr.write(`[BashBros] ${anomaly.warning}
440
+ `);
441
+ }
442
+ }
443
+ if (config.rateLimit.enabled) {
444
+ const rate = checkRateLimit(config.rateLimit, db);
445
+ if (rate.violation) {
446
+ db.close();
447
+ return { allowed: false, reason: rate.violation.message, riskScore: risk.score };
448
+ }
449
+ }
450
+ } finally {
451
+ db.close();
452
+ }
453
+ } catch {
454
+ }
455
+ return {
456
+ allowed: true,
457
+ riskScore: risk.score
458
+ };
459
+ }
460
+
461
+ export {
462
+ ClaudeCodeHooks,
463
+ gateCommand
464
+ };
465
+ //# sourceMappingURL=chunk-25TREQ6V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/claude-code.ts"],"sourcesContent":["/**\n * Claude Code Hook Integration\n * Seamlessly integrate BashBros with Claude Code\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'\nimport { join } from 'path'\nimport { homedir } from 'os'\n\nexport interface ClaudeSettings {\n hooks?: {\n PreToolUse?: HookConfig[]\n PostToolUse?: HookConfig[]\n SessionEnd?: HookConfig[]\n SessionStart?: HookConfig[]\n UserPromptSubmit?: HookConfig[]\n }\n mcpServers?: Record<string, { command: string; args: string[] }>\n [key: string]: unknown\n}\n\ninterface HookConfig {\n matcher?: string\n hooks: { type: string; command: string }[]\n}\n\nconst CLAUDE_SETTINGS_PATH = join(homedir(), '.claude', 'settings.json')\nconst CLAUDE_DIR = join(homedir(), '.claude')\n\nconst BASHBROS_HOOK_MARKER = '# bashbros-managed'\n// Use --marker flag for Windows compatibility (ignored by the command but lets us identify our hooks)\nconst BASHBROS_ALL_TOOLS_MARKER = '--marker=bashbros-all-tools'\nconst BASHBROS_PROMPT_MARKER = '--marker=bashbros-prompt'\n\nexport class ClaudeCodeHooks {\n /**\n * Check if Claude Code is installed\n */\n static isClaudeInstalled(): boolean {\n return existsSync(CLAUDE_DIR)\n }\n\n /**\n * Load current Claude settings\n */\n static loadSettings(): ClaudeSettings {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) {\n return {}\n }\n\n try {\n const content = readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8')\n return JSON.parse(content)\n } catch {\n return {}\n }\n }\n\n /**\n * Save Claude settings\n */\n static saveSettings(settings: ClaudeSettings): void {\n if (!existsSync(CLAUDE_DIR)) {\n mkdirSync(CLAUDE_DIR, { recursive: true })\n }\n\n writeFileSync(\n CLAUDE_SETTINGS_PATH,\n JSON.stringify(settings, null, 2),\n 'utf-8'\n )\n }\n\n /**\n * Install BashBros hooks into Claude Code\n */\n static install(): { success: boolean; message: string } {\n if (!this.isClaudeInstalled()) {\n return {\n success: false,\n message: 'Claude Code not found. Install Claude Code first.'\n }\n }\n\n const settings = this.loadSettings()\n\n // Initialize hooks if not present\n if (!settings.hooks) {\n settings.hooks = {}\n }\n\n // Check if already installed\n if (this.isInstalled(settings)) {\n return {\n success: true,\n message: 'BashBros hooks already installed.'\n }\n }\n\n // Add PreToolUse hook for Bash commands\n const preToolUseHook: HookConfig = {\n matcher: 'Bash',\n hooks: [{\n type: 'command',\n command: `bashbros gate \"$TOOL_INPUT\" ${BASHBROS_HOOK_MARKER}`\n }]\n }\n\n // Add PostToolUse hook for metrics\n const postToolUseHook: HookConfig = {\n matcher: 'Bash',\n hooks: [{\n type: 'command',\n command: `bashbros record \"$TOOL_INPUT\" \"$TOOL_OUTPUT\" ${BASHBROS_HOOK_MARKER}`\n }]\n }\n\n // Add SessionEnd hook for reports\n const sessionEndHook: HookConfig = {\n hooks: [{\n type: 'command',\n command: `bashbros session-end ${BASHBROS_HOOK_MARKER}`\n }]\n }\n\n // Merge with existing hooks\n settings.hooks.PreToolUse = [\n ...(settings.hooks.PreToolUse || []),\n preToolUseHook\n ]\n\n settings.hooks.PostToolUse = [\n ...(settings.hooks.PostToolUse || []),\n postToolUseHook\n ]\n\n settings.hooks.SessionEnd = [\n ...(settings.hooks.SessionEnd || []),\n sessionEndHook\n ]\n\n // Add SessionStart hook for session initialization\n const sessionStartHook: HookConfig = {\n hooks: [{\n type: 'command',\n command: `bashbros session-start ${BASHBROS_HOOK_MARKER}`\n }]\n }\n\n settings.hooks.SessionStart = [\n ...(settings.hooks.SessionStart || []),\n sessionStartHook\n ]\n\n // Add MCP server config\n if (!settings.mcpServers) {\n settings.mcpServers = {}\n }\n if (!settings.mcpServers.bashbros) {\n settings.mcpServers.bashbros = {\n command: 'npx',\n args: ['bashbros', 'mcp'],\n }\n }\n\n this.saveSettings(settings)\n\n return {\n success: true,\n message: 'BashBros hooks installed successfully.'\n }\n }\n\n /**\n * Uninstall BashBros hooks from Claude Code\n */\n static uninstall(): { success: boolean; message: string } {\n if (!this.isClaudeInstalled()) {\n return {\n success: false,\n message: 'Claude Code not found.'\n }\n }\n\n const settings = this.loadSettings()\n\n if (!settings.hooks) {\n return {\n success: true,\n message: 'No hooks to uninstall.'\n }\n }\n\n // Remove BashBros hooks\n const filterHooks = (hooks: HookConfig[] | undefined): HookConfig[] => {\n if (!hooks) return []\n return hooks.filter(h =>\n !h.hooks.some(hook => hook.command.includes(BASHBROS_HOOK_MARKER))\n )\n }\n\n settings.hooks.PreToolUse = filterHooks(settings.hooks.PreToolUse)\n settings.hooks.PostToolUse = filterHooks(settings.hooks.PostToolUse)\n settings.hooks.SessionEnd = filterHooks(settings.hooks.SessionEnd)\n settings.hooks.SessionStart = filterHooks(settings.hooks.SessionStart)\n\n // Also remove prompt hooks\n if (settings.hooks.UserPromptSubmit) {\n settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(h =>\n !h.hooks.some(hook => hook.command.includes(BASHBROS_PROMPT_MARKER))\n )\n }\n\n // Clean up empty arrays\n if (settings.hooks.PreToolUse?.length === 0) delete settings.hooks.PreToolUse\n if (settings.hooks.PostToolUse?.length === 0) delete settings.hooks.PostToolUse\n if (settings.hooks.SessionEnd?.length === 0) delete settings.hooks.SessionEnd\n if (settings.hooks.SessionStart?.length === 0) delete settings.hooks.SessionStart\n if (settings.hooks.UserPromptSubmit?.length === 0) delete settings.hooks.UserPromptSubmit\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks\n\n // Remove MCP server config\n if (settings.mcpServers?.bashbros) {\n delete settings.mcpServers.bashbros\n if (Object.keys(settings.mcpServers).length === 0) {\n delete settings.mcpServers\n }\n }\n\n this.saveSettings(settings)\n\n return {\n success: true,\n message: 'BashBros hooks uninstalled successfully.'\n }\n }\n\n /**\n * Check if BashBros hooks are installed\n */\n static isInstalled(settings?: ClaudeSettings): boolean {\n const s = settings || this.loadSettings()\n\n if (!s.hooks) return false\n\n const hasMarker = (hooks: HookConfig[] | undefined): boolean => {\n if (!hooks) return false\n return hooks.some(h =>\n h.hooks.some(hook => hook.command.includes(BASHBROS_HOOK_MARKER))\n )\n }\n\n return hasMarker(s.hooks.PreToolUse) ||\n hasMarker(s.hooks.PostToolUse) ||\n hasMarker(s.hooks.SessionEnd) ||\n hasMarker(s.hooks.SessionStart)\n }\n\n /**\n * Check if MCP server config is installed\n */\n static isMCPInstalled(settings?: ClaudeSettings): boolean {\n const s = settings || this.loadSettings()\n return !!s.mcpServers?.bashbros\n }\n\n /**\n * Get hook status\n */\n static getStatus(): {\n claudeInstalled: boolean\n hooksInstalled: boolean\n allToolsInstalled: boolean\n promptHookInstalled: boolean\n mcpInstalled: boolean\n hooks: string[]\n } {\n const claudeInstalled = this.isClaudeInstalled()\n const settings = claudeInstalled ? this.loadSettings() : {}\n const hooksInstalled = this.isInstalled(settings)\n const allToolsInstalled = this.isAllToolsInstalled(settings)\n const promptHookInstalled = this.isPromptHookInstalled(settings)\n const mcpInstalled = this.isMCPInstalled(settings)\n\n const hooks: string[] = []\n if (settings.hooks?.PreToolUse) hooks.push('PreToolUse (gate)')\n if (settings.hooks?.PostToolUse) hooks.push('PostToolUse (record)')\n if (settings.hooks?.SessionEnd) hooks.push('SessionEnd (report)')\n if (settings.hooks?.SessionStart) hooks.push('SessionStart (session-start)')\n if (allToolsInstalled) hooks.push('PostToolUse (all-tools)')\n if (promptHookInstalled) hooks.push('UserPromptSubmit (prompt)')\n if (mcpInstalled) hooks.push('MCP Server (bashbros)')\n\n return {\n claudeInstalled,\n hooksInstalled,\n allToolsInstalled,\n promptHookInstalled,\n mcpInstalled,\n hooks\n }\n }\n\n /**\n * Check if all-tools recording is installed\n */\n static isAllToolsInstalled(settings?: ClaudeSettings): boolean {\n const s = settings || this.loadSettings()\n\n if (!s.hooks?.PostToolUse) return false\n\n // Check for both old (# bashbros-all-tools) and new (--marker=bashbros-all-tools) formats\n return s.hooks.PostToolUse.some(h =>\n h.hooks.some(hook =>\n hook.command.includes(BASHBROS_ALL_TOOLS_MARKER) ||\n hook.command.includes('bashbros-all-tools')\n )\n )\n }\n\n /**\n * Install all-tools recording hook (records ALL Claude Code tools, not just Bash)\n */\n static installAllTools(): { success: boolean; message: string } {\n if (!this.isClaudeInstalled()) {\n return {\n success: false,\n message: 'Claude Code not found. Install Claude Code first.'\n }\n }\n\n const settings = this.loadSettings()\n\n // Initialize hooks if not present\n if (!settings.hooks) {\n settings.hooks = {}\n }\n\n // Check if already installed\n if (this.isAllToolsInstalled(settings)) {\n return {\n success: true,\n message: 'BashBros all-tools recording already installed.'\n }\n }\n\n // Add PostToolUse hook for ALL tools (empty matcher = all tools)\n const allToolsHook: HookConfig = {\n matcher: '', // Empty matcher matches ALL tools\n hooks: [{\n type: 'command',\n command: `bashbros record-tool ${BASHBROS_ALL_TOOLS_MARKER}`\n }]\n }\n\n // Add to beginning of PostToolUse hooks so it runs for all tools\n settings.hooks.PostToolUse = [\n allToolsHook,\n ...(settings.hooks.PostToolUse || [])\n ]\n\n this.saveSettings(settings)\n\n return {\n success: true,\n message: 'BashBros all-tools recording installed. All Claude Code tools will now be recorded.'\n }\n }\n\n /**\n * Uninstall all-tools recording hook\n */\n static uninstallAllTools(): { success: boolean; message: string } {\n if (!this.isClaudeInstalled()) {\n return {\n success: false,\n message: 'Claude Code not found.'\n }\n }\n\n const settings = this.loadSettings()\n\n if (!settings.hooks?.PostToolUse) {\n return {\n success: true,\n message: 'No all-tools hook to uninstall.'\n }\n }\n\n // Remove all-tools hook (both old and new marker formats)\n settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(h =>\n !h.hooks.some(hook =>\n hook.command.includes(BASHBROS_ALL_TOOLS_MARKER) ||\n hook.command.includes('bashbros-all-tools')\n )\n )\n\n // Clean up empty array\n if (settings.hooks.PostToolUse.length === 0) {\n delete settings.hooks.PostToolUse\n }\n if (Object.keys(settings.hooks).length === 0) {\n delete settings.hooks\n }\n\n this.saveSettings(settings)\n\n return {\n success: true,\n message: 'BashBros all-tools recording uninstalled.'\n }\n }\n /**\n * Check if prompt recording hook is installed\n */\n static isPromptHookInstalled(settings?: ClaudeSettings): boolean {\n const s = settings || this.loadSettings()\n\n if (!s.hooks?.UserPromptSubmit) return false\n\n return s.hooks.UserPromptSubmit.some(h =>\n h.hooks.some(hook => hook.command.includes(BASHBROS_PROMPT_MARKER))\n )\n }\n\n /**\n * Install prompt recording hook (records user prompt submissions)\n */\n static installPromptHook(): { success: boolean; message: string } {\n if (!this.isClaudeInstalled()) {\n return {\n success: false,\n message: 'Claude Code not found. Install Claude Code first.'\n }\n }\n\n const settings = this.loadSettings()\n\n if (!settings.hooks) {\n settings.hooks = {}\n }\n\n if (this.isPromptHookInstalled(settings)) {\n return {\n success: true,\n message: 'BashBros prompt recording already installed.'\n }\n }\n\n const promptHook: HookConfig = {\n hooks: [{\n type: 'command',\n command: `bashbros record-prompt ${BASHBROS_PROMPT_MARKER}`\n }]\n }\n\n settings.hooks.UserPromptSubmit = [\n ...(settings.hooks.UserPromptSubmit || []),\n promptHook\n ]\n\n this.saveSettings(settings)\n\n return {\n success: true,\n message: 'BashBros prompt recording installed. User prompts will now be recorded.'\n }\n }\n\n /**\n * Uninstall prompt recording hook\n */\n static uninstallPromptHook(): { success: boolean; message: string } {\n if (!this.isClaudeInstalled()) {\n return {\n success: false,\n message: 'Claude Code not found.'\n }\n }\n\n const settings = this.loadSettings()\n\n if (!settings.hooks?.UserPromptSubmit) {\n return {\n success: true,\n message: 'No prompt hook to uninstall.'\n }\n }\n\n settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(h =>\n !h.hooks.some(hook => hook.command.includes(BASHBROS_PROMPT_MARKER))\n )\n\n if (settings.hooks.UserPromptSubmit.length === 0) {\n delete settings.hooks.UserPromptSubmit\n }\n if (Object.keys(settings.hooks).length === 0) {\n delete settings.hooks\n }\n\n this.saveSettings(settings)\n\n return {\n success: true,\n message: 'BashBros prompt recording uninstalled.'\n }\n }\n}\n\n/**\n * Gate command - called by PreToolUse hook\n * Returns exit code 0 to allow, non-zero to block\n */\nexport async function gateCommand(command: string): Promise<{\n allowed: boolean\n reason?: string\n riskScore?: number\n}> {\n // Dynamic import to avoid circular deps\n const { PolicyEngine } = await import('../policy/engine.js')\n const { RiskScorer } = await import('../policy/risk-scorer.js')\n const { loadConfig } = await import('../config.js')\n\n const config = loadConfig()\n const engine = new PolicyEngine(config)\n const scorer = new RiskScorer()\n\n const violations = engine.validate(command)\n const risk = scorer.score(command)\n\n if (violations.length > 0) {\n return {\n allowed: false,\n reason: violations[0].message,\n riskScore: risk.score\n }\n }\n\n // Config-driven risk threshold checks\n if (config.riskScoring.enabled) {\n if (risk.score >= config.riskScoring.blockThreshold) {\n return {\n allowed: false,\n reason: `Risk score ${risk.score} >= block threshold ${config.riskScoring.blockThreshold}: ${risk.factors.join(', ')}`,\n riskScore: risk.score\n }\n }\n if (risk.score >= config.riskScoring.warnThreshold) {\n process.stderr.write(`[BashBros] Warning: risk score ${risk.score} (${risk.factors.join(', ')})\\n`)\n }\n }\n\n // DB-backed cross-process checks (fail-open: DB errors never block commands)\n try {\n const { join } = await import('path')\n const { homedir } = await import('os')\n const { DashboardDB } = await import('../dashboard/db.js')\n const { checkLoopDetection, checkAnomalyDetection, checkRateLimit } = await import('../policy/db-checks.js')\n\n const dbPath = join(homedir(), '.bashbros', 'dashboard.db')\n const db = new DashboardDB(dbPath)\n try {\n // Loop detection\n if (config.loopDetection.enabled) {\n const loop = checkLoopDetection(command, config.loopDetection, db)\n if (loop.violation) {\n db.close()\n return { allowed: false, reason: loop.violation.message, riskScore: risk.score }\n }\n if (loop.warning) {\n process.stderr.write(`[BashBros] ${loop.warning}\\n`)\n }\n }\n\n // Anomaly detection\n if (config.anomalyDetection.enabled) {\n const anomaly = checkAnomalyDetection(command, config.anomalyDetection, db)\n if (anomaly.violation) {\n db.close()\n return { allowed: false, reason: anomaly.violation.message, riskScore: risk.score }\n }\n if (anomaly.warning) {\n process.stderr.write(`[BashBros] ${anomaly.warning}\\n`)\n }\n }\n\n // Rate limiting\n if (config.rateLimit.enabled) {\n const rate = checkRateLimit(config.rateLimit, db)\n if (rate.violation) {\n db.close()\n return { allowed: false, reason: rate.violation.message, riskScore: risk.score }\n }\n }\n } finally {\n db.close()\n }\n } catch {\n // Fail-open: DB errors never block commands\n }\n\n return {\n allowed: true,\n riskScore: risk.score\n }\n}\n"],"mappings":"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","names":["join","homedir"]}
@@ -64,7 +64,7 @@ function validateConfig(parsed) {
64
64
  }
65
65
  const config = parsed;
66
66
  const validated = {};
67
- const validAgents = ["claude-code", "clawdbot", "gemini-cli", "aider", "opencode", "custom"];
67
+ const validAgents = ["claude-code", "clawdbot", "moltbot", "gemini-cli", "copilot-cli", "aider", "opencode", "custom"];
68
68
  if (typeof config.agent === "string" && validAgents.includes(config.agent)) {
69
69
  validated.agent = config.agent;
70
70
  }
@@ -165,6 +165,15 @@ function validateConfig(parsed) {
165
165
  backupPath: typeof undo.backupPath === "string" ? undo.backupPath.slice(0, 500) : "~/.bashbros/undo"
166
166
  };
167
167
  }
168
+ if (config.sessionStart && typeof config.sessionStart === "object") {
169
+ const ss = config.sessionStart;
170
+ validated.sessionStart = {
171
+ enabled: typeof ss.enabled === "boolean" ? ss.enabled : true,
172
+ collectMetadata: typeof ss.collectMetadata === "boolean" ? ss.collectMetadata : true,
173
+ ollamaStatus: typeof ss.ollamaStatus === "boolean" ? ss.ollamaStatus : false,
174
+ preloadContext: typeof ss.preloadContext === "boolean" ? ss.preloadContext : true
175
+ };
176
+ }
168
177
  return validated;
169
178
  }
170
179
  function validateRiskPatterns(value) {
@@ -228,7 +237,7 @@ function validateRemotePath(value) {
228
237
  function getDefaultConfig() {
229
238
  return {
230
239
  agent: "claude-code",
231
- profile: "balanced",
240
+ profile: "permissive",
232
241
  commands: getDefaultCommands("balanced"),
233
242
  paths: getDefaultPaths("balanced"),
234
243
  secrets: {
@@ -259,7 +268,13 @@ function getDefaultConfig() {
259
268
  outputScanning: getDefaultOutputScanning("balanced"),
260
269
  undo: getDefaultUndo(),
261
270
  ward: getDefaultWard(),
262
- dashboard: getDefaultDashboard()
271
+ dashboard: getDefaultDashboard(),
272
+ sessionStart: {
273
+ enabled: true,
274
+ collectMetadata: true,
275
+ ollamaStatus: false,
276
+ preloadContext: true
277
+ }
263
278
  };
264
279
  }
265
280
  function getDefaultRiskScoring(profile) {
@@ -498,9 +513,10 @@ function getDefaultCommands(profile) {
498
513
  "biome *",
499
514
  "ruff *",
500
515
  "black *",
501
- // AI coding assistants
516
+ // AI coding assistants & security tools
502
517
  "claude *",
503
518
  "aider *",
519
+ "bashbros *",
504
520
  // Editors
505
521
  "code *",
506
522
  "cursor *",
@@ -593,7 +609,8 @@ function mergeWithDefaults(parsed) {
593
609
  outputScanning: { ...defaults.outputScanning, ...parsed.outputScanning },
594
610
  undo: { ...defaults.undo, ...parsed.undo },
595
611
  ward: { ...defaults.ward, ...parsed.ward },
596
- dashboard: { ...defaults.dashboard, ...parsed.dashboard }
612
+ dashboard: { ...defaults.dashboard, ...parsed.dashboard },
613
+ sessionStart: { ...defaults.sessionStart, ...parsed.sessionStart }
597
614
  };
598
615
  }
599
616
 
@@ -602,4 +619,4 @@ export {
602
619
  loadConfig,
603
620
  getDefaultConfig
604
621
  };
605
- //# sourceMappingURL=chunk-A535VV7N.js.map
622
+ //# sourceMappingURL=chunk-2CI2MRKI.js.map