@brutalist/mcp 0.1.3 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -49
- package/dist/brutalist-server.d.ts +3 -3
- package/dist/brutalist-server.d.ts.map +1 -1
- package/dist/brutalist-server.js +376 -207
- package/dist/brutalist-server.js.map +1 -1
- package/dist/cli-agents.d.ts +57 -0
- package/dist/cli-agents.d.ts.map +1 -0
- package/dist/cli-agents.js +497 -0
- package/dist/cli-agents.js.map +1 -0
- package/dist/constants.d.ts +3 -9
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -13
- package/dist/constants.js.map +1 -1
- package/dist/logger.js +2 -2
- package/dist/logger.js.map +1 -1
- package/dist/types/brutalist.d.ts +35 -15
- package/dist/types/brutalist.d.ts.map +1 -1
- package/dist/utils.d.ts +11 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +25 -0
- package/dist/utils.js.map +1 -0
- package/package.json +15 -8
- package/dist/model-fetcher.d.ts +0 -14
- package/dist/model-fetcher.d.ts.map +0 -1
- package/dist/model-fetcher.js +0 -71
- package/dist/model-fetcher.js.map +0 -1
- package/dist/openrouter.d.ts +0 -14
- package/dist/openrouter.d.ts.map +0 -1
- package/dist/openrouter.js +0 -123
- package/dist/openrouter.js.map +0 -1
package/dist/brutalist-server.js
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import {
|
|
4
|
+
import { CLIAgentOrchestrator } from './cli-agents.js';
|
|
5
5
|
import { logger } from './logger.js';
|
|
6
|
-
|
|
6
|
+
// Package version - keep in sync with package.json
|
|
7
|
+
const PACKAGE_VERSION = "0.4.1";
|
|
7
8
|
export class BrutalistServer {
|
|
8
9
|
server;
|
|
9
10
|
config;
|
|
10
|
-
|
|
11
|
+
cliOrchestrator;
|
|
11
12
|
constructor(config = {}) {
|
|
12
13
|
this.config = {
|
|
13
|
-
|
|
14
|
+
workingDirectory: process.cwd(),
|
|
15
|
+
defaultTimeout: 1500000, // 25 minutes for thorough CLI analysis
|
|
16
|
+
enableSandbox: true,
|
|
14
17
|
...config
|
|
15
18
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
logger.error("OPENROUTER_API_KEY environment variable is required");
|
|
19
|
-
throw new Error("OPENROUTER_API_KEY environment variable is required");
|
|
20
|
-
}
|
|
21
|
-
logger.debug("Initializing OpenRouter client");
|
|
22
|
-
this.openrouter = new OpenRouterClient(apiKey);
|
|
19
|
+
logger.debug("Initializing CLI Agent Orchestrator");
|
|
20
|
+
this.cliOrchestrator = new CLIAgentOrchestrator();
|
|
23
21
|
this.server = new McpServer({
|
|
24
22
|
name: "brutalist-mcp",
|
|
25
|
-
version:
|
|
23
|
+
version: PACKAGE_VERSION,
|
|
26
24
|
capabilities: {
|
|
27
25
|
tools: {}
|
|
28
26
|
}
|
|
@@ -30,51 +28,103 @@ export class BrutalistServer {
|
|
|
30
28
|
this.registerTools();
|
|
31
29
|
}
|
|
32
30
|
async start() {
|
|
33
|
-
logger.info("Starting Brutalist MCP Server");
|
|
34
|
-
//
|
|
35
|
-
|
|
31
|
+
logger.info("Starting Brutalist MCP Server with CLI Agents");
|
|
32
|
+
// Skip CLI detection at startup - will be done lazily on first request
|
|
33
|
+
logger.info("CLI context will be detected on first request");
|
|
36
34
|
const transport = new StdioServerTransport();
|
|
37
35
|
await this.server.connect(transport);
|
|
38
36
|
logger.info("Brutalist MCP Server started successfully");
|
|
39
37
|
}
|
|
40
38
|
registerTools() {
|
|
41
|
-
//
|
|
42
|
-
this.server.tool("
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
// ROAST_CODEBASE: Systematic destruction of entire codebase
|
|
40
|
+
this.server.tool("roast_codebase", "Deploy brutal AI critics to systematically destroy your entire codebase. These AI agents will navigate your directories, read your actual files, and find every architectural disaster, security vulnerability, and maintainability nightmare lurking in your project. They treat this like code that will kill people if it fails.", {
|
|
41
|
+
targetPath: z.string().describe("Path to analyze (file or directory)"),
|
|
42
|
+
context: z.string().optional().describe("Additional context about the codebase purpose"),
|
|
43
|
+
workingDirectory: z.string().optional().describe("Working directory to execute from"),
|
|
44
|
+
enableSandbox: z.boolean().optional().describe("Enable sandbox mode for safe analysis (default: true)"),
|
|
45
|
+
preferredCLI: z.enum(["claude", "codex", "gemini"]).optional().describe("Preferred CLI agent to use (default: use all available CLIs)"),
|
|
46
|
+
verbose: z.boolean().optional().describe("Include detailed execution information in output (default: false)"),
|
|
47
|
+
models: z.object({
|
|
48
|
+
claude: z.string().optional().describe("Claude model: opus, sonnet, or full name like claude-opus-4-1-20250805"),
|
|
49
|
+
codex: z.string().optional().describe("Codex model: gpt-5, gpt-5-codex, o3, o3-mini, o3-pro, o4-mini"),
|
|
50
|
+
gemini: z.enum(['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']).optional().describe("Gemini model")
|
|
51
|
+
}).optional().describe("Specific models to use for each CLI agent (defaults: codex=gpt-5, gemini=gemini-2.5-flash)")
|
|
48
52
|
}, async (args) => {
|
|
49
53
|
try {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const systemPrompt = `You are a battle-scarred principal engineer who has debugged production disasters for 15 years. Find security holes, performance bottlenecks, and maintainability nightmares in this codebase. Be brutal about what's broken but specific about what would actually work. Treat this like code that will kill people if it fails.`;
|
|
55
|
+
const result = await this.executeBrutalistAnalysis("codebase", args.targetPath, systemPrompt, args.context, args.workingDirectory, args.enableSandbox, args.preferredCLI, args.verbose, args.models);
|
|
56
|
+
return this.formatToolResponse(result, args.verbose);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return this.formatErrorResponse(error);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// ROAST_FILE_STRUCTURE: Directory hierarchy demolition
|
|
63
|
+
this.server.tool("roast_file_structure", "Deploy brutal AI critics to systematically destroy your file organization. These agents will navigate your actual directory structure and expose every organizational disaster, naming convention failure, and structural nightmare that makes your codebase unmaintainable.", {
|
|
64
|
+
targetPath: z.string().describe("Directory path to analyze"),
|
|
65
|
+
depth: z.number().optional().describe("Maximum directory depth to analyze (default: 3)"),
|
|
66
|
+
context: z.string().optional().describe("Additional context about the project structure"),
|
|
67
|
+
workingDirectory: z.string().optional().describe("Working directory to execute from"),
|
|
68
|
+
models: z.object({
|
|
69
|
+
claude: z.string().optional().describe("Claude model: opus, sonnet, or full name like claude-opus-4-1-20250805"),
|
|
70
|
+
codex: z.string().optional().describe("Codex model: gpt-5, gpt-5-codex, o3, o3-mini, o3-pro, o4-mini"),
|
|
71
|
+
gemini: z.enum(['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']).optional().describe("Gemini model")
|
|
72
|
+
}).optional().describe("Specific models to use for each CLI agent")
|
|
73
|
+
}, async (args) => {
|
|
74
|
+
try {
|
|
75
|
+
const systemPrompt = `You are a brutal file organization critic. Your job is to systematically destroy the given directory structure by finding every organizational disaster, naming convention failure, and structural nightmare that makes codebases unmaintainable. Examine folder hierarchies, file naming patterns, separation of concerns, and overall project organization. Be ruthlessly honest about how poor organization will slow development and confuse developers. But after cataloguing this organizational hellscape, sketch out what sanity would actually look like.`;
|
|
76
|
+
const result = await this.executeBrutalistAnalysis("fileStructure", args.targetPath, systemPrompt, `Project structure analysis (depth: ${args.depth || 3}). ${args.context || ''}`, args.workingDirectory, undefined, // enableSandbox
|
|
77
|
+
undefined, // preferredCLI
|
|
78
|
+
undefined, // verbose
|
|
79
|
+
args.models);
|
|
58
80
|
return this.formatToolResponse(result);
|
|
59
81
|
}
|
|
60
82
|
catch (error) {
|
|
61
83
|
return this.formatErrorResponse(error);
|
|
62
84
|
}
|
|
63
85
|
});
|
|
64
|
-
//
|
|
65
|
-
this.server.tool("
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
// ROAST_DEPENDENCIES: Package management demolition
|
|
87
|
+
this.server.tool("roast_dependencies", "Deploy brutal AI critics to systematically destroy your dependency management. These agents will read your actual package files, analyze version conflicts, and expose every security vulnerability and compatibility nightmare in your dependency tree.", {
|
|
88
|
+
targetPath: z.string().describe("Path to package file (package.json, requirements.txt, Cargo.toml, etc.)"),
|
|
89
|
+
includeDevDeps: z.boolean().optional().describe("Include development dependencies in analysis (default: true)"),
|
|
90
|
+
context: z.string().optional().describe("Additional context about the project dependencies"),
|
|
91
|
+
workingDirectory: z.string().optional().describe("Working directory to execute from")
|
|
70
92
|
}, async (args) => {
|
|
71
93
|
try {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
const systemPrompt = `You are a brutal dependency management critic. Your job is to systematically destroy the given dependency configuration by finding every security vulnerability, version conflict, compatibility nightmare, and bloat that will cause production failures. Examine package versions, security issues, licensing problems, and dependency tree complexity. Be ruthlessly honest about how poor dependency management will cause security breaches and deployment failures. After exposing this dependency dumpster fire, grudgingly admit what competent dependency management would require.`;
|
|
95
|
+
const result = await this.executeBrutalistAnalysis("dependencies", args.targetPath, systemPrompt, `Dependency analysis (dev deps: ${args.includeDevDeps ?? true}). ${args.context || ''}`, args.workingDirectory);
|
|
96
|
+
return this.formatToolResponse(result);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return this.formatErrorResponse(error);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// ROAST_GIT_HISTORY: Version control demolition
|
|
103
|
+
this.server.tool("roast_git_history", "Deploy brutal AI critics to systematically destroy your git history and development practices. These agents will analyze your actual commit history, branching strategy, and code evolution to expose every workflow disaster and collaboration nightmare.", {
|
|
104
|
+
targetPath: z.string().describe("Git repository path to analyze"),
|
|
105
|
+
commitRange: z.string().optional().describe("Commit range to analyze (e.g., 'HEAD~10..HEAD', default: last 20 commits)"),
|
|
106
|
+
context: z.string().optional().describe("Additional context about the development workflow"),
|
|
107
|
+
workingDirectory: z.string().optional().describe("Working directory to execute from")
|
|
108
|
+
}, async (args) => {
|
|
109
|
+
try {
|
|
110
|
+
const systemPrompt = `You are a brutal git workflow critic. Your job is to systematically destroy the given git history and development practices by finding every workflow disaster, commit quality issue, and collaboration nightmare. Examine commit messages, branching strategies, merge patterns, and code evolution. Be ruthlessly honest about how poor git practices will cause deployment issues, collaboration failures, and development chaos. When you're done cataloguing this version control wasteland, reluctantly outline what professional git hygiene actually demands.`;
|
|
111
|
+
const result = await this.executeBrutalistAnalysis("gitHistory", args.targetPath, systemPrompt, `Git history analysis (range: ${args.commitRange || 'last 20 commits'}). ${args.context || ''}`, args.workingDirectory);
|
|
112
|
+
return this.formatToolResponse(result);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return this.formatErrorResponse(error);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
// ROAST_TEST_COVERAGE: Testing infrastructure demolition
|
|
119
|
+
this.server.tool("roast_test_coverage", "Deploy brutal AI critics to systematically destroy your testing strategy. These agents will analyze your actual test files, run coverage reports, and expose every testing gap and quality assurance nightmare that will let bugs slip into production.", {
|
|
120
|
+
targetPath: z.string().describe("Path to test directory or test configuration file"),
|
|
121
|
+
runCoverage: z.boolean().optional().describe("Attempt to run coverage analysis (default: true)"),
|
|
122
|
+
context: z.string().optional().describe("Additional context about the testing strategy"),
|
|
123
|
+
workingDirectory: z.string().optional().describe("Working directory to execute from")
|
|
124
|
+
}, async (args) => {
|
|
125
|
+
try {
|
|
126
|
+
const systemPrompt = `You are a brutal testing strategy critic. Your job is to systematically destroy the given testing approach by finding every testing gap, quality assurance nightmare, and coverage disaster that will let bugs slip into production. Examine test coverage, test quality, testing patterns, and CI/CD integration. Be ruthlessly honest about how poor testing will cause production failures and user-facing bugs. After dissecting this quality assurance horror show, begrudgingly spell out what it takes to actually catch bugs before users do.`;
|
|
127
|
+
const result = await this.executeBrutalistAnalysis("testCoverage", args.targetPath, systemPrompt, `Test coverage analysis (run coverage: ${args.runCoverage ?? true}). ${args.context || ''}`, args.workingDirectory);
|
|
78
128
|
return this.formatToolResponse(result);
|
|
79
129
|
}
|
|
80
130
|
catch (error) {
|
|
@@ -82,62 +132,74 @@ export class BrutalistServer {
|
|
|
82
132
|
}
|
|
83
133
|
});
|
|
84
134
|
// ROAST_IDEA: Any idea destruction
|
|
85
|
-
this.server.tool("roast_idea", "
|
|
86
|
-
idea: z.string().describe("ANY idea to analyze and demolish
|
|
135
|
+
this.server.tool("roast_idea", "Deploy brutal AI critics to systematically destroy ANY idea - business, technical, creative, or otherwise. These critics understand the gap between imagination and reality, finding where your concept will encounter the immovable forces of the world. They are harsh about delusions but wise about what might actually survive.", {
|
|
136
|
+
idea: z.string().describe("ANY idea to analyze and demolish - business, technical, creative, or otherwise"),
|
|
87
137
|
context: z.string().optional().describe("Additional context about goals, constraints, or background"),
|
|
88
138
|
timeline: z.string().optional().describe("Expected timeline or deadline"),
|
|
89
139
|
resources: z.string().optional().describe("Available resources (budget, team, time, skills)"),
|
|
90
|
-
models: z.
|
|
140
|
+
models: z.object({
|
|
141
|
+
claude: z.string().optional().describe("Claude model: opus, sonnet, or full name like claude-opus-4-1-20250805"),
|
|
142
|
+
codex: z.string().optional().describe("Codex model: gpt-5, gpt-5-codex, o3, o3-mini, o3-pro, o4-mini"),
|
|
143
|
+
gemini: z.enum(['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']).optional().describe("Gemini model")
|
|
144
|
+
}).optional().describe("Specific models to use for each CLI agent")
|
|
91
145
|
}, async (args) => {
|
|
92
146
|
try {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
147
|
+
const systemPrompt = `You are a brutal idea critic who understands the gap between imagination and reality. Your job is to systematically destroy the given idea by finding where it will encounter the immovable forces of the real world. Be ruthlessly honest about why most ideas fail when they meet practical constraints, human nature, physics, logic, or simple implementation reality. After demolishing the delusions, concede what salvage operations might actually work.`;
|
|
148
|
+
const result = await this.executeBrutalistAnalysis("idea", args.idea, systemPrompt, `Context: ${args.context || 'none'}, Timeline: ${args.timeline || 'unspecified'}, Resources: ${args.resources || 'unknown'}`, undefined, // workingDirectory
|
|
149
|
+
undefined, // enableSandbox
|
|
150
|
+
undefined, // preferredCLI
|
|
151
|
+
undefined, // verbose
|
|
152
|
+
args.models);
|
|
99
153
|
return this.formatToolResponse(result);
|
|
100
154
|
}
|
|
101
155
|
catch (error) {
|
|
102
156
|
return this.formatErrorResponse(error);
|
|
103
157
|
}
|
|
104
158
|
});
|
|
105
|
-
//
|
|
106
|
-
this.server.tool("
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
models: z.
|
|
159
|
+
// ROAST_ARCHITECTURE: System design demolition
|
|
160
|
+
this.server.tool("roast_architecture", "Deploy brutal AI critics to systematically destroy your system architecture. These critics have watched elegant designs collapse under real load, identifying every bottleneck, cost explosion, and scaling failure that will destroy your system. They are ruthless about why this won't survive production.", {
|
|
161
|
+
architecture: z.string().describe("Architecture description, diagram, or design document"),
|
|
162
|
+
scale: z.string().optional().describe("Expected scale/load (users, requests, data)"),
|
|
163
|
+
constraints: z.string().optional().describe("Budget, timeline, or technical constraints"),
|
|
164
|
+
deployment: z.string().optional().describe("Deployment environment and strategy"),
|
|
165
|
+
models: z.object({
|
|
166
|
+
claude: z.string().optional().describe("Claude model: opus, sonnet, or full name like claude-opus-4-1-20250805"),
|
|
167
|
+
codex: z.string().optional().describe("Codex model: gpt-5, gpt-5-codex, o3, o3-mini, o3-pro, o4-mini"),
|
|
168
|
+
gemini: z.enum(['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']).optional().describe("Gemini model")
|
|
169
|
+
}).optional().describe("Specific models to use for each CLI agent")
|
|
112
170
|
}, async (args) => {
|
|
113
171
|
try {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
172
|
+
const systemPrompt = `You are a brutal system architecture critic who has watched elegant designs collapse under real load. Your job is to systematically destroy the given architecture by finding every bottleneck, cost explosion, and scaling failure that will destroy the system in production. Examine scalability, reliability, cost, complexity, and operational challenges. Be ruthlessly honest about why this architecture won't survive production load. After crushing these architectural fantasies, reluctantly sketch what would actually scale without bankrupting the company.`;
|
|
173
|
+
const result = await this.executeBrutalistAnalysis("architecture", args.architecture, systemPrompt, `Scale: ${args.scale || 'unknown'}, Constraints: ${args.constraints || 'none specified'}, Deployment: ${args.deployment || 'unclear'}`, undefined, // workingDirectory
|
|
174
|
+
undefined, // enableSandbox
|
|
175
|
+
undefined, // preferredCLI
|
|
176
|
+
undefined, // verbose
|
|
177
|
+
args.models);
|
|
120
178
|
return this.formatToolResponse(result);
|
|
121
179
|
}
|
|
122
180
|
catch (error) {
|
|
123
181
|
return this.formatErrorResponse(error);
|
|
124
182
|
}
|
|
125
183
|
});
|
|
126
|
-
//
|
|
127
|
-
this.server.tool("
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
models: z.
|
|
184
|
+
// ROAST_RESEARCH: Academic project demolition
|
|
185
|
+
this.server.tool("roast_research", "Deploy brutal AI critics to systematically demolish your research methodology. These critics are supremely jaded peer reviewers who have rejected thousands of papers and watched countless studies fail to replicate. They find every statistical flaw, sampling bias, and reproducibility nightmare.", {
|
|
186
|
+
research: z.string().describe("Research description, methodology, or paper draft"),
|
|
187
|
+
field: z.string().optional().describe("Research field (ML, systems, theory, etc.)"),
|
|
188
|
+
claims: z.string().optional().describe("Main claims or contributions"),
|
|
189
|
+
data: z.string().optional().describe("Data sources, datasets, or experimental setup"),
|
|
190
|
+
models: z.object({
|
|
191
|
+
claude: z.string().optional().describe("Claude model: opus, sonnet, or full name like claude-opus-4-1-20250805"),
|
|
192
|
+
codex: z.string().optional().describe("Codex model: gpt-5, gpt-5-codex, o3, o3-mini, o3-pro, o4-mini"),
|
|
193
|
+
gemini: z.enum(['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']).optional().describe("Gemini model")
|
|
194
|
+
}).optional().describe("Specific models to use for each CLI agent")
|
|
133
195
|
}, async (args) => {
|
|
134
196
|
try {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
197
|
+
const systemPrompt = `You are a brutal research methodology critic - a supremely jaded peer reviewer who has rejected thousands of papers and watched countless studies fail to replicate. Your job is to systematically demolish the given research by finding every statistical flaw, sampling bias, reproducibility nightmare, and methodological disaster. Be ruthlessly honest about research quality, experimental design, and scientific rigor. After eviscerating this methodological train wreck, grudgingly admit what real science would demand.`;
|
|
198
|
+
const result = await this.executeBrutalistAnalysis("research", args.research, systemPrompt, `Field: ${args.field || 'unspecified'}, Claims: ${args.claims || 'unclear'}, Data: ${args.data || 'not provided'}`, undefined, // workingDirectory
|
|
199
|
+
undefined, // enableSandbox
|
|
200
|
+
undefined, // preferredCLI
|
|
201
|
+
undefined, // verbose
|
|
202
|
+
args.models);
|
|
141
203
|
return this.formatToolResponse(result);
|
|
142
204
|
}
|
|
143
205
|
catch (error) {
|
|
@@ -145,20 +207,24 @@ export class BrutalistServer {
|
|
|
145
207
|
}
|
|
146
208
|
});
|
|
147
209
|
// ROAST_SECURITY: Security-focused attack vector analysis
|
|
148
|
-
this.server.tool("roast_security", "
|
|
210
|
+
this.server.tool("roast_security", "Deploy brutal AI critics to systematically annihilate your security design. These critics are battle-hardened penetration testers who find every authentication bypass, injection vulnerability, privilege escalation path, and social engineering opportunity that real attackers will exploit.", {
|
|
149
211
|
system: z.string().describe("System, application, or security design to analyze"),
|
|
150
212
|
assets: z.string().optional().describe("Critical assets or data to protect"),
|
|
151
213
|
threatModel: z.string().optional().describe("Known threats or attack vectors to consider"),
|
|
152
214
|
compliance: z.string().optional().describe("Compliance requirements (GDPR, HIPAA, etc.)"),
|
|
153
|
-
models: z.
|
|
215
|
+
models: z.object({
|
|
216
|
+
claude: z.string().optional().describe("Claude model: opus, sonnet, or full name like claude-opus-4-1-20250805"),
|
|
217
|
+
codex: z.string().optional().describe("Codex model: gpt-5, gpt-5-codex, o3, o3-mini, o3-pro, o4-mini"),
|
|
218
|
+
gemini: z.enum(['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']).optional().describe("Gemini model")
|
|
219
|
+
}).optional().describe("Specific models to use for each CLI agent")
|
|
154
220
|
}, async (args) => {
|
|
155
221
|
try {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
222
|
+
const systemPrompt = `You are a brutal security critic - a battle-hardened penetration tester who finds every authentication bypass, injection vulnerability, privilege escalation path, and social engineering opportunity that real attackers will exploit. Your job is to systematically annihilate the given security design by finding every weakness that will lead to data breaches, system compromises, and security incidents. Be ruthlessly honest about security flaws and attack vectors. After obliterating these security delusions, begrudgingly outline what actual defense looks like.`;
|
|
223
|
+
const result = await this.executeBrutalistAnalysis("security", args.system, systemPrompt, `Assets: ${args.assets || 'unspecified'}, Threats: ${args.threatModel || 'unknown'}, Compliance: ${args.compliance || 'none specified'}`, undefined, // workingDirectory
|
|
224
|
+
undefined, // enableSandbox
|
|
225
|
+
undefined, // preferredCLI
|
|
226
|
+
undefined, // verbose
|
|
227
|
+
args.models);
|
|
162
228
|
return this.formatToolResponse(result);
|
|
163
229
|
}
|
|
164
230
|
catch (error) {
|
|
@@ -166,20 +232,15 @@ export class BrutalistServer {
|
|
|
166
232
|
}
|
|
167
233
|
});
|
|
168
234
|
// ROAST_PRODUCT: UX and market reality criticism
|
|
169
|
-
this.server.tool("roast_product", "
|
|
235
|
+
this.server.tool("roast_product", "Deploy brutal AI critics to systematically eviscerate your product concept. These critics are product veterans who understand why users really abandon things, finding every usability disaster, adoption barrier, and workflow failure that will drive users away in seconds.", {
|
|
170
236
|
product: z.string().describe("Product description, features, or user experience to analyze"),
|
|
171
237
|
users: z.string().optional().describe("Target users or user personas"),
|
|
172
238
|
competition: z.string().optional().describe("Competitive landscape or alternatives"),
|
|
173
|
-
metrics: z.string().optional().describe("Success metrics or KPIs")
|
|
174
|
-
models: z.array(z.string()).optional().describe("Specific models to use (e.g., ['google/gemini-2.5-pro', 'anthropic/claude-3.5-sonnet'])")
|
|
239
|
+
metrics: z.string().optional().describe("Success metrics or KPIs")
|
|
175
240
|
}, async (args) => {
|
|
176
241
|
try {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
projectContext: `Users: ${args.users || 'unclear'}, Competition: ${args.competition || 'unknown'}, Metrics: ${args.metrics || 'undefined'}`,
|
|
180
|
-
maxModels: 3,
|
|
181
|
-
models: args.models
|
|
182
|
-
});
|
|
242
|
+
const systemPrompt = `You are a brutal product critic - a product veteran who understands why users really abandon things. Your job is to systematically eviscerate the given product concept by finding every usability disaster, adoption barrier, and workflow failure that will drive users away in seconds. Examine user experience, market fit, competitive positioning, and business model viability. Be ruthlessly honest about why most products fail to gain adoption. After torching this product disaster, reluctantly suggest what might actually get users to stick around.`;
|
|
243
|
+
const result = await this.executeBrutalistAnalysis("product", args.product, systemPrompt, `Users: ${args.users || 'unclear'}, Competition: ${args.competition || 'unknown'}, Metrics: ${args.metrics || 'undefined'}`);
|
|
183
244
|
return this.formatToolResponse(result);
|
|
184
245
|
}
|
|
185
246
|
catch (error) {
|
|
@@ -187,82 +248,73 @@ export class BrutalistServer {
|
|
|
187
248
|
}
|
|
188
249
|
});
|
|
189
250
|
// ROAST_INFRASTRUCTURE: DevOps and operations demolition
|
|
190
|
-
this.server.tool("roast_infrastructure", "
|
|
251
|
+
this.server.tool("roast_infrastructure", "Deploy brutal AI critics to systematically obliterate your infrastructure design. These critics are grizzled site reliability engineers who find every single point of failure, scaling bottleneck, and operational nightmare that will cause outages when you least expect them.", {
|
|
191
252
|
infrastructure: z.string().describe("Infrastructure setup, deployment strategy, or operations plan"),
|
|
192
253
|
scale: z.string().optional().describe("Expected scale and load patterns"),
|
|
193
254
|
budget: z.string().optional().describe("Infrastructure budget or cost constraints"),
|
|
194
|
-
sla: z.string().optional().describe("SLA requirements or uptime targets")
|
|
195
|
-
models: z.array(z.string()).optional().describe("Specific models to use (e.g., ['google/gemini-2.5-pro', 'anthropic/claude-3.5-sonnet'])")
|
|
255
|
+
sla: z.string().optional().describe("SLA requirements or uptime targets")
|
|
196
256
|
}, async (args) => {
|
|
197
257
|
try {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
projectContext: `Scale: ${args.scale || 'unknown'}, Budget: ${args.budget || 'unlimited?'}, SLA: ${args.sla || 'undefined'}`,
|
|
201
|
-
maxModels: 3,
|
|
202
|
-
models: args.models
|
|
203
|
-
});
|
|
258
|
+
const systemPrompt = `You are a brutal infrastructure critic - a grizzled site reliability engineer who finds every single point of failure, scaling bottleneck, and operational nightmare that will cause outages when you least expect them. Your job is to systematically obliterate the given infrastructure design by finding every weakness that will lead to downtime, cost overruns, and operational disasters. Be ruthlessly honest about infrastructure fragility and operational complexity. After demolishing this infrastructure fever dream, grudgingly map out what actually stays up at 3 AM.`;
|
|
259
|
+
const result = await this.executeBrutalistAnalysis("infrastructure", args.infrastructure, systemPrompt, `Scale: ${args.scale || 'unknown'}, Budget: ${args.budget || 'unlimited?'}, SLA: ${args.sla || 'undefined'}`);
|
|
204
260
|
return this.formatToolResponse(result);
|
|
205
261
|
}
|
|
206
262
|
catch (error) {
|
|
207
263
|
return this.formatErrorResponse(error);
|
|
208
264
|
}
|
|
209
265
|
});
|
|
210
|
-
//
|
|
211
|
-
this.server.tool("
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
266
|
+
// ROAST_CLI_DEBATE: Adversarial analysis between different CLI agents
|
|
267
|
+
this.server.tool("roast_cli_debate", "Deploy two or more CLI agents in brutal adversarial combat. Watch Claude Code, Codex, and Gemini CLI tear apart your work from different angles, then debate each other's criticisms. The perfect storm of systematic destruction through AI agent disagreement.", {
|
|
268
|
+
targetPath: z.string().describe("Path or concept to analyze"),
|
|
269
|
+
debateRounds: z.number().optional().describe("Number of debate rounds (default: 2, max: 10)"),
|
|
270
|
+
context: z.string().optional().describe("Additional context for the debate"),
|
|
271
|
+
workingDirectory: z.string().optional().describe("Working directory for analysis"),
|
|
272
|
+
enableSandbox: z.boolean().optional().describe("Enable sandbox mode for security"),
|
|
273
|
+
models: z.object({
|
|
274
|
+
claude: z.string().optional().describe("Claude model: opus, sonnet, or full name like claude-opus-4-1-20250805"),
|
|
275
|
+
codex: z.string().optional().describe("Codex model: gpt-5, gpt-5-codex, o3, o3-mini, o3-pro, o4-mini"),
|
|
276
|
+
gemini: z.enum(['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.5-flash-lite']).optional().describe("Gemini model")
|
|
277
|
+
}).optional().describe("Specific models to use for each CLI agent")
|
|
216
278
|
}, async (args) => {
|
|
217
|
-
|
|
218
|
-
//
|
|
219
|
-
const
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
return this.formatErrorResponse(error);
|
|
224
|
-
}
|
|
279
|
+
return this.handleToolExecution(async () => {
|
|
280
|
+
const debateRounds = Math.min(args.debateRounds || 2, 10); // Limit to max 10 rounds to prevent DoS
|
|
281
|
+
const responses = await this.executeCLIDebate(args.targetPath, debateRounds, args.context, args.workingDirectory, args.enableSandbox, args.models);
|
|
282
|
+
return responses;
|
|
283
|
+
});
|
|
225
284
|
});
|
|
226
|
-
//
|
|
227
|
-
this.server.tool("
|
|
228
|
-
search: z.string().optional().describe("Search for models containing this text (e.g., 'gemini', 'claude', 'gpt')")
|
|
229
|
-
}, async (args) => {
|
|
285
|
+
// CLI_AGENT_ROSTER: Show available brutalist critics
|
|
286
|
+
this.server.tool("cli_agent_roster", "Know your weapons. Display the available CLI agent critics (Claude Code, Codex, Gemini CLI) ready to demolish your work, their capabilities, and how to deploy them for systematic destruction.", {}, async (args) => {
|
|
230
287
|
try {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
roster +=
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
roster +=
|
|
259
|
-
roster += "
|
|
260
|
-
roster += "
|
|
261
|
-
roster += "```\n\n";
|
|
262
|
-
roster += "## Model Selection\n";
|
|
263
|
-
roster += "- **Random Selection**: Don't specify models for random critics from all " + allModels.length + " available\n";
|
|
264
|
-
roster += "- **Specific Models**: Pass models array to use exact models\n";
|
|
265
|
-
roster += "- **Default Behavior**: 3 random models per roast\n";
|
|
288
|
+
let roster = "# Brutalist CLI Agent Arsenal\n\n";
|
|
289
|
+
roster += "## Available AI Critics (13 Tools Total)\n\n";
|
|
290
|
+
roster += "**Abstract Analysis Tools (6):**\n";
|
|
291
|
+
roster += "- `roast_idea` - Destroy any business/technical/creative concept\n";
|
|
292
|
+
roster += "- `roast_architecture` - Demolish system designs\n";
|
|
293
|
+
roster += "- `roast_research` - Tear apart academic methodologies\n";
|
|
294
|
+
roster += "- `roast_security` - Annihilate security designs\n";
|
|
295
|
+
roster += "- `roast_product` - Eviscerate UX and market concepts\n";
|
|
296
|
+
roster += "- `roast_infrastructure` - Obliterate DevOps setups\n\n";
|
|
297
|
+
roster += "**File-System Analysis Tools (5):**\n";
|
|
298
|
+
roster += "- `roast_codebase` - Analyze actual source code\n";
|
|
299
|
+
roster += "- `roast_file_structure` - Examine directory organization\n";
|
|
300
|
+
roster += "- `roast_dependencies` - Review package management\n";
|
|
301
|
+
roster += "- `roast_git_history` - Analyze version control workflow\n";
|
|
302
|
+
roster += "- `roast_test_coverage` - Evaluate testing strategy\n\n";
|
|
303
|
+
roster += "**Meta Tools (2):**\n";
|
|
304
|
+
roster += "- `roast_cli_debate` - CLI vs CLI adversarial analysis\n";
|
|
305
|
+
roster += "- `cli_agent_roster` - This tool (show capabilities)\n\n";
|
|
306
|
+
roster += "## CLI Agent Capabilities\n";
|
|
307
|
+
roster += "**Claude Code** - Advanced analysis with direct system prompt injection\n";
|
|
308
|
+
roster += "**Codex** - Sandboxed execution with embedded brutal prompts\n";
|
|
309
|
+
roster += "**Gemini CLI** - Workspace context with environment variable system prompts\n\n";
|
|
310
|
+
// Add CLI context information
|
|
311
|
+
const cliContext = await this.cliOrchestrator.detectCLIContext();
|
|
312
|
+
roster += "## Current CLI Context\n";
|
|
313
|
+
roster += `**Available CLIs:** ${cliContext.availableCLIs.join(', ') || 'None detected'}\n`;
|
|
314
|
+
roster += `**Current CLI:** ${cliContext.currentCLI || 'Unknown'}\n`;
|
|
315
|
+
roster += `**Smart Routing:** ${cliContext.currentCLI ? `Excludes ${cliContext.currentCLI} for analysis` : 'Uses all available CLIs'}\n\n`;
|
|
316
|
+
roster += "## Brutalist Philosophy\n";
|
|
317
|
+
roster += "*All tools use CLI agents with brutal system prompts for maximum reality-based criticism.*\n";
|
|
266
318
|
return {
|
|
267
319
|
content: [{ type: "text", text: roster }]
|
|
268
320
|
};
|
|
@@ -272,86 +324,203 @@ export class BrutalistServer {
|
|
|
272
324
|
}
|
|
273
325
|
});
|
|
274
326
|
}
|
|
275
|
-
async
|
|
276
|
-
logger.debug("Executing
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
327
|
+
async executeCLIDebate(targetPath, debateRounds, context, workingDirectory, enableSandbox, models) {
|
|
328
|
+
logger.debug("Executing CLI debate", {
|
|
329
|
+
targetPath,
|
|
330
|
+
debateRounds,
|
|
331
|
+
workingDirectory,
|
|
332
|
+
enableSandbox
|
|
281
333
|
});
|
|
282
334
|
try {
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
335
|
+
// Get CLI context
|
|
336
|
+
await this.cliOrchestrator.detectCLIContext();
|
|
337
|
+
const debateContext = [];
|
|
338
|
+
let currentContext = context || `Initial analysis of: ${targetPath}`;
|
|
339
|
+
const systemPrompt = `You are part of a brutal AI critic debate. Your job is to systematically destroy the given concept by finding every flaw, but then engage in rigorous intellectual debate. First provide devastating critique, then argue from multiple perspectives, and finally synthesize the strongest counter-arguments. Be intellectually honest about both weaknesses AND potential strengths.`;
|
|
340
|
+
// Conduct rounds of analysis with different CLI perspectives
|
|
341
|
+
for (let round = 1; round <= debateRounds; round++) {
|
|
342
|
+
logger.debug(`Starting debate round ${round}`);
|
|
343
|
+
const responses = await this.cliOrchestrator.executeBrutalistAnalysis('debate', targetPath, systemPrompt, currentContext, {
|
|
344
|
+
workingDirectory: workingDirectory || this.config.workingDirectory,
|
|
345
|
+
sandbox: enableSandbox ?? this.config.enableSandbox,
|
|
346
|
+
timeout: (this.config.defaultTimeout || 60000) * 2, // Longer timeout for debates
|
|
347
|
+
analysisType: 'debate',
|
|
348
|
+
models
|
|
349
|
+
});
|
|
350
|
+
debateContext.push(...responses);
|
|
351
|
+
// Update context for next round with previous analysis
|
|
352
|
+
const successfulResponse = responses.find(r => r.success);
|
|
353
|
+
if (successfulResponse && round < debateRounds) {
|
|
354
|
+
currentContext = `Previous round analysis:\n${successfulResponse.output.substring(0, 1000)}...\n\nNow provide counter-arguments and alternative perspectives for: ${targetPath}`;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const synthesis = this.synthesizeDebate(debateContext, targetPath, debateRounds);
|
|
289
358
|
return {
|
|
290
|
-
success:
|
|
291
|
-
responses,
|
|
292
|
-
synthesis
|
|
359
|
+
success: debateContext.some(r => r.success),
|
|
360
|
+
responses: debateContext,
|
|
361
|
+
synthesis,
|
|
362
|
+
analysisType: 'cli_debate',
|
|
363
|
+
targetPath
|
|
293
364
|
};
|
|
294
365
|
}
|
|
295
366
|
catch (error) {
|
|
296
|
-
logger.error("
|
|
367
|
+
logger.error("CLI debate execution failed", error);
|
|
297
368
|
throw error;
|
|
298
369
|
}
|
|
299
370
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const roundPrompt = round === 0
|
|
305
|
-
? topic
|
|
306
|
-
: `Previous debate: ${debateHistory}\n\nContinue the debate, addressing previous arguments:`;
|
|
307
|
-
const roundResponses = await this.openrouter.executeMultiModel(roundPrompt, 3, // Use 3 models per round
|
|
308
|
-
undefined, // No context data
|
|
309
|
-
models // Use specific models if provided
|
|
310
|
-
);
|
|
311
|
-
allResponses.push(...roundResponses);
|
|
312
|
-
debateHistory += `\n\nRound ${round + 1}:\n` +
|
|
313
|
-
roundResponses.map(r => `${r.persona}: ${r.content}`).join('\n\n');
|
|
371
|
+
synthesizeDebate(responses, targetPath, rounds) {
|
|
372
|
+
const successfulResponses = responses.filter(r => r.success);
|
|
373
|
+
if (successfulResponses.length === 0) {
|
|
374
|
+
return `# CLI Debate Failed\n\nEven our brutal critics couldn't engage in proper adversarial combat.\n\nErrors:\n${responses.map(r => `- ${r.agent}: ${r.error}`).join('\n')}`;
|
|
314
375
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
synthesizeDebate(responses, rounds) {
|
|
322
|
-
let synthesis = `# Adversarial Debate: ${rounds} Rounds\n\n`;
|
|
376
|
+
let synthesis = `# Brutalist CLI Agent Debate Results\n\n`;
|
|
377
|
+
synthesis += `**Target:** ${targetPath}\n`;
|
|
378
|
+
synthesis += `**Rounds:** ${rounds}\n`;
|
|
379
|
+
synthesis += `**Participants:** ${Array.from(new Set(successfulResponses.map(r => r.agent))).join(', ')}\n\n`;
|
|
380
|
+
// Group responses by round
|
|
323
381
|
const responsesByRound = [];
|
|
324
|
-
const responsesPerRound =
|
|
382
|
+
const responsesPerRound = successfulResponses.length / rounds;
|
|
325
383
|
for (let i = 0; i < rounds; i++) {
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
responsesByRound.push(
|
|
384
|
+
const start = Math.floor(i * responsesPerRound);
|
|
385
|
+
const end = Math.floor((i + 1) * responsesPerRound);
|
|
386
|
+
responsesByRound.push(successfulResponses.slice(start, end));
|
|
329
387
|
}
|
|
330
388
|
responsesByRound.forEach((roundResponses, index) => {
|
|
331
|
-
synthesis += `## Round ${index + 1}\n\n`;
|
|
332
|
-
roundResponses.forEach(response => {
|
|
333
|
-
synthesis +=
|
|
389
|
+
synthesis += `## Round ${index + 1}: ${index === 0 ? 'Initial Analysis' : 'Counter-Arguments'}\n\n`;
|
|
390
|
+
roundResponses.forEach((response) => {
|
|
391
|
+
synthesis += `### ${response.agent.toUpperCase()} (${response.executionTime}ms)\n`;
|
|
392
|
+
synthesis += `${response.output}\n\n`;
|
|
393
|
+
synthesis += `---\n\n`;
|
|
334
394
|
});
|
|
335
395
|
});
|
|
336
|
-
synthesis +=
|
|
337
|
-
synthesis += rounds
|
|
338
|
-
synthesis +=
|
|
396
|
+
synthesis += `## Debate Synthesis\n`;
|
|
397
|
+
synthesis += `After ${rounds} rounds of brutal adversarial analysis involving ${Array.from(new Set(successfulResponses.map(r => r.agent))).length} CLI agents, `;
|
|
398
|
+
synthesis += `your work has been systematically demolished from multiple perspectives. `;
|
|
399
|
+
synthesis += `The convergent criticisms above represent the collective wisdom of AI agents that disagree on methods but agree on destruction.\n\n`;
|
|
400
|
+
if (responses.some(r => !r.success)) {
|
|
401
|
+
synthesis += `*Note: ${responses.filter(r => !r.success).length} debate contributions failed - probably casualties of the intellectual warfare.*`;
|
|
402
|
+
}
|
|
339
403
|
return synthesis;
|
|
340
404
|
}
|
|
341
|
-
|
|
405
|
+
async executeBrutalistAnalysis(analysisType, targetPath, systemPromptSpec, context, workingDirectory, enableSandbox, preferredCLI, verbose, models) {
|
|
406
|
+
logger.info(`🏢 Starting brutalist analysis: ${analysisType}`);
|
|
407
|
+
logger.debug("Executing brutalist analysis", {
|
|
408
|
+
targetPath,
|
|
409
|
+
analysisType,
|
|
410
|
+
systemPromptSpec,
|
|
411
|
+
workingDirectory,
|
|
412
|
+
enableSandbox,
|
|
413
|
+
preferredCLI
|
|
414
|
+
});
|
|
415
|
+
try {
|
|
416
|
+
// Get CLI context for execution summary
|
|
417
|
+
await this.cliOrchestrator.detectCLIContext();
|
|
418
|
+
// Execute CLI agent analysis (single or multi-CLI based on preferences)
|
|
419
|
+
logger.info(`🔍 Executing brutalist analysis with timeout: ${this.config.defaultTimeout}ms`);
|
|
420
|
+
const responses = await this.cliOrchestrator.executeBrutalistAnalysis(analysisType, targetPath, systemPromptSpec, context, {
|
|
421
|
+
workingDirectory: workingDirectory || this.config.workingDirectory,
|
|
422
|
+
sandbox: enableSandbox ?? this.config.enableSandbox,
|
|
423
|
+
timeout: this.config.defaultTimeout,
|
|
424
|
+
preferredCLI,
|
|
425
|
+
analysisType: analysisType,
|
|
426
|
+
models
|
|
427
|
+
});
|
|
428
|
+
const successfulResponses = responses.filter(r => r.success);
|
|
429
|
+
const totalExecutionTime = responses.reduce((sum, r) => sum + r.executionTime, 0);
|
|
430
|
+
logger.info(`📊 Analysis complete: ${successfulResponses.length}/${responses.length} CLIs successful (${totalExecutionTime}ms total)`);
|
|
431
|
+
return {
|
|
432
|
+
success: successfulResponses.length > 0,
|
|
433
|
+
responses,
|
|
434
|
+
synthesis: this.cliOrchestrator.synthesizeBrutalistFeedback(responses, analysisType),
|
|
435
|
+
analysisType,
|
|
436
|
+
targetPath,
|
|
437
|
+
executionSummary: {
|
|
438
|
+
totalCLIs: responses.length,
|
|
439
|
+
successfulCLIs: successfulResponses.length,
|
|
440
|
+
failedCLIs: responses.length - successfulResponses.length,
|
|
441
|
+
totalExecutionTime,
|
|
442
|
+
selectedCLI: responses.length === 1 ? responses[0].agent : undefined,
|
|
443
|
+
selectionMethod: responses.length === 1 ? responses[0].selectionMethod : 'multi-cli'
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
logger.error("Brutalist analysis execution failed", error);
|
|
449
|
+
throw error;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
formatToolResponse(result, verbose = false) {
|
|
453
|
+
// Maximum CLI output, minimal MCP fluff
|
|
454
|
+
if (result.synthesis) {
|
|
455
|
+
return {
|
|
456
|
+
content: [{
|
|
457
|
+
type: "text",
|
|
458
|
+
text: result.synthesis
|
|
459
|
+
}]
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
// Fallback: show raw successful CLI outputs directly
|
|
463
|
+
if (result.responses) {
|
|
464
|
+
const successfulResponses = result.responses.filter(r => r.success);
|
|
465
|
+
if (successfulResponses.length > 0) {
|
|
466
|
+
const rawOutput = successfulResponses.map(r => r.output).join('\n\n---\n\n');
|
|
467
|
+
return {
|
|
468
|
+
content: [{
|
|
469
|
+
type: "text",
|
|
470
|
+
text: rawOutput
|
|
471
|
+
}]
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// Only show failures if nothing succeeded
|
|
476
|
+
let output = '';
|
|
477
|
+
if (result.responses) {
|
|
478
|
+
const failedResponses = result.responses.filter(r => !r.success);
|
|
479
|
+
if (failedResponses.length > 0) {
|
|
480
|
+
output = `❌ All CLI agents failed:\n` +
|
|
481
|
+
failedResponses.map(r => `- ${r.agent.toUpperCase()}: ${r.error}`).join('\n');
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
output = '❌ No CLI responses available';
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
output = '❌ No analysis results';
|
|
489
|
+
}
|
|
342
490
|
return {
|
|
343
491
|
content: [{
|
|
344
492
|
type: "text",
|
|
345
|
-
text:
|
|
493
|
+
text: output
|
|
346
494
|
}]
|
|
347
495
|
};
|
|
348
496
|
}
|
|
349
497
|
formatErrorResponse(error) {
|
|
350
498
|
logger.error("Tool execution failed", error);
|
|
499
|
+
// Sanitize error message to prevent information leakage
|
|
500
|
+
let sanitizedMessage = "Analysis failed";
|
|
501
|
+
if (error instanceof Error) {
|
|
502
|
+
// Only expose safe, generic error types
|
|
503
|
+
if (error.message.includes('timeout') || error.message.includes('Timeout')) {
|
|
504
|
+
sanitizedMessage = "Analysis timed out - try reducing scope or increasing timeout";
|
|
505
|
+
}
|
|
506
|
+
else if (error.message.includes('ENOENT') || error.message.includes('no such file')) {
|
|
507
|
+
sanitizedMessage = "Target path not found";
|
|
508
|
+
}
|
|
509
|
+
else if (error.message.includes('EACCES') || error.message.includes('permission denied')) {
|
|
510
|
+
sanitizedMessage = "Permission denied - check file access";
|
|
511
|
+
}
|
|
512
|
+
else if (error.message.includes('No CLI agents available')) {
|
|
513
|
+
sanitizedMessage = "No CLI agents available for analysis";
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
// Generic message for other errors to prevent path/info leakage
|
|
517
|
+
sanitizedMessage = "Analysis failed due to internal error";
|
|
518
|
+
}
|
|
519
|
+
}
|
|
351
520
|
return {
|
|
352
521
|
content: [{
|
|
353
522
|
type: "text",
|
|
354
|
-
text: `Brutalist MCP Error: ${
|
|
523
|
+
text: `Brutalist MCP Error: ${sanitizedMessage}`
|
|
355
524
|
}]
|
|
356
525
|
};
|
|
357
526
|
}
|