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.
- package/README.md +45 -44
- package/dist/{chunk-LZYW7XQO.js → chunk-25TREQ6V.js} +131 -5
- package/dist/chunk-25TREQ6V.js.map +1 -0
- package/dist/{chunk-RTZ4QWG2.js → chunk-2CI2MRKI.js} +19 -3
- package/dist/chunk-2CI2MRKI.js.map +1 -0
- package/dist/chunk-5BBPRDWL.js +186 -0
- package/dist/chunk-5BBPRDWL.js.map +1 -0
- package/dist/{chunk-7OEWYFN3.js → chunk-6QVMBCSX.js} +7 -306
- package/dist/chunk-6QVMBCSX.js.map +1 -0
- package/dist/{chunk-RDNSS3ME.js → chunk-6SLR5WPD.js} +173 -5
- package/dist/chunk-6SLR5WPD.js.map +1 -0
- package/dist/{chunk-KYDMPE4N.js → chunk-AZVT6AZY.js} +20 -2
- package/dist/chunk-AZVT6AZY.js.map +1 -0
- package/dist/{chunk-CG6VEHJM.js → chunk-C4GZNBFF.js} +2 -2
- package/dist/{chunk-EMLEJVJZ.js → chunk-JOIAG54E.js} +1 -107
- package/dist/chunk-JOIAG54E.js.map +1 -0
- package/dist/{chunk-QWZGB4V3.js → chunk-PAZIDRXK.js} +42 -181
- package/dist/chunk-PAZIDRXK.js.map +1 -0
- package/dist/chunk-PLSHJHHR.js +293 -0
- package/dist/chunk-PLSHJHHR.js.map +1 -0
- package/dist/chunk-R5I5DEXE.js +228 -0
- package/dist/chunk-R5I5DEXE.js.map +1 -0
- package/dist/cli.js +157 -122
- package/dist/cli.js.map +1 -1
- package/dist/{config-I5NCK3RJ.js → config-IXBXMIUA.js} +2 -2
- package/dist/{db-ETWTBXAE.js → db-GJALN3R7.js} +2 -2
- package/dist/{display-UH7KEHOW.js → display-UDIACHTP.js} +3 -3
- package/dist/{engine-EGPAS2EX.js → engine-4WNPXVMS.js} +3 -2
- package/dist/index.d.ts +57 -57
- package/dist/index.js +17 -8
- package/dist/index.js.map +1 -1
- package/dist/{ollama-5JVKNFOV.js → ollama-TNMD5WHW.js} +2 -2
- package/dist/server-3CMTP4W4.js +13 -0
- package/dist/{setup-YS27MOPE.js → setup-U4R5QJMV.js} +2 -2
- package/dist/static/index.html +75 -28
- package/dist/{writer-3NAVABN6.js → writer-OMHUMJR5.js} +3 -3
- package/dist/writer-OMHUMJR5.js.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-7OEWYFN3.js.map +0 -1
- package/dist/chunk-EMLEJVJZ.js.map +0 -1
- package/dist/chunk-KYDMPE4N.js.map +0 -1
- package/dist/chunk-LZYW7XQO.js.map +0 -1
- package/dist/chunk-QWZGB4V3.js.map +0 -1
- package/dist/chunk-RDNSS3ME.js.map +0 -1
- package/dist/chunk-RTZ4QWG2.js.map +0 -1
- /package/dist/{chunk-CG6VEHJM.js.map → chunk-C4GZNBFF.js.map} +0 -0
- /package/dist/{config-I5NCK3RJ.js.map → config-IXBXMIUA.js.map} +0 -0
- /package/dist/{db-ETWTBXAE.js.map → db-GJALN3R7.js.map} +0 -0
- /package/dist/{display-UH7KEHOW.js.map → display-UDIACHTP.js.map} +0 -0
- /package/dist/{engine-EGPAS2EX.js.map → engine-4WNPXVMS.js.map} +0 -0
- /package/dist/{ollama-5JVKNFOV.js.map → ollama-TNMD5WHW.js.map} +0 -0
- /package/dist/{writer-3NAVABN6.js.map → server-3CMTP4W4.js.map} +0 -0
- /package/dist/{setup-YS27MOPE.js.map → setup-U4R5QJMV.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# BashBros
|
|
2
|
+

|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
385
|
+
const { PolicyEngine } = await import("./engine-4WNPXVMS.js");
|
|
260
386
|
const { RiskScorer } = await import("./risk-scorer-Y6KF2XCZ.js");
|
|
261
|
-
const { loadConfig } = await import("./config-
|
|
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-
|
|
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-
|
|
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-
|
|
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":[]}
|