bashbros 0.1.4 → 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 (53) hide show
  1. package/README.md +45 -44
  2. package/dist/{chunk-LZYW7XQO.js → chunk-25TREQ6V.js} +131 -5
  3. package/dist/chunk-25TREQ6V.js.map +1 -0
  4. package/dist/{chunk-RTZ4QWG2.js → chunk-2CI2MRKI.js} +19 -3
  5. package/dist/chunk-2CI2MRKI.js.map +1 -0
  6. package/dist/chunk-5BBPRDWL.js +186 -0
  7. package/dist/chunk-5BBPRDWL.js.map +1 -0
  8. package/dist/{chunk-7OEWYFN3.js → chunk-6QVMBCSX.js} +7 -306
  9. package/dist/chunk-6QVMBCSX.js.map +1 -0
  10. package/dist/{chunk-RDNSS3ME.js → chunk-6SLR5WPD.js} +173 -5
  11. package/dist/chunk-6SLR5WPD.js.map +1 -0
  12. package/dist/{chunk-KYDMPE4N.js → chunk-AZVT6AZY.js} +20 -2
  13. package/dist/chunk-AZVT6AZY.js.map +1 -0
  14. package/dist/{chunk-CG6VEHJM.js → chunk-C4GZNBFF.js} +2 -2
  15. package/dist/{chunk-EMLEJVJZ.js → chunk-JOIAG54E.js} +1 -107
  16. package/dist/chunk-JOIAG54E.js.map +1 -0
  17. package/dist/{chunk-QWZGB4V3.js → chunk-PAZIDRXK.js} +42 -181
  18. package/dist/chunk-PAZIDRXK.js.map +1 -0
  19. package/dist/chunk-PLSHJHHR.js +293 -0
  20. package/dist/chunk-PLSHJHHR.js.map +1 -0
  21. package/dist/chunk-R5I5DEXE.js +228 -0
  22. package/dist/chunk-R5I5DEXE.js.map +1 -0
  23. package/dist/cli.js +157 -122
  24. package/dist/cli.js.map +1 -1
  25. package/dist/{config-I5NCK3RJ.js → config-IXBXMIUA.js} +2 -2
  26. package/dist/{db-ETWTBXAE.js → db-GJALN3R7.js} +2 -2
  27. package/dist/{display-UH7KEHOW.js → display-UDIACHTP.js} +3 -3
  28. package/dist/{engine-EGPAS2EX.js → engine-4WNPXVMS.js} +3 -2
  29. package/dist/index.d.ts +57 -57
  30. package/dist/index.js +17 -8
  31. package/dist/index.js.map +1 -1
  32. package/dist/{ollama-5JVKNFOV.js → ollama-TNMD5WHW.js} +2 -2
  33. package/dist/server-3CMTP4W4.js +13 -0
  34. package/dist/{setup-YS27MOPE.js → setup-U4R5QJMV.js} +2 -2
  35. package/dist/static/index.html +75 -28
  36. package/dist/{writer-3NAVABN6.js → writer-OMHUMJR5.js} +3 -3
  37. package/dist/writer-OMHUMJR5.js.map +1 -0
  38. package/package.json +2 -1
  39. package/dist/chunk-7OEWYFN3.js.map +0 -1
  40. package/dist/chunk-EMLEJVJZ.js.map +0 -1
  41. package/dist/chunk-KYDMPE4N.js.map +0 -1
  42. package/dist/chunk-LZYW7XQO.js.map +0 -1
  43. package/dist/chunk-QWZGB4V3.js.map +0 -1
  44. package/dist/chunk-RDNSS3ME.js.map +0 -1
  45. package/dist/chunk-RTZ4QWG2.js.map +0 -1
  46. /package/dist/{chunk-CG6VEHJM.js.map → chunk-C4GZNBFF.js.map} +0 -0
  47. /package/dist/{config-I5NCK3RJ.js.map → config-IXBXMIUA.js.map} +0 -0
  48. /package/dist/{db-ETWTBXAE.js.map → db-GJALN3R7.js.map} +0 -0
  49. /package/dist/{display-UH7KEHOW.js.map → display-UDIACHTP.js.map} +0 -0
  50. /package/dist/{engine-EGPAS2EX.js.map → engine-4WNPXVMS.js.map} +0 -0
  51. /package/dist/{ollama-5JVKNFOV.js.map → ollama-TNMD5WHW.js.map} +0 -0
  52. /package/dist/{writer-3NAVABN6.js.map → server-3CMTP4W4.js.map} +0 -0
  53. /package/dist/{setup-YS27MOPE.js.map → setup-U4R5QJMV.js.map} +0 -0
package/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # BashBros
2
+ ![bashbros](https://github.com/user-attachments/assets/766502da-f31d-4304-a4dc-7f0a1845335f)
2
3
 
3
4
  ```
4
5
  /____ _ ____
@@ -38,6 +39,7 @@ Supports **Claude Code**, **Copilot CLI**, **Gemini CLI**, **OpenCode**, **Aider
38
39
  - [Settings Tab](#settings-tab)
39
40
  - [WebSocket Events](#websocket-events)
40
41
  - [Agent Integrations](#agent-integrations)
42
+ - [MCP Sidekick for Claude Code](#mcp-sidekick-for-claude-code)
41
43
  - [Ward (Network Security)](#ward-network-security)
42
44
  - [Observability](#observability)
43
45
  - [CLI Reference](#cli-reference)
@@ -124,17 +126,13 @@ BashBros provides nine security modules that work together to protect your syste
124
126
 
125
127
  ### AI Sidekick (Ollama-powered)
126
128
 
127
- BashBros includes a local AI sidekick powered by Ollama. It can explain commands, fix errors, suggest next steps, generate scripts, and perform security analysis -- all running locally on your machine.
129
+ BashBros includes a local AI sidekick powered by Ollama. It can suggest next steps, perform security analysis, and run interactive chat sessions -- all running locally on your machine.
128
130
 
129
131
  | Command | What it does |
130
132
  |---------|--------------|
131
- | `bashbros explain <command>` | Explain what a command does in plain language |
132
- | `bashbros fix <command> -e "error"` | Suggest fixes for failed commands |
133
133
  | `bashbros suggest` | Context-aware next command suggestions |
134
- | `bashbros script <description>` | Generate shell scripts from natural language |
135
- | `bashbros do <description>` | Convert natural language to executable commands |
136
134
  | `bashbros safety <command>` | AI-powered security risk analysis |
137
- | `bashbros ai <prompt>` | Free-form AI Q&A about your system |
135
+ | `bashbros chat` | Interactive Ollama conversation |
138
136
 
139
137
  Under the hood, the AI sidekick uses a hybrid routing system: pattern matching handles well-known commands instantly, while Ollama provides fallback analysis for ambiguous inputs. Suggestions are cached with a 5-minute TTL for fast repeated access.
140
138
 
@@ -152,7 +150,7 @@ Integrate fine-tuned LoRA adapters into your AI sidekick workflow:
152
150
 
153
151
  - Auto-discover GGUF LoRA adapters from `~/.bashgym/integration/models/adapters/`.
154
152
  - Activate adapters with one click (auto-generates Ollama Modelfile, registers with Ollama).
155
- - Per-function adapter routing -- assign different adapters to `suggest`, `safety`, `route`, `explain`, `fix`, and `script` functions.
153
+ - Per-function adapter routing -- assign different adapters to individual sidekick functions.
156
154
  - Connects to the [BashGym](https://github.com/GhostPeony/bashgym) training pipeline for continuous improvement.
157
155
 
158
156
  ### Model Profiles
@@ -267,6 +265,44 @@ BashBros hooks into six CLI agents with a single command per agent:
267
265
 
268
266
  Use `bashbros setup` for a guided multi-agent setup wizard.
269
267
 
268
+ ### MCP Sidekick for Claude Code
269
+
270
+ BashBros integrates with Claude Code as an MCP server, providing cross-session memory, local history intelligence, and safety tools that Claude Code doesn't have natively.
271
+
272
+ **Setup:**
273
+ ```bash
274
+ # Automatic (installs hooks + MCP server)
275
+ npx bashbros hook install
276
+
277
+ # Manual: add to ~/.claude/settings.json
278
+ {
279
+ "mcpServers": {
280
+ "bashbros": {
281
+ "command": "npx",
282
+ "args": ["bashbros", "mcp"]
283
+ }
284
+ }
285
+ }
286
+ ```
287
+
288
+ **5 MCP Tools:**
289
+
290
+ | Tool | Description |
291
+ |------|-------------|
292
+ | `session_summary` | Structured summaries of recent sessions -- commands, risk scores, AI-generated descriptions |
293
+ | `trace_search` | Search command history across sessions by text query |
294
+ | `history_suggest` | Smart next-command suggestions based on local usage patterns |
295
+ | `secret_scan` | Scan text for leaked credentials, API keys, tokens, and private keys |
296
+ | `code_task` | Delegate bounded coding tasks to a local Ollama model |
297
+
298
+ **Capability Tiers (code_task):**
299
+
300
+ | Model Size | Tier | Handles |
301
+ |-----------|------|---------|
302
+ | < 14B | Basic | Formatting, renames, imports, boilerplate, type annotations |
303
+ | 14B - 32B | Moderate | Tests, error handling, function refactors, interface implementations |
304
+ | 33B+ | Advanced | Complex logic, multi-function refactors, pattern-following generation |
305
+
270
306
  ### Ward (Network Security)
271
307
 
272
308
  Network-level security scanning and egress monitoring:
@@ -302,15 +338,11 @@ bashbros audit [-n lines] View recent command history
302
338
  Requires [Ollama](https://ollama.com) running locally.
303
339
 
304
340
  ```
305
- bashbros explain <command> Explain what a command does
306
- bashbros fix <command> -e "err" Fix a failed command
307
341
  bashbros suggest Get next command suggestions
308
- bashbros ai <prompt> Ask Bash Bro anything
309
- bashbros script <desc> [-o file] Generate a shell script (optionally save to file)
310
- bashbros do <desc> [-x] Natural language to command (-x to execute)
311
342
  bashbros safety <command> AI security risk analysis
312
- bashbros help-ai <topic> Get AI help on a topic
343
+ bashbros chat Start an interactive Ollama conversation
313
344
  bashbros models List available Ollama models
345
+ bashbros mcp Start MCP server for Claude Code integration (stdio)
314
346
  ```
315
347
 
316
348
  ### Bash Bro Commands
@@ -412,37 +444,6 @@ $ bashbros route "git status"
412
444
  Route: Bash Bro (90% confidence)
413
445
  ```
414
446
 
415
- ### Generate a script from natural language
416
-
417
- ```bash
418
- $ bashbros script "backup all .env files"
419
- #!/bin/bash
420
- find . -name "*.env" -exec cp {} {}.backup \;
421
- ```
422
-
423
- ### Convert natural language to a command
424
-
425
- ```bash
426
- $ bashbros do "find large files over 100mb"
427
- $ find . -size +100M -type f
428
- ```
429
-
430
- ### Explain a command
431
-
432
- ```bash
433
- $ bashbros explain "tar -xzf archive.tar.gz"
434
- Extracts the gzip-compressed tar archive 'archive.tar.gz' into the current directory.
435
- -x: extract, -z: decompress gzip, -f: specify file
436
- ```
437
-
438
- ### Fix a failed command
439
-
440
- ```bash
441
- $ bashbros fix "npm start" -e "Error: Cannot find module 'express'"
442
- Suggestion: npm install express
443
- The 'express' module is missing. Install it to resolve the error.
444
- ```
445
-
446
447
  ### View session report
447
448
 
448
449
  ```bash
@@ -8,6 +8,7 @@ var CLAUDE_SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
8
8
  var CLAUDE_DIR = join(homedir(), ".claude");
9
9
  var BASHBROS_HOOK_MARKER = "# bashbros-managed";
10
10
  var BASHBROS_ALL_TOOLS_MARKER = "--marker=bashbros-all-tools";
11
+ var BASHBROS_PROMPT_MARKER = "--marker=bashbros-prompt";
11
12
  var ClaudeCodeHooks = class {
12
13
  /**
13
14
  * Check if Claude Code is installed
@@ -94,6 +95,25 @@ var ClaudeCodeHooks = class {
94
95
  ...settings.hooks.SessionEnd || [],
95
96
  sessionEndHook
96
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
+ }
97
117
  this.saveSettings(settings);
98
118
  return {
99
119
  success: true,
@@ -126,10 +146,24 @@ var ClaudeCodeHooks = class {
126
146
  settings.hooks.PreToolUse = filterHooks(settings.hooks.PreToolUse);
127
147
  settings.hooks.PostToolUse = filterHooks(settings.hooks.PostToolUse);
128
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
+ }
129
155
  if (settings.hooks.PreToolUse?.length === 0) delete settings.hooks.PreToolUse;
130
156
  if (settings.hooks.PostToolUse?.length === 0) delete settings.hooks.PostToolUse;
131
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;
132
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
+ }
133
167
  this.saveSettings(settings);
134
168
  return {
135
169
  success: true,
@@ -148,7 +182,14 @@ var ClaudeCodeHooks = class {
148
182
  (h) => h.hooks.some((hook) => hook.command.includes(BASHBROS_HOOK_MARKER))
149
183
  );
150
184
  };
151
- return hasMarker(s.hooks.PreToolUse) || hasMarker(s.hooks.PostToolUse) || hasMarker(s.hooks.SessionEnd);
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;
152
193
  }
153
194
  /**
154
195
  * Get hook status
@@ -158,15 +199,22 @@ var ClaudeCodeHooks = class {
158
199
  const settings = claudeInstalled ? this.loadSettings() : {};
159
200
  const hooksInstalled = this.isInstalled(settings);
160
201
  const allToolsInstalled = this.isAllToolsInstalled(settings);
202
+ const promptHookInstalled = this.isPromptHookInstalled(settings);
203
+ const mcpInstalled = this.isMCPInstalled(settings);
161
204
  const hooks = [];
162
205
  if (settings.hooks?.PreToolUse) hooks.push("PreToolUse (gate)");
163
206
  if (settings.hooks?.PostToolUse) hooks.push("PostToolUse (record)");
164
207
  if (settings.hooks?.SessionEnd) hooks.push("SessionEnd (report)");
208
+ if (settings.hooks?.SessionStart) hooks.push("SessionStart (session-start)");
165
209
  if (allToolsInstalled) hooks.push("PostToolUse (all-tools)");
210
+ if (promptHookInstalled) hooks.push("UserPromptSubmit (prompt)");
211
+ if (mcpInstalled) hooks.push("MCP Server (bashbros)");
166
212
  return {
167
213
  claudeInstalled,
168
214
  hooksInstalled,
169
215
  allToolsInstalled,
216
+ promptHookInstalled,
217
+ mcpInstalled,
170
218
  hooks
171
219
  };
172
220
  }
@@ -254,11 +302,89 @@ var ClaudeCodeHooks = class {
254
302
  message: "BashBros all-tools recording uninstalled."
255
303
  };
256
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
+ }
257
383
  };
258
384
  async function gateCommand(command) {
259
- const { PolicyEngine } = await import("./engine-EGPAS2EX.js");
385
+ const { PolicyEngine } = await import("./engine-4WNPXVMS.js");
260
386
  const { RiskScorer } = await import("./risk-scorer-Y6KF2XCZ.js");
261
- const { loadConfig } = await import("./config-I5NCK3RJ.js");
387
+ const { loadConfig } = await import("./config-IXBXMIUA.js");
262
388
  const config = loadConfig();
263
389
  const engine = new PolicyEngine(config);
264
390
  const scorer = new RiskScorer();
@@ -287,7 +413,7 @@ async function gateCommand(command) {
287
413
  try {
288
414
  const { join: join2 } = await import("path");
289
415
  const { homedir: homedir2 } = await import("os");
290
- const { DashboardDB } = await import("./db-ETWTBXAE.js");
416
+ const { DashboardDB } = await import("./db-GJALN3R7.js");
291
417
  const { checkLoopDetection, checkAnomalyDetection, checkRateLimit } = await import("./db-checks-2YOVECD4.js");
292
418
  const dbPath = join2(homedir2(), ".bashbros", "dashboard.db");
293
419
  const db = new DashboardDB(dbPath);
@@ -336,4 +462,4 @@ export {
336
462
  ClaudeCodeHooks,
337
463
  gateCommand
338
464
  };
339
- //# sourceMappingURL=chunk-LZYW7XQO.js.map
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"]}
@@ -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) {
@@ -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) {
@@ -594,7 +609,8 @@ function mergeWithDefaults(parsed) {
594
609
  outputScanning: { ...defaults.outputScanning, ...parsed.outputScanning },
595
610
  undo: { ...defaults.undo, ...parsed.undo },
596
611
  ward: { ...defaults.ward, ...parsed.ward },
597
- dashboard: { ...defaults.dashboard, ...parsed.dashboard }
612
+ dashboard: { ...defaults.dashboard, ...parsed.dashboard },
613
+ sessionStart: { ...defaults.sessionStart, ...parsed.sessionStart }
598
614
  };
599
615
  }
600
616
 
@@ -603,4 +619,4 @@ export {
603
619
  loadConfig,
604
620
  getDefaultConfig
605
621
  };
606
- //# sourceMappingURL=chunk-RTZ4QWG2.js.map
622
+ //# sourceMappingURL=chunk-2CI2MRKI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { readFileSync, existsSync, statSync } from 'fs'\nimport { parse } from 'yaml'\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport type {\n BashBrosConfig,\n SecurityProfile,\n RiskScoringPolicy,\n LoopDetectionPolicy,\n AnomalyDetectionPolicy,\n OutputScanningPolicy,\n UndoPolicy,\n RiskPattern,\n WardPolicy,\n DashboardPolicy\n} from './types.js'\n\nconst CONFIG_FILENAME = '.bashbros.yml'\n\n// Configuration limits for validation\nconst CONFIG_LIMITS = {\n maxPerMinute: { min: 1, max: 10000 },\n maxPerHour: { min: 1, max: 100000 },\n maxPatterns: 100,\n maxPathLength: 1000\n}\n\nexport function findConfig(): string | null {\n // Check current directory\n if (existsSync(CONFIG_FILENAME)) {\n return CONFIG_FILENAME\n }\n\n // Check home directory\n const homeConfig = join(homedir(), CONFIG_FILENAME)\n if (existsSync(homeConfig)) {\n return homeConfig\n }\n\n // Check ~/.bashbros/config.yml\n const dotConfig = join(homedir(), '.bashbros', 'config.yml')\n if (existsSync(dotConfig)) {\n return dotConfig\n }\n\n return null\n}\n\n/**\n * SECURITY: Validate config file permissions\n */\nfunction validateConfigPermissions(configPath: string): void {\n try {\n const stats = statSync(configPath)\n\n // On Unix, check if file is world-writable (security risk)\n if (process.platform !== 'win32') {\n const mode = stats.mode\n const worldWritable = (mode & 0o002) !== 0\n const groupWritable = (mode & 0o020) !== 0\n\n if (worldWritable || groupWritable) {\n console.warn(`⚠️ Warning: Config file ${configPath} has insecure permissions`)\n console.warn(' Run: chmod 600 ' + configPath)\n }\n }\n } catch {\n // Ignore permission check errors\n }\n}\n\nexport function loadConfig(path?: string): BashBrosConfig {\n const configPath = path || findConfig()\n\n if (!configPath) {\n return getDefaultConfig()\n }\n\n // SECURITY: Check file permissions\n validateConfigPermissions(configPath)\n\n const content = readFileSync(configPath, 'utf-8')\n\n // SECURITY: Use safe YAML parsing (no custom tags)\n let parsed: unknown\n try {\n parsed = parse(content, { strict: true })\n } catch (error) {\n console.error('Failed to parse config file:', error)\n return getDefaultConfig()\n }\n\n // SECURITY: Validate parsed config\n const validated = validateConfig(parsed)\n\n return mergeWithDefaults(validated)\n}\n\n/**\n * SECURITY: Validate and sanitize config values\n */\nfunction validateConfig(parsed: unknown): Partial<BashBrosConfig> {\n if (!parsed || typeof parsed !== 'object') {\n return {}\n }\n\n const config = parsed as Record<string, unknown>\n const validated: Partial<BashBrosConfig> = {}\n\n // Validate agent type\n const validAgents = ['claude-code', 'clawdbot', 'moltbot', 'gemini-cli', 'copilot-cli', 'aider', 'opencode', 'custom']\n if (typeof config.agent === 'string' && validAgents.includes(config.agent)) {\n validated.agent = config.agent as BashBrosConfig['agent']\n }\n\n // Validate profile\n const validProfiles = ['balanced', 'strict', 'permissive', 'custom']\n if (typeof config.profile === 'string' && validProfiles.includes(config.profile)) {\n validated.profile = config.profile as SecurityProfile\n }\n\n // Validate commands\n if (config.commands && typeof config.commands === 'object') {\n const cmds = config.commands as Record<string, unknown>\n validated.commands = {\n allow: validateStringArray(cmds.allow, CONFIG_LIMITS.maxPatterns),\n block: validateStringArray(cmds.block, CONFIG_LIMITS.maxPatterns)\n }\n }\n\n // Validate paths\n if (config.paths && typeof config.paths === 'object') {\n const paths = config.paths as Record<string, unknown>\n validated.paths = {\n allow: validatePathArray(paths.allow),\n block: validatePathArray(paths.block)\n }\n }\n\n // Validate secrets\n if (config.secrets && typeof config.secrets === 'object') {\n const secrets = config.secrets as Record<string, unknown>\n validated.secrets = {\n enabled: typeof secrets.enabled === 'boolean' ? secrets.enabled : true,\n mode: secrets.mode === 'audit' ? 'audit' : 'block',\n patterns: validateStringArray(secrets.patterns, CONFIG_LIMITS.maxPatterns)\n }\n }\n\n // Validate audit\n if (config.audit && typeof config.audit === 'object') {\n const audit = config.audit as Record<string, unknown>\n validated.audit = {\n enabled: typeof audit.enabled === 'boolean' ? audit.enabled : true,\n destination: validateAuditDestination(audit.destination),\n remotePath: validateRemotePath(audit.remotePath)\n }\n }\n\n // Validate rate limit\n if (config.rateLimit && typeof config.rateLimit === 'object') {\n const rl = config.rateLimit as Record<string, unknown>\n const maxPerMinute = validateNumber(rl.maxPerMinute, CONFIG_LIMITS.maxPerMinute)\n const maxPerHour = validateNumber(rl.maxPerHour, CONFIG_LIMITS.maxPerHour)\n\n // SECURITY: Ensure hour limit >= minute limit\n validated.rateLimit = {\n enabled: typeof rl.enabled === 'boolean' ? rl.enabled : true,\n maxPerMinute,\n maxPerHour: Math.max(maxPerHour, maxPerMinute)\n }\n }\n\n // Validate risk scoring\n if (config.riskScoring && typeof config.riskScoring === 'object') {\n const rs = config.riskScoring as Record<string, unknown>\n validated.riskScoring = {\n enabled: typeof rs.enabled === 'boolean' ? rs.enabled : true,\n blockThreshold: validateNumber(rs.blockThreshold, { min: 1, max: 10 }),\n warnThreshold: validateNumber(rs.warnThreshold, { min: 1, max: 10 }),\n customPatterns: validateRiskPatterns(rs.customPatterns)\n }\n }\n\n // Validate loop detection\n if (config.loopDetection && typeof config.loopDetection === 'object') {\n const ld = config.loopDetection as Record<string, unknown>\n validated.loopDetection = {\n enabled: typeof ld.enabled === 'boolean' ? ld.enabled : true,\n maxRepeats: validateNumber(ld.maxRepeats, { min: 1, max: 100 }),\n maxTurns: validateNumber(ld.maxTurns, { min: 10, max: 10000 }),\n similarityThreshold: validateNumber(ld.similarityThreshold, { min: 0, max: 1 }) / 1, // Keep as float\n cooldownMs: validateNumber(ld.cooldownMs, { min: 0, max: 60000 }),\n windowSize: validateNumber(ld.windowSize, { min: 5, max: 100 }),\n action: ld.action === 'block' ? 'block' : 'warn'\n }\n }\n\n // Validate anomaly detection\n if (config.anomalyDetection && typeof config.anomalyDetection === 'object') {\n const ad = config.anomalyDetection as Record<string, unknown>\n validated.anomalyDetection = {\n enabled: typeof ad.enabled === 'boolean' ? ad.enabled : true,\n workingHours: validateWorkingHours(ad.workingHours),\n typicalCommandsPerMinute: validateNumber(ad.typicalCommandsPerMinute, { min: 1, max: 1000 }),\n learningCommands: validateNumber(ad.learningCommands, { min: 10, max: 500 }),\n suspiciousPatterns: validateStringArray(ad.suspiciousPatterns, 50),\n action: ad.action === 'block' ? 'block' : 'warn'\n }\n }\n\n // Validate output scanning\n if (config.outputScanning && typeof config.outputScanning === 'object') {\n const os = config.outputScanning as Record<string, unknown>\n validated.outputScanning = {\n enabled: typeof os.enabled === 'boolean' ? os.enabled : true,\n scanForSecrets: typeof os.scanForSecrets === 'boolean' ? os.scanForSecrets : true,\n scanForErrors: typeof os.scanForErrors === 'boolean' ? os.scanForErrors : true,\n maxOutputLength: validateNumber(os.maxOutputLength, { min: 1000, max: 10000000 }),\n redactPatterns: validateStringArray(os.redactPatterns, 50)\n }\n }\n\n // Validate undo\n if (config.undo && typeof config.undo === 'object') {\n const undo = config.undo as Record<string, unknown>\n validated.undo = {\n enabled: typeof undo.enabled === 'boolean' ? undo.enabled : true,\n maxStackSize: validateNumber(undo.maxStackSize, { min: 10, max: 1000 }),\n maxFileSize: validateNumber(undo.maxFileSize, { min: 1024, max: 100 * 1024 * 1024 }),\n ttlMinutes: validateNumber(undo.ttlMinutes, { min: 5, max: 1440 }),\n backupPath: typeof undo.backupPath === 'string' ? undo.backupPath.slice(0, 500) : '~/.bashbros/undo'\n }\n }\n\n // Validate sessionStart\n if (config.sessionStart && typeof config.sessionStart === 'object') {\n const ss = config.sessionStart as Record<string, unknown>\n validated.sessionStart = {\n enabled: typeof ss.enabled === 'boolean' ? ss.enabled : true,\n collectMetadata: typeof ss.collectMetadata === 'boolean' ? ss.collectMetadata : true,\n ollamaStatus: typeof ss.ollamaStatus === 'boolean' ? ss.ollamaStatus : false,\n preloadContext: typeof ss.preloadContext === 'boolean' ? ss.preloadContext : true\n }\n }\n\n return validated\n}\n\nfunction validateRiskPatterns(value: unknown): RiskPattern[] {\n if (!Array.isArray(value)) return []\n\n return value\n .filter((item): item is Record<string, unknown> =>\n item && typeof item === 'object' &&\n typeof item.pattern === 'string' &&\n typeof item.score === 'number' &&\n typeof item.factor === 'string'\n )\n .slice(0, 50)\n .map(item => ({\n pattern: String(item.pattern).slice(0, 500),\n score: Math.max(1, Math.min(10, Math.floor(Number(item.score)))),\n factor: String(item.factor).slice(0, 200)\n }))\n}\n\nfunction validateWorkingHours(value: unknown): [number, number] {\n if (!Array.isArray(value) || value.length !== 2) {\n return [6, 22]\n }\n\n const start = Math.max(0, Math.min(23, Math.floor(Number(value[0]) || 0)))\n const end = Math.max(0, Math.min(24, Math.floor(Number(value[1]) || 24)))\n\n return [start, end]\n}\n\nfunction validateStringArray(value: unknown, maxItems: number): string[] {\n if (!Array.isArray(value)) return []\n\n return value\n .filter((item): item is string => typeof item === 'string')\n .slice(0, maxItems)\n .map(s => s.slice(0, 500)) // Limit string length\n}\n\nfunction validatePathArray(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n\n return value\n .filter((item): item is string => typeof item === 'string')\n .slice(0, CONFIG_LIMITS.maxPatterns)\n .map(s => s.slice(0, CONFIG_LIMITS.maxPathLength))\n .filter(s => !s.includes('\\0')) // Block null bytes\n}\n\nfunction validateNumber(value: unknown, limits: { min: number; max: number }): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return limits.min\n }\n return Math.max(limits.min, Math.min(limits.max, Math.floor(value)))\n}\n\nfunction validateAuditDestination(value: unknown): 'local' | 'remote' | 'both' {\n if (value === 'remote' || value === 'both') {\n return value\n }\n return 'local'\n}\n\n/**\n * SECURITY: Validate remote audit path (must be HTTPS)\n */\nfunction validateRemotePath(value: unknown): string | undefined {\n if (typeof value !== 'string') {\n return undefined\n }\n\n try {\n const url = new URL(value)\n\n // SECURITY: Only allow HTTPS\n if (url.protocol !== 'https:') {\n console.warn('⚠️ Warning: Remote audit path must use HTTPS. Ignoring:', value)\n return undefined\n }\n\n // Block localhost/private IPs for remote\n const hostname = url.hostname.toLowerCase()\n if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.startsWith('192.168.') || hostname.startsWith('10.')) {\n // Allow for testing but warn\n console.warn('⚠️ Warning: Remote audit path points to local address')\n }\n\n return value\n } catch {\n console.warn('⚠️ Warning: Invalid remote audit URL:', value)\n return undefined\n }\n}\n\nexport function getDefaultConfig(): BashBrosConfig {\n return {\n agent: 'claude-code',\n profile: 'permissive',\n commands: getDefaultCommands('balanced'),\n paths: getDefaultPaths('balanced'),\n secrets: {\n enabled: true,\n mode: 'block',\n patterns: [\n '.env*',\n '*.pem',\n '*.key',\n '*credentials*',\n '*secret*',\n '.aws/*',\n '.ssh/*'\n ]\n },\n audit: {\n enabled: true,\n destination: 'local'\n },\n rateLimit: {\n enabled: true,\n maxPerMinute: 100,\n maxPerHour: 1000\n },\n riskScoring: getDefaultRiskScoring('balanced'),\n loopDetection: getDefaultLoopDetection('balanced'),\n anomalyDetection: getDefaultAnomalyDetection('balanced'),\n outputScanning: getDefaultOutputScanning('balanced'),\n undo: getDefaultUndo(),\n ward: getDefaultWard(),\n dashboard: getDefaultDashboard(),\n sessionStart: {\n enabled: true,\n collectMetadata: true,\n ollamaStatus: false,\n preloadContext: true\n }\n }\n}\n\nfunction getDefaultRiskScoring(profile: SecurityProfile): RiskScoringPolicy {\n const thresholds: Record<string, { block: number; warn: number }> = {\n strict: { block: 6, warn: 3 },\n balanced: { block: 9, warn: 6 },\n permissive: { block: 10, warn: 8 }\n }\n const t = thresholds[profile] || thresholds.balanced\n\n return {\n enabled: true,\n blockThreshold: t.block,\n warnThreshold: t.warn,\n customPatterns: []\n }\n}\n\nfunction getDefaultLoopDetection(profile: SecurityProfile): LoopDetectionPolicy {\n const settings: Record<string, { maxRepeats: number; maxTurns: number; action: 'warn' | 'block' }> = {\n strict: { maxRepeats: 2, maxTurns: 50, action: 'block' },\n balanced: { maxRepeats: 3, maxTurns: 100, action: 'warn' },\n permissive: { maxRepeats: 5, maxTurns: 200, action: 'warn' }\n }\n const s = settings[profile] || settings.balanced\n\n return {\n enabled: true,\n maxRepeats: s.maxRepeats,\n maxTurns: s.maxTurns,\n similarityThreshold: 0.85,\n cooldownMs: 1000,\n windowSize: 20,\n action: s.action\n }\n}\n\nfunction getDefaultAnomalyDetection(profile: SecurityProfile): AnomalyDetectionPolicy {\n return {\n enabled: profile !== 'permissive',\n workingHours: [6, 22],\n typicalCommandsPerMinute: 30,\n learningCommands: 50,\n suspiciousPatterns: [],\n action: profile === 'strict' ? 'block' : 'warn'\n }\n}\n\nfunction getDefaultOutputScanning(profile: SecurityProfile): OutputScanningPolicy {\n return {\n enabled: true,\n scanForSecrets: true,\n scanForErrors: true,\n maxOutputLength: 100000,\n redactPatterns: [\n 'password\\\\s*[=:]\\\\s*\\\\S+',\n 'api[_-]?key\\\\s*[=:]\\\\s*\\\\S+',\n 'secret\\\\s*[=:]\\\\s*\\\\S+',\n 'token\\\\s*[=:]\\\\s*\\\\S+',\n 'Bearer\\\\s+[A-Za-z0-9\\\\-._~+/]+=*',\n 'sk-[A-Za-z0-9]{20,}',\n 'ghp_[A-Za-z0-9]{36}',\n 'glpat-[A-Za-z0-9\\\\-]{20,}'\n ]\n }\n}\n\nfunction getDefaultUndo(): UndoPolicy {\n return {\n enabled: true,\n maxStackSize: 100,\n maxFileSize: 10 * 1024 * 1024, // 10MB\n ttlMinutes: 60, // 1 hour\n backupPath: '~/.bashbros/undo'\n }\n}\n\nfunction getDefaultWard(): WardPolicy {\n return {\n enabled: true,\n exposure: {\n scanInterval: 30000, // 30 seconds\n externalProbe: false,\n severityActions: {\n low: 'alert',\n medium: 'alert',\n high: 'block',\n critical: 'block_and_kill'\n }\n },\n connectors: {\n proxyAllMcp: false,\n telemetryRetention: '7d'\n },\n egress: {\n defaultAction: 'block'\n }\n }\n}\n\nfunction getDefaultDashboard(): DashboardPolicy {\n return {\n enabled: true,\n port: 7890,\n bind: '127.0.0.1'\n }\n}\n\nfunction getDefaultCommands(profile: SecurityProfile) {\n const dangerousCommands = [\n // Destructive rm patterns (various flag orders)\n 'rm * /',\n 'rm * ~',\n 'rm * /*',\n 'rm * /home*',\n 'rm * /etc*',\n 'rm * /usr*',\n 'rm * /var*',\n 'rm * /bin*',\n 'rm * /sbin*',\n 'rm * /lib*',\n 'rm * /boot*',\n 'rm * /opt*',\n 'rm * /root*',\n 'rm * /srv*',\n 'rm * /mnt*',\n 'rm * /media*',\n // Windows destructive patterns\n 'rm * C:\\\\*',\n 'rm * C:/*',\n 'Remove-Item * C:\\\\*',\n 'Remove-Item * C:/*',\n 'rd /s *',\n 'rmdir /s *',\n // Fork bomb\n ':(){:|:&};:',\n // Disk destruction\n 'mkfs*',\n 'dd if=/dev/zero*',\n 'dd of=/dev/*',\n '> /dev/sda*',\n '> /dev/nvme*',\n '> /dev/hd*',\n // Dangerous permission changes\n 'chmod -R 777 /*',\n 'chmod -R 777 /',\n 'chmod * 777 /*',\n 'chown -R * /*',\n // Pipe to shell (code execution)\n 'curl * | bash*',\n 'curl * | sh*',\n 'wget * | bash*',\n 'wget * | sh*',\n 'curl * | sudo*',\n 'wget * | sudo*',\n // History/log destruction\n 'history -c*',\n 'shred *',\n // Network attacks\n ':(){ :|:& };:',\n // Dangerous redirects\n '> /etc/passwd*',\n '> /etc/shadow*'\n ]\n\n const commonAllowed = [\n // File operations\n 'ls *', 'dir *', 'cat *', 'head *', 'tail *', 'less *', 'more *',\n 'grep *', 'find *', 'rg *', 'fd *',\n 'mkdir *', 'touch *', 'cp *', 'mv *', 'rm *',\n 'cd *', 'pwd', 'echo *', 'printf *', 'which *', 'where *', 'type *',\n 'tar *', 'zip *', 'unzip *', 'gzip *', 'gunzip *',\n\n // Text processing\n 'sed *', 'awk *', 'sort *', 'uniq *', 'wc *', 'diff *', 'tr *',\n\n // Version control\n 'git *', 'gh *',\n\n // Package managers & runtimes\n 'npm *', 'npx *', 'pnpm *', 'yarn *', 'bun *',\n 'node *', 'deno *', 'tsx *', 'ts-node *',\n 'python *', 'python3 *', 'pip *', 'pip3 *', 'uv *', 'pipx *',\n 'cargo *', 'rustc *', 'rustup *',\n 'go *',\n\n // Build tools\n 'tsc *', 'esbuild *', 'vite *', 'webpack *', 'rollup *', 'tsup *',\n 'make *', 'cmake *',\n\n // Testing & linting\n 'jest *', 'vitest *', 'pytest *', 'mocha *',\n 'eslint *', 'prettier *', 'biome *', 'ruff *', 'black *',\n\n // AI coding assistants & security tools\n 'claude *', 'aider *', 'bashbros *',\n\n // Editors\n 'code *', 'cursor *', 'vim *', 'nvim *', 'nano *', 'emacs *',\n\n // Docker & containers\n 'docker *', 'docker-compose *', 'podman *',\n\n // Network (safe operations)\n 'curl *', 'wget *', 'ping *', 'ssh *',\n\n // System info\n 'env', 'env *', 'printenv *', 'whoami', 'hostname', 'uname *', 'date', 'uptime',\n 'ps *', 'top', 'htop', 'btop',\n\n // Shell basics\n 'clear', 'cls', 'history', 'alias *', 'export *', 'source *', 'exit',\n 'true', 'false', 'test *', 'man *', 'help *',\n\n // PowerShell (Windows)\n 'Get-*', 'Set-*', 'New-*', 'Remove-*', 'Select-*', 'Where-*', 'ForEach-*'\n ]\n\n if (profile === 'strict') {\n return { allow: [], block: dangerousCommands }\n }\n\n if (profile === 'permissive') {\n return { allow: ['*'], block: dangerousCommands }\n }\n\n // balanced\n return { allow: commonAllowed, block: dangerousCommands }\n}\n\nfunction getDefaultPaths(profile: SecurityProfile) {\n const dangerousPaths = [\n '~/.ssh',\n '~/.aws',\n '~/.gnupg',\n '~/.config/gh',\n '/etc/passwd',\n '/etc/shadow'\n ]\n\n if (profile === 'strict') {\n return { allow: ['.'], block: dangerousPaths }\n }\n\n if (profile === 'permissive') {\n return { allow: ['*'], block: dangerousPaths }\n }\n\n // balanced\n return { allow: ['.', '~'], block: dangerousPaths }\n}\n\nfunction mergeWithDefaults(parsed: Partial<BashBrosConfig>): BashBrosConfig {\n const defaults = getDefaultConfig()\n return {\n ...defaults,\n ...parsed,\n commands: { ...defaults.commands, ...parsed.commands },\n paths: { ...defaults.paths, ...parsed.paths },\n secrets: { ...defaults.secrets, ...parsed.secrets },\n audit: { ...defaults.audit, ...parsed.audit },\n rateLimit: { ...defaults.rateLimit, ...parsed.rateLimit },\n riskScoring: { ...defaults.riskScoring, ...parsed.riskScoring },\n loopDetection: { ...defaults.loopDetection, ...parsed.loopDetection },\n anomalyDetection: { ...defaults.anomalyDetection, ...parsed.anomalyDetection },\n outputScanning: { ...defaults.outputScanning, ...parsed.outputScanning },\n undo: { ...defaults.undo, ...parsed.undo },\n ward: { ...defaults.ward, ...parsed.ward },\n dashboard: { ...defaults.dashboard, ...parsed.dashboard },\n sessionStart: { ...defaults.sessionStart, ...parsed.sessionStart }\n }\n}\n\nexport { BashBrosConfig }\n"],"mappings":"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","names":[]}