bashbros 0.1.3 → 0.1.4

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 (66) hide show
  1. package/README.md +727 -265
  2. package/dist/adapters-JAZGGNVP.js +9 -0
  3. package/dist/chunk-4XZ64P4V.js +47 -0
  4. package/dist/chunk-4XZ64P4V.js.map +1 -0
  5. package/dist/{chunk-2RPTM6EQ.js → chunk-7OEWYFN3.js} +745 -629
  6. package/dist/chunk-7OEWYFN3.js.map +1 -0
  7. package/dist/{chunk-WPJJZLT6.js → chunk-CG6VEHJM.js} +3 -2
  8. package/dist/chunk-CG6VEHJM.js.map +1 -0
  9. package/dist/{chunk-DLP2O6PN.js → chunk-EMLEJVJZ.js} +102 -1
  10. package/dist/chunk-EMLEJVJZ.js.map +1 -0
  11. package/dist/chunk-IUUBCPMV.js +166 -0
  12. package/dist/chunk-IUUBCPMV.js.map +1 -0
  13. package/dist/chunk-J6ONXY6N.js +146 -0
  14. package/dist/chunk-J6ONXY6N.js.map +1 -0
  15. package/dist/{chunk-EYO44OMN.js → chunk-KYDMPE4N.js} +60 -17
  16. package/dist/chunk-KYDMPE4N.js.map +1 -0
  17. package/dist/chunk-LJE4EPIU.js +56 -0
  18. package/dist/chunk-LJE4EPIU.js.map +1 -0
  19. package/dist/chunk-LZYW7XQO.js +339 -0
  20. package/dist/chunk-LZYW7XQO.js.map +1 -0
  21. package/dist/{chunk-JYWQT2B4.js → chunk-RDNSS3ME.js} +489 -14
  22. package/dist/chunk-RDNSS3ME.js.map +1 -0
  23. package/dist/{chunk-A535VV7N.js → chunk-RTZ4QWG2.js} +5 -4
  24. package/dist/chunk-RTZ4QWG2.js.map +1 -0
  25. package/dist/chunk-SDN6TAGD.js +157 -0
  26. package/dist/chunk-SDN6TAGD.js.map +1 -0
  27. package/dist/chunk-T5ONCUHZ.js +198 -0
  28. package/dist/chunk-T5ONCUHZ.js.map +1 -0
  29. package/dist/cli.js +1069 -88
  30. package/dist/cli.js.map +1 -1
  31. package/dist/{config-43SK6SFI.js → config-I5NCK3RJ.js} +2 -2
  32. package/dist/copilot-cli-5WJWK5YT.js +9 -0
  33. package/dist/{db-SWJUUSFX.js → db-ETWTBXAE.js} +2 -2
  34. package/dist/db-checks-2YOVECD4.js +133 -0
  35. package/dist/db-checks-2YOVECD4.js.map +1 -0
  36. package/dist/{display-HFIFXOOL.js → display-UH7KEHOW.js} +3 -3
  37. package/dist/gemini-cli-3563EELZ.js +9 -0
  38. package/dist/gemini-cli-3563EELZ.js.map +1 -0
  39. package/dist/index.d.ts +176 -72
  40. package/dist/index.js +119 -398
  41. package/dist/index.js.map +1 -1
  42. package/dist/{ollama-HY35OHW4.js → ollama-5JVKNFOV.js} +2 -2
  43. package/dist/ollama-5JVKNFOV.js.map +1 -0
  44. package/dist/opencode-DRCY275R.js +9 -0
  45. package/dist/opencode-DRCY275R.js.map +1 -0
  46. package/dist/profiles-7CLN6TAT.js +9 -0
  47. package/dist/profiles-7CLN6TAT.js.map +1 -0
  48. package/dist/setup-YS27MOPE.js +124 -0
  49. package/dist/setup-YS27MOPE.js.map +1 -0
  50. package/dist/static/index.html +4815 -2007
  51. package/dist/store-WJ5Y7MOE.js +9 -0
  52. package/dist/store-WJ5Y7MOE.js.map +1 -0
  53. package/dist/{writer-4ZEAKUFD.js → writer-3NAVABN6.js} +3 -3
  54. package/dist/writer-3NAVABN6.js.map +1 -0
  55. package/package.json +77 -68
  56. package/dist/chunk-2RPTM6EQ.js.map +0 -1
  57. package/dist/chunk-A535VV7N.js.map +0 -1
  58. package/dist/chunk-DLP2O6PN.js.map +0 -1
  59. package/dist/chunk-EYO44OMN.js.map +0 -1
  60. package/dist/chunk-JYWQT2B4.js.map +0 -1
  61. package/dist/chunk-WPJJZLT6.js.map +0 -1
  62. /package/dist/{config-43SK6SFI.js.map → adapters-JAZGGNVP.js.map} +0 -0
  63. /package/dist/{db-SWJUUSFX.js.map → config-I5NCK3RJ.js.map} +0 -0
  64. /package/dist/{display-HFIFXOOL.js.map → copilot-cli-5WJWK5YT.js.map} +0 -0
  65. /package/dist/{ollama-HY35OHW4.js.map → db-ETWTBXAE.js.map} +0 -0
  66. /package/dist/{writer-4ZEAKUFD.js.map → display-UH7KEHOW.js.map} +0 -0
@@ -3,11 +3,11 @@ import {
3
3
  findConfig,
4
4
  getDefaultConfig,
5
5
  loadConfig
6
- } from "./chunk-A535VV7N.js";
6
+ } from "./chunk-RTZ4QWG2.js";
7
7
  import "./chunk-7OCVIDC7.js";
8
8
  export {
9
9
  findConfig,
10
10
  getDefaultConfig,
11
11
  loadConfig
12
12
  };
13
- //# sourceMappingURL=config-43SK6SFI.js.map
13
+ //# sourceMappingURL=config-I5NCK3RJ.js.map
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CopilotCLIHooks
4
+ } from "./chunk-SDN6TAGD.js";
5
+ import "./chunk-7OCVIDC7.js";
6
+ export {
7
+ CopilotCLIHooks
8
+ };
9
+ //# sourceMappingURL=copilot-cli-5WJWK5YT.js.map
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  DashboardDB,
4
4
  db_default
5
- } from "./chunk-JYWQT2B4.js";
5
+ } from "./chunk-RDNSS3ME.js";
6
6
  import "./chunk-7OCVIDC7.js";
7
7
  export {
8
8
  DashboardDB,
9
9
  db_default as default
10
10
  };
11
- //# sourceMappingURL=db-SWJUUSFX.js.map
11
+ //# sourceMappingURL=db-ETWTBXAE.js.map
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-7OCVIDC7.js";
3
+
4
+ // src/policy/db-checks.ts
5
+ function normalize(command) {
6
+ return command.toLowerCase().replace(/["']/g, "").replace(/\s+/g, " ").replace(/\d+/g, "N").replace(/[a-f0-9]{8,}/gi, "H").trim();
7
+ }
8
+ function jaccardSimilarity(a, b) {
9
+ const wordsA = new Set(a.split(/\s+/));
10
+ const wordsB = new Set(b.split(/\s+/));
11
+ const intersection = new Set([...wordsA].filter((x) => wordsB.has(x)));
12
+ const union = /* @__PURE__ */ new Set([...wordsA, ...wordsB]);
13
+ if (union.size === 0) return 1;
14
+ return intersection.size / union.size;
15
+ }
16
+ function checkLoopDetection(command, config, db) {
17
+ if (!config.enabled) return { violation: null, warning: null };
18
+ const totalCount = db.getTotalCommandCount();
19
+ if (totalCount >= config.maxTurns) {
20
+ const msg = `Maximum turns reached (${config.maxTurns}). Session may be stuck.`;
21
+ if (config.action === "block") {
22
+ return { violation: { type: "loop", rule: "max_turns", message: msg }, warning: null };
23
+ }
24
+ return { violation: null, warning: msg };
25
+ }
26
+ const recent = db.getRecentCommandTexts(config.windowSize);
27
+ const exactCount = recent.filter((r) => r.command === command).length;
28
+ if (exactCount >= config.maxRepeats) {
29
+ const msg = `Command repeated ${exactCount + 1} times: "${command.slice(0, 50)}"`;
30
+ if (config.action === "block") {
31
+ return { violation: { type: "loop", rule: "exact_repeat", message: msg }, warning: null };
32
+ }
33
+ return { violation: null, warning: msg };
34
+ }
35
+ const normalizedCmd = normalize(command);
36
+ const similarCount = recent.filter(
37
+ (r) => jaccardSimilarity(normalize(r.command), normalizedCmd) >= config.similarityThreshold
38
+ ).length;
39
+ if (similarCount >= config.maxRepeats) {
40
+ const msg = `Similar commands repeated ${similarCount + 1} times`;
41
+ if (config.action === "block") {
42
+ return { violation: { type: "loop", rule: "semantic_repeat", message: msg }, warning: null };
43
+ }
44
+ return { violation: null, warning: msg };
45
+ }
46
+ return { violation: null, warning: null };
47
+ }
48
+ var DEFAULT_SUSPICIOUS_PATTERNS = [
49
+ /\bpasswd\b/,
50
+ /\bshadow\b/,
51
+ /\/root\//,
52
+ /\.ssh\//,
53
+ /\.gnupg\//,
54
+ /\.aws\//,
55
+ /\.kube\//,
56
+ /wallet/i,
57
+ /crypto/i,
58
+ /bitcoin/i,
59
+ /ethereum/i,
60
+ /private.*key/i
61
+ ];
62
+ function checkAnomalyDetection(command, config, db) {
63
+ if (!config.enabled) return { violation: null, warning: null };
64
+ const totalCount = db.getTotalCommandCount();
65
+ if (totalCount < config.learningCommands) {
66
+ return { violation: null, warning: null };
67
+ }
68
+ const findings = [];
69
+ const hour = (/* @__PURE__ */ new Date()).getHours();
70
+ const [start, end] = config.workingHours;
71
+ if (hour < start || hour >= end) {
72
+ findings.push(`Activity outside working hours (${hour}:00, allowed ${start}-${end})`);
73
+ }
74
+ const oneMinuteAgo = new Date(Date.now() - 6e4).toISOString();
75
+ const recentCount = db.getCommandCountSince(oneMinuteAgo);
76
+ if (recentCount > config.typicalCommandsPerMinute * 2) {
77
+ findings.push(`High command rate: ${recentCount}/min (typical: ${config.typicalCommandsPerMinute})`);
78
+ }
79
+ const configPatterns = (config.suspiciousPatterns || []).map((p) => {
80
+ try {
81
+ return new RegExp(p, "i");
82
+ } catch {
83
+ return null;
84
+ }
85
+ }).filter((p) => p !== null);
86
+ const allPatterns = [...DEFAULT_SUSPICIOUS_PATTERNS, ...configPatterns];
87
+ for (const pattern of allPatterns) {
88
+ if (pattern.test(command)) {
89
+ findings.push(`Suspicious pattern: ${pattern.source}`);
90
+ break;
91
+ }
92
+ }
93
+ if (findings.length === 0) return { violation: null, warning: null };
94
+ const msg = `Anomaly: ${findings.join("; ")}`;
95
+ if (config.action === "block") {
96
+ return { violation: { type: "anomaly", rule: "anomaly_detection", message: msg }, warning: null };
97
+ }
98
+ return { violation: null, warning: msg };
99
+ }
100
+ function checkRateLimit(config, db) {
101
+ if (!config.enabled) return { violation: null, warning: null };
102
+ const oneMinuteAgo = new Date(Date.now() - 6e4).toISOString();
103
+ const perMinute = db.getCommandCountSince(oneMinuteAgo);
104
+ if (perMinute >= config.maxPerMinute) {
105
+ return {
106
+ violation: {
107
+ type: "rate_limit",
108
+ rule: "rate_per_minute",
109
+ message: `Rate limit exceeded: ${perMinute}/${config.maxPerMinute} per minute`
110
+ },
111
+ warning: null
112
+ };
113
+ }
114
+ const oneHourAgo = new Date(Date.now() - 36e5).toISOString();
115
+ const perHour = db.getCommandCountSince(oneHourAgo);
116
+ if (perHour >= config.maxPerHour) {
117
+ return {
118
+ violation: {
119
+ type: "rate_limit",
120
+ rule: "rate_per_hour",
121
+ message: `Rate limit exceeded: ${perHour}/${config.maxPerHour} per hour`
122
+ },
123
+ warning: null
124
+ };
125
+ }
126
+ return { violation: null, warning: null };
127
+ }
128
+ export {
129
+ checkAnomalyDetection,
130
+ checkLoopDetection,
131
+ checkRateLimit
132
+ };
133
+ //# sourceMappingURL=db-checks-2YOVECD4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/policy/db-checks.ts"],"sourcesContent":["/**\r\n * DB-backed cross-process security checks\r\n * Each function takes a DashboardDB + config section and returns a violation or warning.\r\n */\r\n\r\nimport type { DashboardDB } from '../dashboard/db.js'\r\nimport type {\r\n LoopDetectionPolicy,\r\n AnomalyDetectionPolicy,\r\n RateLimitPolicy,\r\n PolicyViolation\r\n} from '../types.js'\r\n\r\nexport interface CheckResult {\r\n violation: PolicyViolation | null\r\n warning: string | null\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────\r\n// Helpers (mirrored from LoopDetector for DB-backed checks)\r\n// ─────────────────────────────────────────────────────────────\r\n\r\nfunction normalize(command: string): string {\r\n return command\r\n .toLowerCase()\r\n .replace(/[\"']/g, '')\r\n .replace(/\\s+/g, ' ')\r\n .replace(/\\d+/g, 'N')\r\n .replace(/[a-f0-9]{8,}/gi, 'H')\r\n .trim()\r\n}\r\n\r\nfunction jaccardSimilarity(a: string, b: string): number {\r\n const wordsA = new Set(a.split(/\\s+/))\r\n const wordsB = new Set(b.split(/\\s+/))\r\n const intersection = new Set([...wordsA].filter(x => wordsB.has(x)))\r\n const union = new Set([...wordsA, ...wordsB])\r\n if (union.size === 0) return 1\r\n return intersection.size / union.size\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────\r\n// Loop Detection (DB-backed)\r\n// ─────────────────────────────────────────────────────────────\r\n\r\nexport function checkLoopDetection(\r\n command: string,\r\n config: LoopDetectionPolicy,\r\n db: DashboardDB\r\n): CheckResult {\r\n if (!config.enabled) return { violation: null, warning: null }\r\n\r\n // Check max turns\r\n const totalCount = db.getTotalCommandCount()\r\n if (totalCount >= config.maxTurns) {\r\n const msg = `Maximum turns reached (${config.maxTurns}). Session may be stuck.`\r\n if (config.action === 'block') {\r\n return { violation: { type: 'loop', rule: 'max_turns', message: msg }, warning: null }\r\n }\r\n return { violation: null, warning: msg }\r\n }\r\n\r\n // Check recent commands for repeats\r\n const recent = db.getRecentCommandTexts(config.windowSize)\r\n\r\n // Exact repeats\r\n const exactCount = recent.filter(r => r.command === command).length\r\n if (exactCount >= config.maxRepeats) {\r\n const msg = `Command repeated ${exactCount + 1} times: \"${command.slice(0, 50)}\"`\r\n if (config.action === 'block') {\r\n return { violation: { type: 'loop', rule: 'exact_repeat', message: msg }, warning: null }\r\n }\r\n return { violation: null, warning: msg }\r\n }\r\n\r\n // Semantic repeats\r\n const normalizedCmd = normalize(command)\r\n const similarCount = recent.filter(\r\n r => jaccardSimilarity(normalize(r.command), normalizedCmd) >= config.similarityThreshold\r\n ).length\r\n if (similarCount >= config.maxRepeats) {\r\n const msg = `Similar commands repeated ${similarCount + 1} times`\r\n if (config.action === 'block') {\r\n return { violation: { type: 'loop', rule: 'semantic_repeat', message: msg }, warning: null }\r\n }\r\n return { violation: null, warning: msg }\r\n }\r\n\r\n return { violation: null, warning: null }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────\r\n// Anomaly Detection (DB-backed)\r\n// ─────────────────────────────────────────────────────────────\r\n\r\n// Default suspicious patterns (same as anomaly-detector.ts)\r\nconst DEFAULT_SUSPICIOUS_PATTERNS: RegExp[] = [\r\n /\\bpasswd\\b/,\r\n /\\bshadow\\b/,\r\n /\\/root\\//,\r\n /\\.ssh\\//,\r\n /\\.gnupg\\//,\r\n /\\.aws\\//,\r\n /\\.kube\\//,\r\n /wallet/i,\r\n /crypto/i,\r\n /bitcoin/i,\r\n /ethereum/i,\r\n /private.*key/i,\r\n]\r\n\r\nexport function checkAnomalyDetection(\r\n command: string,\r\n config: AnomalyDetectionPolicy,\r\n db: DashboardDB\r\n): CheckResult {\r\n if (!config.enabled) return { violation: null, warning: null }\r\n\r\n // Still learning — skip checks\r\n const totalCount = db.getTotalCommandCount()\r\n if (totalCount < config.learningCommands) {\r\n return { violation: null, warning: null }\r\n }\r\n\r\n const findings: string[] = []\r\n\r\n // Check working hours\r\n const hour = new Date().getHours()\r\n const [start, end] = config.workingHours\r\n if (hour < start || hour >= end) {\r\n findings.push(`Activity outside working hours (${hour}:00, allowed ${start}-${end})`)\r\n }\r\n\r\n // Check frequency (commands in last minute)\r\n const oneMinuteAgo = new Date(Date.now() - 60_000).toISOString()\r\n const recentCount = db.getCommandCountSince(oneMinuteAgo)\r\n if (recentCount > config.typicalCommandsPerMinute * 2) {\r\n findings.push(`High command rate: ${recentCount}/min (typical: ${config.typicalCommandsPerMinute})`)\r\n }\r\n\r\n // Check suspicious patterns (config + built-in)\r\n const configPatterns = (config.suspiciousPatterns || []).map(p => {\r\n try { return new RegExp(p, 'i') } catch { return null }\r\n }).filter((p): p is RegExp => p !== null)\r\n\r\n const allPatterns = [...DEFAULT_SUSPICIOUS_PATTERNS, ...configPatterns]\r\n for (const pattern of allPatterns) {\r\n if (pattern.test(command)) {\r\n findings.push(`Suspicious pattern: ${pattern.source}`)\r\n break // one is enough\r\n }\r\n }\r\n\r\n if (findings.length === 0) return { violation: null, warning: null }\r\n\r\n const msg = `Anomaly: ${findings.join('; ')}`\r\n if (config.action === 'block') {\r\n return { violation: { type: 'anomaly', rule: 'anomaly_detection', message: msg }, warning: null }\r\n }\r\n return { violation: null, warning: msg }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────\r\n// Rate Limiting (DB-backed)\r\n// ─────────────────────────────────────────────────────────────\r\n\r\nexport function checkRateLimit(\r\n config: RateLimitPolicy,\r\n db: DashboardDB\r\n): CheckResult {\r\n if (!config.enabled) return { violation: null, warning: null }\r\n\r\n // Per-minute check\r\n const oneMinuteAgo = new Date(Date.now() - 60_000).toISOString()\r\n const perMinute = db.getCommandCountSince(oneMinuteAgo)\r\n if (perMinute >= config.maxPerMinute) {\r\n return {\r\n violation: {\r\n type: 'rate_limit',\r\n rule: 'rate_per_minute',\r\n message: `Rate limit exceeded: ${perMinute}/${config.maxPerMinute} per minute`\r\n },\r\n warning: null\r\n }\r\n }\r\n\r\n // Per-hour check\r\n const oneHourAgo = new Date(Date.now() - 3_600_000).toISOString()\r\n const perHour = db.getCommandCountSince(oneHourAgo)\r\n if (perHour >= config.maxPerHour) {\r\n return {\r\n violation: {\r\n type: 'rate_limit',\r\n rule: 'rate_per_hour',\r\n message: `Rate limit exceeded: ${perHour}/${config.maxPerHour} per hour`\r\n },\r\n warning: null\r\n }\r\n }\r\n\r\n return { violation: null, warning: null }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -5,8 +5,8 @@ import {
5
5
  formatAllAgentsInfo,
6
6
  formatPermissionsTable,
7
7
  getEffectivePermissions
8
- } from "./chunk-WPJJZLT6.js";
9
- import "./chunk-A535VV7N.js";
8
+ } from "./chunk-CG6VEHJM.js";
9
+ import "./chunk-RTZ4QWG2.js";
10
10
  import "./chunk-7OCVIDC7.js";
11
11
  export {
12
12
  formatAgentInfo,
@@ -15,4 +15,4 @@ export {
15
15
  formatPermissionsTable,
16
16
  getEffectivePermissions
17
17
  };
18
- //# sourceMappingURL=display-HFIFXOOL.js.map
18
+ //# sourceMappingURL=display-UH7KEHOW.js.map
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ GeminiCLIHooks
4
+ } from "./chunk-T5ONCUHZ.js";
5
+ import "./chunk-7OCVIDC7.js";
6
+ export {
7
+ GeminiCLIHooks
8
+ };
9
+ //# sourceMappingURL=gemini-cli-3563EELZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ interface BashBrosConfig {
16
16
  ward: WardPolicy;
17
17
  dashboard: DashboardPolicy;
18
18
  }
19
- type AgentType = 'claude-code' | 'clawdbot' | 'moltbot' | 'gemini-cli' | 'aider' | 'opencode' | 'custom';
19
+ type AgentType = 'claude-code' | 'clawdbot' | 'moltbot' | 'gemini-cli' | 'copilot-cli' | 'aider' | 'opencode' | 'custom';
20
20
  interface MoltbotGatewayInfo {
21
21
  port: number;
22
22
  host: string;
@@ -117,7 +117,7 @@ interface CommandResult {
117
117
  violations: PolicyViolation[];
118
118
  }
119
119
  interface PolicyViolation {
120
- type: 'command' | 'path' | 'secrets' | 'rate_limit';
120
+ type: 'command' | 'path' | 'secrets' | 'rate_limit' | 'risk_score' | 'loop' | 'anomaly' | 'output';
121
121
  rule: string;
122
122
  message: string;
123
123
  }
@@ -311,6 +311,133 @@ declare class SystemProfiler {
311
311
  toContext(): string;
312
312
  }
313
313
 
314
+ /**
315
+ * Simple Ollama client for local model inference.
316
+ * Keeps it minimal - just what we need for Bash Bro.
317
+ */
318
+ interface OllamaConfig {
319
+ host: string;
320
+ model: string;
321
+ timeout: number;
322
+ }
323
+ interface ChatMessage {
324
+ role: 'system' | 'user' | 'assistant';
325
+ content: string;
326
+ }
327
+ interface ModelInfo {
328
+ modelfile: string;
329
+ parameters: string;
330
+ template: string;
331
+ details: {
332
+ parent_model: string;
333
+ format: string;
334
+ family: string;
335
+ families: string[];
336
+ parameter_size: string;
337
+ quantization_level: string;
338
+ };
339
+ }
340
+ interface RunningModel {
341
+ name: string;
342
+ model: string;
343
+ size: number;
344
+ size_vram: number;
345
+ digest: string;
346
+ details: {
347
+ family: string;
348
+ parameter_size: string;
349
+ quantization_level: string;
350
+ };
351
+ expires_at: string;
352
+ }
353
+ declare class OllamaClient {
354
+ private config;
355
+ constructor(config?: Partial<OllamaConfig>);
356
+ /**
357
+ * Check if Ollama is running and accessible
358
+ */
359
+ isAvailable(): Promise<boolean>;
360
+ /**
361
+ * List available models
362
+ */
363
+ listModels(): Promise<string[]>;
364
+ /**
365
+ * Generate a response from the model
366
+ */
367
+ generate(prompt: string, systemPrompt?: string): Promise<string>;
368
+ /**
369
+ * Chat with the model (multi-turn conversation)
370
+ */
371
+ chat(messages: ChatMessage[]): Promise<string>;
372
+ /**
373
+ * Ask Bash Bro to suggest a command
374
+ */
375
+ suggestCommand(context: string): Promise<string | null>;
376
+ /**
377
+ * Ask Bash Bro to explain a command
378
+ */
379
+ explainCommand(command: string): Promise<string>;
380
+ /**
381
+ * Ask Bash Bro to fix a command that failed
382
+ */
383
+ fixCommand(command: string, error: string): Promise<string | null>;
384
+ /**
385
+ * Show detailed info about a model
386
+ */
387
+ showModel(name: string): Promise<ModelInfo | null>;
388
+ /**
389
+ * Delete a model
390
+ */
391
+ deleteModel(name: string): Promise<boolean>;
392
+ /**
393
+ * List currently running models
394
+ */
395
+ listRunning(): Promise<RunningModel[]>;
396
+ /**
397
+ * Pull a model from the registry
398
+ */
399
+ pullModel(name: string): Promise<boolean>;
400
+ /**
401
+ * Create a model from a Modelfile
402
+ */
403
+ createModel(name: string, modelfile: string): Promise<boolean>;
404
+ setModel(model: string): void;
405
+ getModel(): string;
406
+ /**
407
+ * Generate a shell script from a natural language description
408
+ */
409
+ generateScript(description: string, shell?: string): Promise<string | null>;
410
+ /**
411
+ * Analyze command safety and provide recommendations
412
+ */
413
+ analyzeCommandSafety(command: string): Promise<{
414
+ safe: boolean;
415
+ risk: 'low' | 'medium' | 'high' | 'critical';
416
+ explanation: string;
417
+ suggestions: string[];
418
+ }>;
419
+ /**
420
+ * Summarize a series of commands and their outputs
421
+ */
422
+ summarizeSession(commands: {
423
+ command: string;
424
+ output?: string;
425
+ error?: string;
426
+ }[]): Promise<string>;
427
+ /**
428
+ * Get help for a specific tool or command
429
+ */
430
+ getHelp(topic: string): Promise<string>;
431
+ /**
432
+ * Convert natural language to a command
433
+ */
434
+ naturalToCommand(description: string): Promise<string | null>;
435
+ /**
436
+ * Generate with a temporary model override (for adapter-specific calls)
437
+ */
438
+ generateWithAdapter(modelOverride: string, prompt: string, systemPrompt?: string): Promise<string>;
439
+ }
440
+
314
441
  type RouteDecision = 'bro' | 'main' | 'both';
315
442
  interface RoutingResult {
316
443
  decision: RouteDecision;
@@ -320,9 +447,11 @@ interface RoutingResult {
320
447
  declare class TaskRouter {
321
448
  private rules;
322
449
  private profile;
323
- constructor(profile?: SystemProfile | null);
450
+ private ollama;
451
+ constructor(profile?: SystemProfile | null, ollama?: OllamaClient | null);
324
452
  private buildDefaultRules;
325
453
  route(command: string): RoutingResult;
454
+ routeAsync(command: string): Promise<RoutingResult>;
326
455
  private looksSimple;
327
456
  addRule(pattern: RegExp, route: RouteDecision, reason: string): void;
328
457
  updateProfile(profile: SystemProfile): void;
@@ -338,9 +467,12 @@ declare class CommandSuggester {
338
467
  private history;
339
468
  private profile;
340
469
  private patterns;
341
- constructor(profile?: SystemProfile | null);
470
+ private ollama;
471
+ private aiCache;
472
+ constructor(profile?: SystemProfile | null, ollama?: OllamaClient | null);
342
473
  private initPatterns;
343
474
  suggest(context: SuggestionContext): Suggestion[];
475
+ suggestAsync(context: SuggestionContext): Promise<Suggestion[]>;
344
476
  private suggestFromPatterns;
345
477
  private suggestFromHistory;
346
478
  private suggestFromContext;
@@ -381,6 +513,23 @@ declare class BackgroundWorker extends EventEmitter {
381
513
  formatStatus(): string;
382
514
  }
383
515
 
516
+ type AdapterPurpose = 'suggest' | 'safety' | 'route' | 'explain' | 'fix' | 'script' | 'general';
517
+ interface AdapterEntry {
518
+ name: string;
519
+ baseModel: string;
520
+ purpose: AdapterPurpose;
521
+ adapterPath: string;
522
+ trainedAt: string;
523
+ tracesUsed: number;
524
+ qualityScore: number;
525
+ }
526
+
527
+ interface ModelProfile {
528
+ name: string;
529
+ baseModel: string;
530
+ adapters: Partial<Record<AdapterPurpose, string>>;
531
+ }
532
+
384
533
  interface BroConfig {
385
534
  modelEndpoint?: string;
386
535
  modelName?: string;
@@ -389,6 +538,7 @@ interface BroConfig {
389
538
  enableBackground?: boolean;
390
539
  enableOllama?: boolean;
391
540
  enableBashgymIntegration?: boolean;
541
+ activeProfile?: string;
392
542
  }
393
543
  declare class BashBro extends EventEmitter {
394
544
  private profiler;
@@ -400,6 +550,9 @@ declare class BashBro extends EventEmitter {
400
550
  private config;
401
551
  private ollamaAvailable;
402
552
  private bashgymModelVersion;
553
+ private adapterRegistry;
554
+ private profileManager;
555
+ private activeProfile;
403
556
  constructor(config?: BroConfig);
404
557
  /**
405
558
  * Initialize bashgym integration for model hot-swap
@@ -414,6 +567,14 @@ declare class BashBro extends EventEmitter {
414
567
  scanProject(projectPath: string): void;
415
568
  route(command: string): RoutingResult;
416
569
  suggest(context: SuggestionContext): Suggestion[];
570
+ /**
571
+ * AI-enhanced async routing - uses pattern matching first, falls back to Ollama
572
+ */
573
+ routeAsync(command: string): Promise<RoutingResult>;
574
+ /**
575
+ * AI-enhanced async suggestions - pattern matching + Ollama suggestions with caching
576
+ */
577
+ suggestAsync(context: SuggestionContext): Promise<Suggestion[]>;
417
578
  /**
418
579
  * SECURITY FIX: Safe command execution with validation
419
580
  */
@@ -484,84 +645,27 @@ declare class BashBro extends EventEmitter {
484
645
  * Force refresh the bashgym model (check for updates)
485
646
  */
486
647
  refreshBashgymModel(): boolean;
487
- status(): string;
488
- }
489
-
490
- /**
491
- * Simple Ollama client for local model inference.
492
- * Keeps it minimal - just what we need for Bash Bro.
493
- */
494
- interface OllamaConfig {
495
- host: string;
496
- model: string;
497
- timeout: number;
498
- }
499
- interface ChatMessage {
500
- role: 'system' | 'user' | 'assistant';
501
- content: string;
502
- }
503
- declare class OllamaClient {
504
- private config;
505
- constructor(config?: Partial<OllamaConfig>);
506
- /**
507
- * Check if Ollama is running and accessible
508
- */
509
- isAvailable(): Promise<boolean>;
510
- /**
511
- * List available models
512
- */
513
- listModels(): Promise<string[]>;
514
- /**
515
- * Generate a response from the model
516
- */
517
- generate(prompt: string, systemPrompt?: string): Promise<string>;
518
648
  /**
519
- * Chat with the model (multi-turn conversation)
649
+ * Get model name for a specific purpose (checks active profile for adapter override)
520
650
  */
521
- chat(messages: ChatMessage[]): Promise<string>;
522
- /**
523
- * Ask Bash Bro to suggest a command
524
- */
525
- suggestCommand(context: string): Promise<string | null>;
651
+ private getModelForPurpose;
526
652
  /**
527
- * Ask Bash Bro to explain a command
653
+ * Get discovered LoRA adapters
528
654
  */
529
- explainCommand(command: string): Promise<string>;
655
+ getAdapters(): AdapterEntry[];
530
656
  /**
531
- * Ask Bash Bro to fix a command that failed
657
+ * Get available model profiles
532
658
  */
533
- fixCommand(command: string, error: string): Promise<string | null>;
534
- setModel(model: string): void;
535
- getModel(): string;
659
+ getProfiles(): ModelProfile[];
536
660
  /**
537
- * Generate a shell script from a natural language description
661
+ * Get the active model profile
538
662
  */
539
- generateScript(description: string, shell?: string): Promise<string | null>;
663
+ getActiveProfile(): ModelProfile | null;
540
664
  /**
541
- * Analyze command safety and provide recommendations
665
+ * Set the active model profile by name
542
666
  */
543
- analyzeCommandSafety(command: string): Promise<{
544
- safe: boolean;
545
- risk: 'low' | 'medium' | 'high' | 'critical';
546
- explanation: string;
547
- suggestions: string[];
548
- }>;
549
- /**
550
- * Summarize a series of commands and their outputs
551
- */
552
- summarizeSession(commands: {
553
- command: string;
554
- output?: string;
555
- error?: string;
556
- }[]): Promise<string>;
557
- /**
558
- * Get help for a specific tool or command
559
- */
560
- getHelp(topic: string): Promise<string>;
561
- /**
562
- * Convert natural language to a command
563
- */
564
- naturalToCommand(description: string): Promise<string | null>;
667
+ setActiveProfile(name: string): boolean;
668
+ status(): string;
565
669
  }
566
670
 
567
671
  /**