@weavelogic/knowledge-graph-agent 0.10.3 → 0.10.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/dist/cli/commands/cultivate.d.ts.map +1 -1
- package/dist/cli/commands/cultivate.js +42 -35
- package/dist/cli/commands/cultivate.js.map +1 -1
- package/dist/cultivation/deep-analyzer.d.ts +61 -24
- package/dist/cultivation/deep-analyzer.d.ts.map +1 -1
- package/dist/cultivation/deep-analyzer.js +255 -148
- package/dist/cultivation/deep-analyzer.js.map +1 -1
- package/dist/node_modules/@google/generative-ai/dist/index.js +1256 -0
- package/dist/node_modules/@google/generative-ai/dist/index.js.map +1 -0
- package/dist/node_modules/@typescript-eslint/project-service/dist/index.js +1 -1
- package/dist/node_modules/tinyglobby/dist/index.js +1 -1
- package/package.json +3 -2
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
import { execFileSync,
|
|
2
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
1
|
+
import { execFileSync, execSync } from "child_process";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from "fs";
|
|
3
3
|
import { resolve, join } from "path";
|
|
4
4
|
import { createLogger } from "../utils/logger.js";
|
|
5
5
|
const logger = createLogger("deep-analyzer");
|
|
6
|
-
const VALID_AGENT_TYPES = /* @__PURE__ */ new Set([
|
|
7
|
-
"researcher",
|
|
8
|
-
"architect",
|
|
9
|
-
"analyst",
|
|
10
|
-
"coder",
|
|
11
|
-
"tester",
|
|
12
|
-
"reviewer",
|
|
13
|
-
"documenter"
|
|
14
|
-
]);
|
|
15
6
|
class DeepAnalyzer {
|
|
16
7
|
projectRoot;
|
|
17
8
|
docsPath;
|
|
@@ -20,7 +11,8 @@ class DeepAnalyzer {
|
|
|
20
11
|
maxAgents;
|
|
21
12
|
agentMode;
|
|
22
13
|
agentTimeout;
|
|
23
|
-
|
|
14
|
+
forceApiKey;
|
|
15
|
+
preferredProvider;
|
|
24
16
|
constructor(options) {
|
|
25
17
|
this.projectRoot = resolve(options.projectRoot);
|
|
26
18
|
this.docsPath = options.docsPath || "docs";
|
|
@@ -28,51 +20,124 @@ class DeepAnalyzer {
|
|
|
28
20
|
this.verbose = options.verbose || false;
|
|
29
21
|
this.maxAgents = options.maxAgents || 5;
|
|
30
22
|
this.agentMode = options.agentMode || "adaptive";
|
|
31
|
-
this.agentTimeout = options.agentTimeout ||
|
|
23
|
+
this.agentTimeout = options.agentTimeout || 12e4;
|
|
24
|
+
this.forceApiKey = options.forceApiKey || false;
|
|
25
|
+
this.preferredProvider = options.preferredProvider || "anthropic";
|
|
32
26
|
}
|
|
33
27
|
/**
|
|
34
|
-
* Check if
|
|
35
|
-
* Checks for direct command first (globally installed), then falls back to npx
|
|
28
|
+
* Check if running inside a Claude Code session
|
|
36
29
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return command !== null;
|
|
30
|
+
isInsideClaudeCode() {
|
|
31
|
+
return process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE === "1";
|
|
40
32
|
}
|
|
41
33
|
/**
|
|
42
|
-
*
|
|
43
|
-
* Returns the command configuration or null if not available
|
|
34
|
+
* Check if Anthropic API key is available
|
|
44
35
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
hasAnthropicApiKey() {
|
|
37
|
+
return !!process.env.ANTHROPIC_API_KEY;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if Google AI / Gemini API key is available
|
|
41
|
+
*/
|
|
42
|
+
hasGeminiApiKey() {
|
|
43
|
+
return !!(process.env.GOOGLE_AI_API_KEY || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the Gemini API key from available env vars
|
|
47
|
+
*/
|
|
48
|
+
getGeminiApiKey() {
|
|
49
|
+
return process.env.GOOGLE_AI_API_KEY || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if Claude CLI is available
|
|
53
|
+
*/
|
|
54
|
+
isCliAvailable() {
|
|
49
55
|
try {
|
|
50
|
-
execFileSync("claude
|
|
56
|
+
execFileSync("claude", ["--version"], {
|
|
51
57
|
stdio: "pipe",
|
|
52
58
|
timeout: 5e3,
|
|
53
59
|
windowsHide: true
|
|
54
60
|
});
|
|
55
|
-
|
|
56
|
-
return this.claudeFlowCommand;
|
|
61
|
+
return true;
|
|
57
62
|
} catch {
|
|
63
|
+
return false;
|
|
58
64
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Determine the best execution mode
|
|
68
|
+
*/
|
|
69
|
+
detectExecutionMode() {
|
|
70
|
+
const insideClaudeCode = this.isInsideClaudeCode();
|
|
71
|
+
const hasAnthropicKey = this.hasAnthropicApiKey();
|
|
72
|
+
const hasGeminiKey = this.hasGeminiApiKey();
|
|
73
|
+
const cliAvailable = this.isCliAvailable();
|
|
74
|
+
if (this.forceApiKey) {
|
|
75
|
+
if (this.preferredProvider === "gemini" && hasGeminiKey) {
|
|
76
|
+
return { mode: "gemini", reason: "Using Gemini API (forced, preferred)" };
|
|
77
|
+
}
|
|
78
|
+
if (hasAnthropicKey) {
|
|
79
|
+
return { mode: "anthropic", reason: "Using Anthropic API (forced)" };
|
|
80
|
+
}
|
|
81
|
+
if (hasGeminiKey) {
|
|
82
|
+
return { mode: "gemini", reason: "Using Gemini API (forced, fallback)" };
|
|
83
|
+
}
|
|
84
|
+
return { mode: "unavailable", reason: "No API key found (required when forceApiKey=true). Set ANTHROPIC_API_KEY or GOOGLE_AI_API_KEY." };
|
|
69
85
|
}
|
|
86
|
+
if (insideClaudeCode) {
|
|
87
|
+
if (this.preferredProvider === "gemini" && hasGeminiKey) {
|
|
88
|
+
return { mode: "gemini", reason: "Using Gemini API (inside Claude Code, preferred)" };
|
|
89
|
+
}
|
|
90
|
+
if (hasAnthropicKey) {
|
|
91
|
+
return { mode: "anthropic", reason: "Using Anthropic API (inside Claude Code)" };
|
|
92
|
+
}
|
|
93
|
+
if (hasGeminiKey) {
|
|
94
|
+
return { mode: "gemini", reason: "Using Gemini API (inside Claude Code, fallback)" };
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
mode: "unavailable",
|
|
98
|
+
reason: "Cannot run deep analysis inside Claude Code session without an API key. Set ANTHROPIC_API_KEY or GOOGLE_AI_API_KEY, or run from a regular terminal."
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (cliAvailable) {
|
|
102
|
+
return { mode: "cli", reason: "Using Claude CLI" };
|
|
103
|
+
}
|
|
104
|
+
if (this.preferredProvider === "gemini" && hasGeminiKey) {
|
|
105
|
+
return { mode: "gemini", reason: "Using Gemini API (CLI not available, preferred)" };
|
|
106
|
+
}
|
|
107
|
+
if (hasAnthropicKey) {
|
|
108
|
+
return { mode: "anthropic", reason: "Using Anthropic API (CLI not available)" };
|
|
109
|
+
}
|
|
110
|
+
if (hasGeminiKey) {
|
|
111
|
+
return { mode: "gemini", reason: "Using Gemini API (CLI not available)" };
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
mode: "unavailable",
|
|
115
|
+
reason: "No execution method available. Install Claude Code (https://claude.ai/code), or set ANTHROPIC_API_KEY or GOOGLE_AI_API_KEY."
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Check if analysis is available
|
|
120
|
+
*/
|
|
121
|
+
async isAvailable() {
|
|
122
|
+
const mode = this.detectExecutionMode();
|
|
123
|
+
return mode.mode !== "unavailable";
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get availability status with reason
|
|
127
|
+
*/
|
|
128
|
+
async getAvailabilityStatus() {
|
|
129
|
+
const mode = this.detectExecutionMode();
|
|
130
|
+
return {
|
|
131
|
+
available: mode.mode !== "unavailable",
|
|
132
|
+
reason: mode.reason
|
|
133
|
+
};
|
|
70
134
|
}
|
|
71
135
|
/**
|
|
72
136
|
* Run deep analysis
|
|
73
137
|
*/
|
|
74
138
|
async analyze() {
|
|
75
139
|
const startTime = Date.now();
|
|
140
|
+
const executionMode = this.detectExecutionMode();
|
|
76
141
|
const result = {
|
|
77
142
|
success: false,
|
|
78
143
|
agentsSpawned: 0,
|
|
@@ -80,13 +145,16 @@ class DeepAnalyzer {
|
|
|
80
145
|
documentsCreated: 0,
|
|
81
146
|
results: [],
|
|
82
147
|
duration: 0,
|
|
83
|
-
errors: []
|
|
148
|
+
errors: [],
|
|
149
|
+
mode: executionMode.mode === "unavailable" ? "static" : executionMode.mode
|
|
84
150
|
};
|
|
85
|
-
if (
|
|
86
|
-
result.errors.push(
|
|
151
|
+
if (executionMode.mode === "unavailable") {
|
|
152
|
+
result.errors.push(executionMode.reason);
|
|
87
153
|
result.duration = Date.now() - startTime;
|
|
154
|
+
logger.error("Deep analysis unavailable", new Error(executionMode.reason));
|
|
88
155
|
return result;
|
|
89
156
|
}
|
|
157
|
+
logger.info(`Starting deep analysis`, { mode: executionMode.mode, reason: executionMode.reason });
|
|
90
158
|
if (!existsSync(this.outputDir)) {
|
|
91
159
|
mkdirSync(this.outputDir, { recursive: true });
|
|
92
160
|
}
|
|
@@ -116,18 +184,15 @@ class DeepAnalyzer {
|
|
|
116
184
|
outputFile: "testing-analysis.md"
|
|
117
185
|
}
|
|
118
186
|
];
|
|
119
|
-
logger.info("
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
result.results = await this.executeSequential(agents);
|
|
124
|
-
} else {
|
|
125
|
-
result.results = await this.executeAdaptive(agents);
|
|
187
|
+
logger.info("Executing analysis agents", { agents: agents.length, mode: "sequential" });
|
|
188
|
+
for (const agent of agents) {
|
|
189
|
+
const agentResult = await this.executeAgent(agent, executionMode.mode);
|
|
190
|
+
result.results.push(agentResult);
|
|
126
191
|
}
|
|
127
192
|
result.agentsSpawned = result.results.length;
|
|
128
193
|
result.insightsCount = result.results.reduce((sum, r) => sum + r.insights.length, 0);
|
|
129
194
|
result.documentsCreated = result.results.reduce((sum, r) => sum + r.documents.length, 0);
|
|
130
|
-
result.success = result.results.
|
|
195
|
+
result.success = result.results.some((r) => r.success);
|
|
131
196
|
result.duration = Date.now() - startTime;
|
|
132
197
|
for (const agentResult of result.results) {
|
|
133
198
|
if (agentResult.error) {
|
|
@@ -142,48 +207,10 @@ class DeepAnalyzer {
|
|
|
142
207
|
});
|
|
143
208
|
return result;
|
|
144
209
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Execute agents in parallel
|
|
147
|
-
*/
|
|
148
|
-
async executeParallel(agents) {
|
|
149
|
-
const promises = agents.map((agent) => this.executeAgent(agent));
|
|
150
|
-
return Promise.all(promises);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Execute agents sequentially
|
|
154
|
-
*/
|
|
155
|
-
async executeSequential(agents) {
|
|
156
|
-
const results = [];
|
|
157
|
-
for (const agent of agents) {
|
|
158
|
-
results.push(await this.executeAgent(agent));
|
|
159
|
-
}
|
|
160
|
-
return results;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Execute agents adaptively (start with 2, scale based on success)
|
|
164
|
-
*/
|
|
165
|
-
async executeAdaptive(agents) {
|
|
166
|
-
const results = [];
|
|
167
|
-
const firstBatch = agents.slice(0, 2);
|
|
168
|
-
const firstResults = await Promise.all(firstBatch.map((a) => this.executeAgent(a)));
|
|
169
|
-
results.push(...firstResults);
|
|
170
|
-
const successRate = firstResults.filter((r) => r.success).length / firstResults.length;
|
|
171
|
-
if (successRate >= 0.5 && agents.length > 2) {
|
|
172
|
-
const remaining = agents.slice(2);
|
|
173
|
-
const remainingResults = await Promise.all(remaining.map((a) => this.executeAgent(a)));
|
|
174
|
-
results.push(...remainingResults);
|
|
175
|
-
} else if (agents.length > 2) {
|
|
176
|
-
logger.warn("Low success rate, switching to sequential mode");
|
|
177
|
-
for (const agent of agents.slice(2)) {
|
|
178
|
-
results.push(await this.executeAgent(agent));
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return results;
|
|
182
|
-
}
|
|
183
210
|
/**
|
|
184
211
|
* Execute a single agent
|
|
185
212
|
*/
|
|
186
|
-
async executeAgent(agent) {
|
|
213
|
+
async executeAgent(agent, mode) {
|
|
187
214
|
const startTime = Date.now();
|
|
188
215
|
const outputPath = join(this.outputDir, agent.outputFile);
|
|
189
216
|
const result = {
|
|
@@ -195,11 +222,18 @@ class DeepAnalyzer {
|
|
|
195
222
|
duration: 0
|
|
196
223
|
};
|
|
197
224
|
try {
|
|
198
|
-
logger.info(`
|
|
225
|
+
logger.info(`Executing agent: ${agent.name}`, { type: agent.type, mode });
|
|
199
226
|
const prompt = this.buildPrompt(agent);
|
|
200
|
-
|
|
227
|
+
let output;
|
|
228
|
+
if (mode === "cli") {
|
|
229
|
+
output = await this.runWithCli(prompt);
|
|
230
|
+
} else if (mode === "anthropic") {
|
|
231
|
+
output = await this.runWithAnthropic(prompt);
|
|
232
|
+
} else {
|
|
233
|
+
output = await this.runWithGemini(prompt);
|
|
234
|
+
}
|
|
201
235
|
result.insights = this.extractInsights(output);
|
|
202
|
-
writeFileSync(outputPath, this.formatOutput(agent, output));
|
|
236
|
+
writeFileSync(outputPath, this.formatOutput(agent, output, mode));
|
|
203
237
|
result.documents.push({ path: outputPath, title: agent.name });
|
|
204
238
|
result.success = true;
|
|
205
239
|
if (this.verbose) {
|
|
@@ -213,76 +247,148 @@ class DeepAnalyzer {
|
|
|
213
247
|
return result;
|
|
214
248
|
}
|
|
215
249
|
/**
|
|
216
|
-
* Build prompt for
|
|
250
|
+
* Build context-aware prompt for analysis
|
|
217
251
|
*/
|
|
218
252
|
buildPrompt(agent) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
**OBJECTIVE**: ${agent.task}
|
|
253
|
+
const context = this.gatherProjectContext();
|
|
254
|
+
return `You are analyzing a codebase. Here is the project context:
|
|
222
255
|
|
|
223
|
-
|
|
224
|
-
\`\`\`bash
|
|
225
|
-
claude-flow hooks pre-task --description "${agent.task}"
|
|
226
|
-
\`\`\`
|
|
256
|
+
${context}
|
|
227
257
|
|
|
228
|
-
|
|
229
|
-
1. Analyze the codebase at ${this.projectRoot}
|
|
230
|
-
2. Identify key patterns and conventions
|
|
231
|
-
3. Document your findings with specific examples
|
|
232
|
-
4. Provide actionable recommendations
|
|
233
|
-
5. Generate comprehensive markdown documentation
|
|
258
|
+
Task: ${agent.task}
|
|
234
259
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
- Prioritize actionable insights
|
|
260
|
+
Provide your findings in markdown format with:
|
|
261
|
+
1. Key observations (prefix with "Observation:")
|
|
262
|
+
2. Specific recommendations (prefix with "Recommendation:")
|
|
263
|
+
3. Any potential issues found (prefix with "Finding:")
|
|
240
264
|
|
|
241
|
-
|
|
242
|
-
\`\`\`bash
|
|
243
|
-
claude-flow hooks post-task --task-id "${agent.type}-analysis"
|
|
244
|
-
\`\`\`
|
|
245
|
-
`;
|
|
265
|
+
Be specific and actionable in your analysis.`;
|
|
246
266
|
}
|
|
247
267
|
/**
|
|
248
|
-
*
|
|
268
|
+
* Gather project context for analysis
|
|
249
269
|
*/
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
270
|
+
gatherProjectContext() {
|
|
271
|
+
const lines = [];
|
|
272
|
+
const packageJsonPath = join(this.projectRoot, "package.json");
|
|
273
|
+
if (existsSync(packageJsonPath)) {
|
|
274
|
+
try {
|
|
275
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
276
|
+
lines.push(`Project: ${pkg.name || "Unknown"} v${pkg.version || "0.0.0"}`);
|
|
277
|
+
lines.push(`Description: ${pkg.description || "No description"}`);
|
|
278
|
+
if (pkg.dependencies) {
|
|
279
|
+
const deps = Object.keys(pkg.dependencies).slice(0, 10);
|
|
280
|
+
lines.push(`Key dependencies: ${deps.join(", ")}`);
|
|
281
|
+
}
|
|
282
|
+
} catch {
|
|
283
|
+
}
|
|
253
284
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
285
|
+
try {
|
|
286
|
+
const entries = readdirSync(this.projectRoot, { withFileTypes: true });
|
|
287
|
+
const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 10);
|
|
288
|
+
if (dirs.length > 0) {
|
|
289
|
+
lines.push(`Project structure: ${dirs.join(", ")}`);
|
|
290
|
+
}
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
const configFiles = [
|
|
294
|
+
"tsconfig.json",
|
|
295
|
+
"vite.config.ts",
|
|
296
|
+
"vitest.config.ts",
|
|
297
|
+
".eslintrc.js",
|
|
298
|
+
"Dockerfile"
|
|
299
|
+
];
|
|
300
|
+
const foundConfigs = configFiles.filter((f) => existsSync(join(this.projectRoot, f)));
|
|
301
|
+
if (foundConfigs.length > 0) {
|
|
302
|
+
lines.push(`Config files: ${foundConfigs.join(", ")}`);
|
|
258
303
|
}
|
|
259
|
-
return
|
|
260
|
-
|
|
261
|
-
|
|
304
|
+
return lines.join("\n");
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Run analysis using Claude CLI
|
|
308
|
+
*/
|
|
309
|
+
async runWithCli(prompt) {
|
|
310
|
+
const sanitizedPrompt = prompt.replace(/"/g, '\\"').replace(/[`$]/g, "");
|
|
311
|
+
try {
|
|
312
|
+
const result = execSync(`claude -p "${sanitizedPrompt}"`, {
|
|
262
313
|
cwd: this.projectRoot,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
let stdout = "";
|
|
268
|
-
let stderr = "";
|
|
269
|
-
proc.stdout?.on("data", (data) => {
|
|
270
|
-
stdout += data.toString();
|
|
314
|
+
encoding: "utf8",
|
|
315
|
+
timeout: this.agentTimeout,
|
|
316
|
+
maxBuffer: 10 * 1024 * 1024
|
|
317
|
+
// 10MB buffer
|
|
271
318
|
});
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
319
|
+
return result;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (error instanceof Error) {
|
|
322
|
+
const execError = error;
|
|
323
|
+
if (execError.killed) {
|
|
324
|
+
if (execError.stdout && execError.stdout.length > 100) {
|
|
325
|
+
return execError.stdout;
|
|
326
|
+
}
|
|
327
|
+
throw new Error(`Claude CLI timed out after ${this.agentTimeout / 1e3}s`);
|
|
280
328
|
}
|
|
329
|
+
throw new Error(execError.stderr || error.message);
|
|
330
|
+
}
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Run analysis using Anthropic API directly
|
|
336
|
+
*/
|
|
337
|
+
async runWithAnthropic(prompt) {
|
|
338
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
339
|
+
if (!apiKey) {
|
|
340
|
+
throw new Error("ANTHROPIC_API_KEY not set");
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
const { default: Anthropic } = await import("@anthropic-ai/sdk");
|
|
344
|
+
const client = new Anthropic({ apiKey });
|
|
345
|
+
const response = await client.messages.create({
|
|
346
|
+
model: "claude-sonnet-4-20250514",
|
|
347
|
+
max_tokens: 4096,
|
|
348
|
+
messages: [
|
|
349
|
+
{
|
|
350
|
+
role: "user",
|
|
351
|
+
content: prompt
|
|
352
|
+
}
|
|
353
|
+
]
|
|
281
354
|
});
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
355
|
+
const textBlock = response.content.find((block) => block.type === "text");
|
|
356
|
+
if (textBlock && textBlock.type === "text") {
|
|
357
|
+
return textBlock.text;
|
|
358
|
+
}
|
|
359
|
+
throw new Error("No text content in API response");
|
|
360
|
+
} catch (error) {
|
|
361
|
+
if (error instanceof Error) {
|
|
362
|
+
throw new Error(`Anthropic API call failed: ${error.message}`);
|
|
363
|
+
}
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Run analysis using Google Gemini API
|
|
369
|
+
*/
|
|
370
|
+
async runWithGemini(prompt) {
|
|
371
|
+
const apiKey = this.getGeminiApiKey();
|
|
372
|
+
if (!apiKey) {
|
|
373
|
+
throw new Error("GOOGLE_AI_API_KEY, GEMINI_API_KEY, or GOOGLE_API_KEY not set");
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const { GoogleGenerativeAI } = await import("../node_modules/@google/generative-ai/dist/index.js");
|
|
377
|
+
const genAI = new GoogleGenerativeAI(apiKey);
|
|
378
|
+
const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" });
|
|
379
|
+
const result = await model.generateContent(prompt);
|
|
380
|
+
const response = result.response;
|
|
381
|
+
const text = response.text();
|
|
382
|
+
if (!text) {
|
|
383
|
+
throw new Error("No text content in Gemini response");
|
|
384
|
+
}
|
|
385
|
+
return text;
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (error instanceof Error) {
|
|
388
|
+
throw new Error(`Gemini API call failed: ${error.message}`);
|
|
389
|
+
}
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
286
392
|
}
|
|
287
393
|
/**
|
|
288
394
|
* Extract insights from agent output
|
|
@@ -290,7 +396,7 @@ claude-flow hooks post-task --task-id "${agent.type}-analysis"
|
|
|
290
396
|
extractInsights(output) {
|
|
291
397
|
const insights = [];
|
|
292
398
|
const patterns = [
|
|
293
|
-
/[-*]
|
|
399
|
+
/[-*]?\s*(?:insight|finding|observation|recommendation):\s*(.+)/gi,
|
|
294
400
|
/##\s*(?:insight|finding|observation|recommendation):\s*(.+)/gi,
|
|
295
401
|
/(?:key\s+)?(?:insight|finding|observation|recommendation):\s*(.+)/gi
|
|
296
402
|
];
|
|
@@ -307,19 +413,20 @@ claude-flow hooks post-task --task-id "${agent.type}-analysis"
|
|
|
307
413
|
/**
|
|
308
414
|
* Format output for documentation
|
|
309
415
|
*/
|
|
310
|
-
formatOutput(agent, output) {
|
|
416
|
+
formatOutput(agent, output, mode) {
|
|
311
417
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
312
418
|
return `---
|
|
313
419
|
title: "${agent.name} Analysis"
|
|
314
420
|
type: analysis
|
|
315
421
|
generator: deep-analyzer
|
|
316
422
|
agent: ${agent.type}
|
|
423
|
+
provider: ${mode}
|
|
317
424
|
created: ${timestamp}
|
|
318
425
|
---
|
|
319
426
|
|
|
320
427
|
# ${agent.name} Analysis
|
|
321
428
|
|
|
322
|
-
> Generated by DeepAnalyzer using
|
|
429
|
+
> Generated by DeepAnalyzer using ${mode === "cli" ? "Claude CLI" : mode === "anthropic" ? "Anthropic API" : "Gemini API"}
|
|
323
430
|
|
|
324
431
|
## Overview
|
|
325
432
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-analyzer.js","sources":["../../src/cultivation/deep-analyzer.ts"],"sourcesContent":["/**\n * DeepAnalyzer - Claude-Flow Integration for Deep Codebase Analysis\n *\n * Uses claude-flow agents for comprehensive codebase analysis:\n * - Pattern detection and architectural insights\n * - Documentation gap analysis\n * - Standards compliance checking\n *\n * @module cultivation/deep-analyzer\n */\n\nimport { execFileSync, spawn } from 'child_process';\nimport { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { createLogger } from '../utils/index.js';\n\nconst logger = createLogger('deep-analyzer');\n\n/**\n * Valid agent types for security validation\n */\nconst VALID_AGENT_TYPES = new Set([\n 'researcher',\n 'architect',\n 'analyst',\n 'coder',\n 'tester',\n 'reviewer',\n 'documenter',\n]);\n\n/**\n * Deep analyzer options\n */\nexport interface DeepAnalyzerOptions {\n /** Project root directory */\n projectRoot: string;\n /** Documentation path (relative to project root) */\n docsPath?: string;\n /** Output directory for analysis results */\n outputDir?: string;\n /** Enable verbose logging */\n verbose?: boolean;\n /** Maximum agents to spawn */\n maxAgents?: number;\n /** Agent execution mode */\n agentMode?: 'sequential' | 'parallel' | 'adaptive';\n /** Timeout for each agent (ms) */\n agentTimeout?: number;\n}\n\n/**\n * Analysis result from an agent\n */\nexport interface AgentResult {\n name: string;\n type: string;\n success: boolean;\n insights: string[];\n documents: Array<{ path: string; title: string }>;\n duration: number;\n error?: string;\n}\n\n/**\n * Deep analysis result\n */\nexport interface DeepAnalysisResult {\n success: boolean;\n agentsSpawned: number;\n insightsCount: number;\n documentsCreated: number;\n results: AgentResult[];\n duration: number;\n errors: string[];\n}\n\n/**\n * Agent configuration\n */\ninterface AgentConfig {\n name: string;\n type: 'researcher' | 'analyst' | 'coder' | 'tester' | 'reviewer';\n task: string;\n outputFile: string;\n}\n\n/**\n * DeepAnalyzer - Uses claude-flow for deep codebase analysis\n *\n * @example\n * ```typescript\n * const analyzer = new DeepAnalyzer({\n * projectRoot: '/my/project',\n * docsPath: 'docs',\n * });\n *\n * const result = await analyzer.analyze();\n * console.log(`Generated ${result.insightsCount} insights`);\n * ```\n */\nexport class DeepAnalyzer {\n private projectRoot: string;\n private docsPath: string;\n private outputDir: string;\n private verbose: boolean;\n private maxAgents: number;\n private agentMode: 'sequential' | 'parallel' | 'adaptive';\n private agentTimeout: number;\n private claudeFlowCommand: { cmd: string; args: string[] } | null = null;\n\n constructor(options: DeepAnalyzerOptions) {\n this.projectRoot = resolve(options.projectRoot);\n this.docsPath = options.docsPath || 'docs';\n this.outputDir = options.outputDir || join(this.projectRoot, this.docsPath, 'analysis');\n this.verbose = options.verbose || false;\n this.maxAgents = options.maxAgents || 5;\n this.agentMode = options.agentMode || 'adaptive';\n // Default timeout of 60 seconds (configurable via agentTimeout option)\n this.agentTimeout = options.agentTimeout || 60000;\n }\n\n /**\n * Check if claude-flow is available and determine the best command to use\n * Checks for direct command first (globally installed), then falls back to npx\n */\n async isAvailable(): Promise<boolean> {\n const command = await this.detectClaudeFlowCommand();\n return command !== null;\n }\n\n /**\n * Detect which claude-flow command to use\n * Returns the command configuration or null if not available\n */\n private async detectClaudeFlowCommand(): Promise<{ cmd: string; args: string[] } | null> {\n // Return cached result if available\n if (this.claudeFlowCommand !== null) {\n return this.claudeFlowCommand;\n }\n\n // First try direct command (globally installed claude-flow)\n try {\n // SECURITY: execFileSync does not spawn a shell by default, preventing shell injection\n execFileSync('claude-flow', ['--version'], {\n stdio: 'pipe',\n timeout: 5000,\n windowsHide: true,\n });\n this.claudeFlowCommand = { cmd: 'claude-flow', args: [] };\n return this.claudeFlowCommand;\n } catch {\n // Direct command not available, try npx\n }\n\n // Fall back to npx\n try {\n execFileSync('npx', ['claude-flow', '--version'], {\n stdio: 'pipe',\n timeout: 30000,\n windowsHide: true,\n });\n this.claudeFlowCommand = { cmd: 'npx', args: ['claude-flow'] };\n return this.claudeFlowCommand;\n } catch {\n return null;\n }\n }\n\n /**\n * Run deep analysis\n */\n async analyze(): Promise<DeepAnalysisResult> {\n const startTime = Date.now();\n\n const result: DeepAnalysisResult = {\n success: false,\n agentsSpawned: 0,\n insightsCount: 0,\n documentsCreated: 0,\n results: [],\n duration: 0,\n errors: [],\n };\n\n // Check availability\n if (!(await this.isAvailable())) {\n result.errors.push('claude-flow is not available');\n result.duration = Date.now() - startTime;\n return result;\n }\n\n // Ensure output directory exists\n if (!existsSync(this.outputDir)) {\n mkdirSync(this.outputDir, { recursive: true });\n }\n\n // Define agents\n const agents: AgentConfig[] = [\n {\n name: 'Pattern Researcher',\n type: 'researcher',\n task: 'Analyze codebase architecture, patterns, and design decisions',\n outputFile: 'architecture-patterns.md',\n },\n {\n name: 'Code Analyst',\n type: 'analyst',\n task: 'Identify code quality issues, complexity hotspots, and improvement opportunities',\n outputFile: 'code-analysis.md',\n },\n {\n name: 'Implementation Reviewer',\n type: 'coder',\n task: 'Review implementation patterns, naming conventions, and code style',\n outputFile: 'implementation-review.md',\n },\n {\n name: 'Test Analyzer',\n type: 'tester',\n task: 'Analyze test coverage, testing patterns, and testing gaps',\n outputFile: 'testing-analysis.md',\n },\n ];\n\n logger.info('Starting deep analysis', { agents: agents.length, mode: this.agentMode });\n\n // Execute agents based on mode\n if (this.agentMode === 'parallel') {\n result.results = await this.executeParallel(agents);\n } else if (this.agentMode === 'sequential') {\n result.results = await this.executeSequential(agents);\n } else {\n // Adaptive: start with 2, scale based on success\n result.results = await this.executeAdaptive(agents);\n }\n\n // Calculate totals\n result.agentsSpawned = result.results.length;\n result.insightsCount = result.results.reduce((sum, r) => sum + r.insights.length, 0);\n result.documentsCreated = result.results.reduce((sum, r) => sum + r.documents.length, 0);\n result.success = result.results.every(r => r.success);\n result.duration = Date.now() - startTime;\n\n // Collect errors\n for (const agentResult of result.results) {\n if (agentResult.error) {\n result.errors.push(`${agentResult.name}: ${agentResult.error}`);\n }\n }\n\n logger.info('Deep analysis complete', {\n success: result.success,\n insights: result.insightsCount,\n documents: result.documentsCreated,\n duration: result.duration,\n });\n\n return result;\n }\n\n /**\n * Execute agents in parallel\n */\n private async executeParallel(agents: AgentConfig[]): Promise<AgentResult[]> {\n const promises = agents.map(agent => this.executeAgent(agent));\n return Promise.all(promises);\n }\n\n /**\n * Execute agents sequentially\n */\n private async executeSequential(agents: AgentConfig[]): Promise<AgentResult[]> {\n const results: AgentResult[] = [];\n for (const agent of agents) {\n results.push(await this.executeAgent(agent));\n }\n return results;\n }\n\n /**\n * Execute agents adaptively (start with 2, scale based on success)\n */\n private async executeAdaptive(agents: AgentConfig[]): Promise<AgentResult[]> {\n const results: AgentResult[] = [];\n\n // First batch: 2 agents\n const firstBatch = agents.slice(0, 2);\n const firstResults = await Promise.all(firstBatch.map(a => this.executeAgent(a)));\n results.push(...firstResults);\n\n // Check success rate\n const successRate = firstResults.filter(r => r.success).length / firstResults.length;\n\n if (successRate >= 0.5 && agents.length > 2) {\n // Continue with remaining agents in parallel\n const remaining = agents.slice(2);\n const remainingResults = await Promise.all(remaining.map(a => this.executeAgent(a)));\n results.push(...remainingResults);\n } else if (agents.length > 2) {\n // Fall back to sequential\n logger.warn('Low success rate, switching to sequential mode');\n for (const agent of agents.slice(2)) {\n results.push(await this.executeAgent(agent));\n }\n }\n\n return results;\n }\n\n /**\n * Execute a single agent\n */\n private async executeAgent(agent: AgentConfig): Promise<AgentResult> {\n const startTime = Date.now();\n const outputPath = join(this.outputDir, agent.outputFile);\n\n const result: AgentResult = {\n name: agent.name,\n type: agent.type,\n success: false,\n insights: [],\n documents: [],\n duration: 0,\n };\n\n try {\n logger.info(`Spawning agent: ${agent.name}`, { type: agent.type });\n\n const prompt = this.buildPrompt(agent);\n\n // Execute claude-flow agent\n const output = await this.runClaudeFlowAgent(agent.type, prompt);\n\n // Parse output for insights\n result.insights = this.extractInsights(output);\n\n // Write output to file\n writeFileSync(outputPath, this.formatOutput(agent, output));\n result.documents.push({ path: outputPath, title: agent.name });\n\n result.success = true;\n\n if (this.verbose) {\n logger.debug(`Agent completed: ${agent.name}`, { insights: result.insights.length });\n }\n } catch (error) {\n result.error = error instanceof Error ? error.message : String(error);\n logger.error(`Agent failed: ${agent.name}`, error instanceof Error ? error : new Error(String(error)));\n }\n\n result.duration = Date.now() - startTime;\n return result;\n }\n\n /**\n * Build prompt for agent\n */\n private buildPrompt(agent: AgentConfig): string {\n return `You are a ${agent.name} analyzing the project at ${this.projectRoot}.\n\n**OBJECTIVE**: ${agent.task}\n\n**COORDINATION PROTOCOL**:\n\\`\\`\\`bash\nclaude-flow hooks pre-task --description \"${agent.task}\"\n\\`\\`\\`\n\n**YOUR TASKS**:\n1. Analyze the codebase at ${this.projectRoot}\n2. Identify key patterns and conventions\n3. Document your findings with specific examples\n4. Provide actionable recommendations\n5. Generate comprehensive markdown documentation\n\n**OUTPUT REQUIREMENTS**:\n- Use clear markdown formatting\n- Include code examples from the project\n- Organize findings by category\n- Prioritize actionable insights\n\nAfter completing:\n\\`\\`\\`bash\nclaude-flow hooks post-task --task-id \"${agent.type}-analysis\"\n\\`\\`\\`\n`;\n }\n\n /**\n * Run claude-flow agent\n */\n private async runClaudeFlowAgent(type: string, prompt: string): Promise<string> {\n // Security: Validate agent type against allowlist to prevent command injection\n if (!VALID_AGENT_TYPES.has(type)) {\n throw new Error(`Invalid agent type: ${type}. Valid types: ${[...VALID_AGENT_TYPES].join(', ')}`);\n }\n\n // Security: Sanitize prompt to prevent injection via shell metacharacters\n const sanitizedPrompt = prompt.replace(/[`$\\\\]/g, '');\n\n // Get the detected command configuration\n const commandConfig = await this.detectClaudeFlowCommand();\n if (!commandConfig) {\n throw new Error('claude-flow is not available');\n }\n\n return new Promise((resolve, reject) => {\n // Build args based on detected command\n // claude-flow agent run <type> \"<prompt>\"\n const args = [...commandConfig.args, 'agent', 'run', type, sanitizedPrompt];\n\n const proc = spawn(commandConfig.cmd, args, {\n cwd: this.projectRoot,\n shell: false, // Security: Disable shell to prevent command injection\n timeout: this.agentTimeout,\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout?.on('data', (data) => {\n stdout += data.toString();\n });\n\n proc.stderr?.on('data', (data) => {\n stderr += data.toString();\n });\n\n proc.on('close', (code) => {\n if (code === 0) {\n resolve(stdout);\n } else {\n reject(new Error(stderr || `Agent exited with code ${code}`));\n }\n });\n\n proc.on('error', (error) => {\n reject(error);\n });\n });\n }\n\n /**\n * Extract insights from agent output\n */\n private extractInsights(output: string): string[] {\n const insights: string[] = [];\n\n // Look for patterns like \"- Insight:\" or \"## Finding:\"\n const patterns = [\n /[-*]\\s*(?:insight|finding|observation|recommendation):\\s*(.+)/gi,\n /##\\s*(?:insight|finding|observation|recommendation):\\s*(.+)/gi,\n /(?:key\\s+)?(?:insight|finding|observation|recommendation):\\s*(.+)/gi,\n ];\n\n for (const pattern of patterns) {\n const matches = output.matchAll(pattern);\n for (const match of matches) {\n if (match[1]) {\n insights.push(match[1].trim());\n }\n }\n }\n\n // Deduplicate\n return [...new Set(insights)];\n }\n\n /**\n * Format output for documentation\n */\n private formatOutput(agent: AgentConfig, output: string): string {\n const timestamp = new Date().toISOString();\n\n return `---\ntitle: \"${agent.name} Analysis\"\ntype: analysis\ngenerator: deep-analyzer\nagent: ${agent.type}\ncreated: ${timestamp}\n---\n\n# ${agent.name} Analysis\n\n> Generated by DeepAnalyzer using claude-flow\n\n## Overview\n\n${agent.task}\n\n## Analysis\n\n${output}\n\n---\n\n*Generated on ${new Date().toLocaleString()}*\n`;\n }\n}\n\n/**\n * Create a deep analyzer instance\n */\nexport function createDeepAnalyzer(options: DeepAnalyzerOptions): DeepAnalyzer {\n return new DeepAnalyzer(options);\n}\n\n/**\n * Run deep analysis on a project\n */\nexport async function analyzeDeep(\n projectRoot: string,\n docsPath?: string\n): Promise<DeepAnalysisResult> {\n const analyzer = new DeepAnalyzer({ projectRoot, docsPath });\n return analyzer.analyze();\n}\n"],"names":["resolve"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deep-analyzer.js","sources":["../../src/cultivation/deep-analyzer.ts"],"sourcesContent":["/**\n * DeepAnalyzer - Deep Codebase Analysis\n *\n * Provides comprehensive codebase analysis using:\n * - Claude CLI (`claude -p`) when running outside Claude Code\n * - Direct Anthropic API when ANTHROPIC_API_KEY is available\n * - Google Gemini API when GOOGLE_AI_API_KEY is available (fallback)\n * - Clear error messaging when no option is available\n *\n * @module cultivation/deep-analyzer\n */\n\nimport { execFileSync, execSync } from 'child_process';\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';\nimport { join, resolve, relative } from 'path';\nimport { createLogger } from '../utils/index.js';\n\nconst logger = createLogger('deep-analyzer');\n\n/**\n * Valid agent types for analysis\n */\nconst VALID_AGENT_TYPES = new Set([\n 'researcher',\n 'architect',\n 'analyst',\n 'coder',\n 'tester',\n 'reviewer',\n 'documenter',\n]);\n\n/**\n * Deep analyzer options\n */\nexport interface DeepAnalyzerOptions {\n /** Project root directory */\n projectRoot: string;\n /** Documentation path (relative to project root) */\n docsPath?: string;\n /** Output directory for analysis results */\n outputDir?: string;\n /** Enable verbose logging */\n verbose?: boolean;\n /** Maximum agents to spawn */\n maxAgents?: number;\n /** Agent execution mode */\n agentMode?: 'sequential' | 'parallel' | 'adaptive';\n /** Timeout for each agent (ms) */\n agentTimeout?: number;\n /** Force use of API key even if CLI is available */\n forceApiKey?: boolean;\n /** Preferred provider when multiple are available */\n preferredProvider?: 'anthropic' | 'gemini';\n}\n\n/**\n * Analysis result from an agent\n */\nexport interface AgentResult {\n name: string;\n type: string;\n success: boolean;\n insights: string[];\n documents: Array<{ path: string; title: string }>;\n duration: number;\n error?: string;\n}\n\n/**\n * Deep analysis result\n */\nexport interface DeepAnalysisResult {\n success: boolean;\n agentsSpawned: number;\n insightsCount: number;\n documentsCreated: number;\n results: AgentResult[];\n duration: number;\n errors: string[];\n mode: 'cli' | 'anthropic' | 'gemini' | 'static';\n}\n\n/**\n * Agent configuration\n */\ninterface AgentConfig {\n name: string;\n type: 'researcher' | 'analyst' | 'coder' | 'tester' | 'reviewer';\n task: string;\n outputFile: string;\n}\n\n/**\n * Execution mode detection result\n */\ninterface ExecutionMode {\n mode: 'cli' | 'anthropic' | 'gemini' | 'unavailable';\n reason: string;\n}\n\n/**\n * DeepAnalyzer - Deep codebase analysis with multiple execution modes\n *\n * Supports multiple providers:\n * - Claude CLI (uses OAuth session, no API key needed)\n * - Anthropic API (requires ANTHROPIC_API_KEY)\n * - Google Gemini API (requires GOOGLE_AI_API_KEY or GEMINI_API_KEY)\n *\n * @example\n * ```typescript\n * const analyzer = new DeepAnalyzer({\n * projectRoot: '/my/project',\n * docsPath: 'docs',\n * });\n *\n * const result = await analyzer.analyze();\n * console.log(`Generated ${result.insightsCount} insights`);\n * ```\n */\nexport class DeepAnalyzer {\n private projectRoot: string;\n private docsPath: string;\n private outputDir: string;\n private verbose: boolean;\n private maxAgents: number;\n private agentMode: 'sequential' | 'parallel' | 'adaptive';\n private agentTimeout: number;\n private forceApiKey: boolean;\n private preferredProvider: 'anthropic' | 'gemini';\n\n constructor(options: DeepAnalyzerOptions) {\n this.projectRoot = resolve(options.projectRoot);\n this.docsPath = options.docsPath || 'docs';\n this.outputDir = options.outputDir || join(this.projectRoot, this.docsPath, 'analysis');\n this.verbose = options.verbose || false;\n this.maxAgents = options.maxAgents || 5;\n this.agentMode = options.agentMode || 'adaptive';\n // Default timeout of 2 minutes (120 seconds)\n this.agentTimeout = options.agentTimeout || 120000;\n this.forceApiKey = options.forceApiKey || false;\n this.preferredProvider = options.preferredProvider || 'anthropic';\n }\n\n /**\n * Check if running inside a Claude Code session\n */\n private isInsideClaudeCode(): boolean {\n return process.env.CLAUDECODE === '1' || process.env.CLAUDE_CODE === '1';\n }\n\n /**\n * Check if Anthropic API key is available\n */\n private hasAnthropicApiKey(): boolean {\n return !!process.env.ANTHROPIC_API_KEY;\n }\n\n /**\n * Check if Google AI / Gemini API key is available\n */\n private hasGeminiApiKey(): boolean {\n return !!(process.env.GOOGLE_AI_API_KEY || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY);\n }\n\n /**\n * Get the Gemini API key from available env vars\n */\n private getGeminiApiKey(): string | undefined {\n return process.env.GOOGLE_AI_API_KEY || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;\n }\n\n /**\n * Check if Claude CLI is available\n */\n private isCliAvailable(): boolean {\n try {\n execFileSync('claude', ['--version'], {\n stdio: 'pipe',\n timeout: 5000,\n windowsHide: true,\n });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Determine the best execution mode\n */\n private detectExecutionMode(): ExecutionMode {\n const insideClaudeCode = this.isInsideClaudeCode();\n const hasAnthropicKey = this.hasAnthropicApiKey();\n const hasGeminiKey = this.hasGeminiApiKey();\n const cliAvailable = this.isCliAvailable();\n\n // If forced to use API key\n if (this.forceApiKey) {\n // Check preferred provider first\n if (this.preferredProvider === 'gemini' && hasGeminiKey) {\n return { mode: 'gemini', reason: 'Using Gemini API (forced, preferred)' };\n }\n if (hasAnthropicKey) {\n return { mode: 'anthropic', reason: 'Using Anthropic API (forced)' };\n }\n if (hasGeminiKey) {\n return { mode: 'gemini', reason: 'Using Gemini API (forced, fallback)' };\n }\n return { mode: 'unavailable', reason: 'No API key found (required when forceApiKey=true). Set ANTHROPIC_API_KEY or GOOGLE_AI_API_KEY.' };\n }\n\n // Inside Claude Code session - CLI doesn't work due to resource contention\n if (insideClaudeCode) {\n // Check preferred provider first\n if (this.preferredProvider === 'gemini' && hasGeminiKey) {\n return { mode: 'gemini', reason: 'Using Gemini API (inside Claude Code, preferred)' };\n }\n if (hasAnthropicKey) {\n return { mode: 'anthropic', reason: 'Using Anthropic API (inside Claude Code)' };\n }\n if (hasGeminiKey) {\n return { mode: 'gemini', reason: 'Using Gemini API (inside Claude Code, fallback)' };\n }\n return {\n mode: 'unavailable',\n reason: 'Cannot run deep analysis inside Claude Code session without an API key. ' +\n 'Set ANTHROPIC_API_KEY or GOOGLE_AI_API_KEY, or run from a regular terminal.',\n };\n }\n\n // Outside Claude Code - prefer CLI for OAuth session support\n if (cliAvailable) {\n return { mode: 'cli', reason: 'Using Claude CLI' };\n }\n\n // CLI not available, try API keys\n if (this.preferredProvider === 'gemini' && hasGeminiKey) {\n return { mode: 'gemini', reason: 'Using Gemini API (CLI not available, preferred)' };\n }\n if (hasAnthropicKey) {\n return { mode: 'anthropic', reason: 'Using Anthropic API (CLI not available)' };\n }\n if (hasGeminiKey) {\n return { mode: 'gemini', reason: 'Using Gemini API (CLI not available)' };\n }\n\n return {\n mode: 'unavailable',\n reason: 'No execution method available. Install Claude Code (https://claude.ai/code), ' +\n 'or set ANTHROPIC_API_KEY or GOOGLE_AI_API_KEY.',\n };\n }\n\n /**\n * Check if analysis is available\n */\n async isAvailable(): Promise<boolean> {\n const mode = this.detectExecutionMode();\n return mode.mode !== 'unavailable';\n }\n\n /**\n * Get availability status with reason\n */\n async getAvailabilityStatus(): Promise<{ available: boolean; reason: string }> {\n const mode = this.detectExecutionMode();\n return {\n available: mode.mode !== 'unavailable',\n reason: mode.reason,\n };\n }\n\n /**\n * Run deep analysis\n */\n async analyze(): Promise<DeepAnalysisResult> {\n const startTime = Date.now();\n const executionMode = this.detectExecutionMode();\n\n const result: DeepAnalysisResult = {\n success: false,\n agentsSpawned: 0,\n insightsCount: 0,\n documentsCreated: 0,\n results: [],\n duration: 0,\n errors: [],\n mode: executionMode.mode === 'unavailable' ? 'static' : executionMode.mode,\n };\n\n // Check availability\n if (executionMode.mode === 'unavailable') {\n result.errors.push(executionMode.reason);\n result.duration = Date.now() - startTime;\n logger.error('Deep analysis unavailable', new Error(executionMode.reason));\n return result;\n }\n\n logger.info(`Starting deep analysis`, { mode: executionMode.mode, reason: executionMode.reason });\n\n // Ensure output directory exists\n if (!existsSync(this.outputDir)) {\n mkdirSync(this.outputDir, { recursive: true });\n }\n\n // Define agents\n const agents: AgentConfig[] = [\n {\n name: 'Pattern Researcher',\n type: 'researcher',\n task: 'Analyze codebase architecture, patterns, and design decisions',\n outputFile: 'architecture-patterns.md',\n },\n {\n name: 'Code Analyst',\n type: 'analyst',\n task: 'Identify code quality issues, complexity hotspots, and improvement opportunities',\n outputFile: 'code-analysis.md',\n },\n {\n name: 'Implementation Reviewer',\n type: 'coder',\n task: 'Review implementation patterns, naming conventions, and code style',\n outputFile: 'implementation-review.md',\n },\n {\n name: 'Test Analyzer',\n type: 'tester',\n task: 'Analyze test coverage, testing patterns, and testing gaps',\n outputFile: 'testing-analysis.md',\n },\n ];\n\n logger.info('Executing analysis agents', { agents: agents.length, mode: 'sequential' });\n\n // Execute agents sequentially\n for (const agent of agents) {\n const agentResult = await this.executeAgent(agent, executionMode.mode as 'cli' | 'anthropic' | 'gemini');\n result.results.push(agentResult);\n }\n\n // Calculate totals\n result.agentsSpawned = result.results.length;\n result.insightsCount = result.results.reduce((sum, r) => sum + r.insights.length, 0);\n result.documentsCreated = result.results.reduce((sum, r) => sum + r.documents.length, 0);\n result.success = result.results.some(r => r.success); // Success if at least one agent succeeded\n result.duration = Date.now() - startTime;\n\n // Collect errors\n for (const agentResult of result.results) {\n if (agentResult.error) {\n result.errors.push(`${agentResult.name}: ${agentResult.error}`);\n }\n }\n\n logger.info('Deep analysis complete', {\n success: result.success,\n insights: result.insightsCount,\n documents: result.documentsCreated,\n duration: result.duration,\n });\n\n return result;\n }\n\n /**\n * Execute a single agent\n */\n private async executeAgent(agent: AgentConfig, mode: 'cli' | 'anthropic' | 'gemini'): Promise<AgentResult> {\n const startTime = Date.now();\n const outputPath = join(this.outputDir, agent.outputFile);\n\n const result: AgentResult = {\n name: agent.name,\n type: agent.type,\n success: false,\n insights: [],\n documents: [],\n duration: 0,\n };\n\n try {\n logger.info(`Executing agent: ${agent.name}`, { type: agent.type, mode });\n\n const prompt = this.buildPrompt(agent);\n let output: string;\n\n if (mode === 'cli') {\n output = await this.runWithCli(prompt);\n } else if (mode === 'anthropic') {\n output = await this.runWithAnthropic(prompt);\n } else {\n output = await this.runWithGemini(prompt);\n }\n\n // Parse output for insights\n result.insights = this.extractInsights(output);\n\n // Write output to file\n writeFileSync(outputPath, this.formatOutput(agent, output, mode));\n result.documents.push({ path: outputPath, title: agent.name });\n\n result.success = true;\n\n if (this.verbose) {\n logger.debug(`Agent completed: ${agent.name}`, { insights: result.insights.length });\n }\n } catch (error) {\n result.error = error instanceof Error ? error.message : String(error);\n logger.error(`Agent failed: ${agent.name}`, error instanceof Error ? error : new Error(String(error)));\n }\n\n result.duration = Date.now() - startTime;\n return result;\n }\n\n /**\n * Build context-aware prompt for analysis\n */\n private buildPrompt(agent: AgentConfig): string {\n // Gather project context\n const context = this.gatherProjectContext();\n\n return `You are analyzing a codebase. Here is the project context:\n\n${context}\n\nTask: ${agent.task}\n\nProvide your findings in markdown format with:\n1. Key observations (prefix with \"Observation:\")\n2. Specific recommendations (prefix with \"Recommendation:\")\n3. Any potential issues found (prefix with \"Finding:\")\n\nBe specific and actionable in your analysis.`;\n }\n\n /**\n * Gather project context for analysis\n */\n private gatherProjectContext(): string {\n const lines: string[] = [];\n\n // Check for package.json\n const packageJsonPath = join(this.projectRoot, 'package.json');\n if (existsSync(packageJsonPath)) {\n try {\n const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n lines.push(`Project: ${pkg.name || 'Unknown'} v${pkg.version || '0.0.0'}`);\n lines.push(`Description: ${pkg.description || 'No description'}`);\n\n if (pkg.dependencies) {\n const deps = Object.keys(pkg.dependencies).slice(0, 10);\n lines.push(`Key dependencies: ${deps.join(', ')}`);\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n // List top-level directories\n try {\n const entries = readdirSync(this.projectRoot, { withFileTypes: true });\n const dirs = entries\n .filter(e => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules')\n .map(e => e.name)\n .slice(0, 10);\n\n if (dirs.length > 0) {\n lines.push(`Project structure: ${dirs.join(', ')}`);\n }\n } catch {\n // Ignore errors\n }\n\n // Check for common config files\n const configFiles = [\n 'tsconfig.json',\n 'vite.config.ts',\n 'vitest.config.ts',\n '.eslintrc.js',\n 'Dockerfile',\n ];\n\n const foundConfigs = configFiles.filter(f => existsSync(join(this.projectRoot, f)));\n if (foundConfigs.length > 0) {\n lines.push(`Config files: ${foundConfigs.join(', ')}`);\n }\n\n return lines.join('\\n');\n }\n\n /**\n * Run analysis using Claude CLI\n */\n private async runWithCli(prompt: string): Promise<string> {\n // Sanitize prompt - escape double quotes and remove dangerous chars\n const sanitizedPrompt = prompt\n .replace(/\"/g, '\\\\\"')\n .replace(/[`$]/g, '');\n\n try {\n const result = execSync(`claude -p \"${sanitizedPrompt}\"`, {\n cwd: this.projectRoot,\n encoding: 'utf8',\n timeout: this.agentTimeout,\n maxBuffer: 10 * 1024 * 1024, // 10MB buffer\n });\n return result;\n } catch (error) {\n if (error instanceof Error) {\n const execError = error as { stderr?: string; stdout?: string; killed?: boolean };\n if (execError.killed) {\n // Timeout - return partial output if available\n if (execError.stdout && execError.stdout.length > 100) {\n return execError.stdout;\n }\n throw new Error(`Claude CLI timed out after ${this.agentTimeout / 1000}s`);\n }\n throw new Error(execError.stderr || error.message);\n }\n throw error;\n }\n }\n\n /**\n * Run analysis using Anthropic API directly\n */\n private async runWithAnthropic(prompt: string): Promise<string> {\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n throw new Error('ANTHROPIC_API_KEY not set');\n }\n\n try {\n // Dynamic import to avoid bundling issues\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n\n const client = new Anthropic({ apiKey });\n\n const response = await client.messages.create({\n model: 'claude-sonnet-4-20250514',\n max_tokens: 4096,\n messages: [\n {\n role: 'user',\n content: prompt,\n },\n ],\n });\n\n // Extract text from response\n const textBlock = response.content.find(block => block.type === 'text');\n if (textBlock && textBlock.type === 'text') {\n return textBlock.text;\n }\n\n throw new Error('No text content in API response');\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Anthropic API call failed: ${error.message}`);\n }\n throw error;\n }\n }\n\n /**\n * Run analysis using Google Gemini API\n */\n private async runWithGemini(prompt: string): Promise<string> {\n const apiKey = this.getGeminiApiKey();\n if (!apiKey) {\n throw new Error('GOOGLE_AI_API_KEY, GEMINI_API_KEY, or GOOGLE_API_KEY not set');\n }\n\n try {\n // Dynamic import to avoid bundling issues\n const { GoogleGenerativeAI } = await import('@google/generative-ai');\n\n const genAI = new GoogleGenerativeAI(apiKey);\n const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });\n\n const result = await model.generateContent(prompt);\n const response = result.response;\n const text = response.text();\n\n if (!text) {\n throw new Error('No text content in Gemini response');\n }\n\n return text;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Gemini API call failed: ${error.message}`);\n }\n throw error;\n }\n }\n\n /**\n * Extract insights from agent output\n */\n private extractInsights(output: string): string[] {\n const insights: string[] = [];\n\n // Look for patterns like \"- Insight:\", \"Observation:\", \"Finding:\", \"Recommendation:\"\n const patterns = [\n /[-*]?\\s*(?:insight|finding|observation|recommendation):\\s*(.+)/gi,\n /##\\s*(?:insight|finding|observation|recommendation):\\s*(.+)/gi,\n /(?:key\\s+)?(?:insight|finding|observation|recommendation):\\s*(.+)/gi,\n ];\n\n for (const pattern of patterns) {\n const matches = output.matchAll(pattern);\n for (const match of matches) {\n if (match[1]) {\n insights.push(match[1].trim());\n }\n }\n }\n\n // Deduplicate\n return [...new Set(insights)];\n }\n\n /**\n * Format output for documentation\n */\n private formatOutput(agent: AgentConfig, output: string, mode: string): string {\n const timestamp = new Date().toISOString();\n\n return `---\ntitle: \"${agent.name} Analysis\"\ntype: analysis\ngenerator: deep-analyzer\nagent: ${agent.type}\nprovider: ${mode}\ncreated: ${timestamp}\n---\n\n# ${agent.name} Analysis\n\n> Generated by DeepAnalyzer using ${mode === 'cli' ? 'Claude CLI' : mode === 'anthropic' ? 'Anthropic API' : 'Gemini API'}\n\n## Overview\n\n${agent.task}\n\n## Analysis\n\n${output}\n\n---\n\n*Generated on ${new Date().toLocaleString()}*\n`;\n }\n}\n\n/**\n * Create a deep analyzer instance\n */\nexport function createDeepAnalyzer(options: DeepAnalyzerOptions): DeepAnalyzer {\n return new DeepAnalyzer(options);\n}\n\n/**\n * Run deep analysis on a project\n */\nexport async function analyzeDeep(\n projectRoot: string,\n docsPath?: string\n): Promise<DeepAnalysisResult> {\n const analyzer = new DeepAnalyzer({ projectRoot, docsPath });\n return analyzer.analyze();\n}\n"],"names":[],"mappings":";;;;AAiBA,MAAM,SAAS,aAAa,eAAe;AAuGpC,MAAM,aAAa;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAA8B;AACxC,SAAK,cAAc,QAAQ,QAAQ,WAAW;AAC9C,SAAK,WAAW,QAAQ,YAAY;AACpC,SAAK,YAAY,QAAQ,aAAa,KAAK,KAAK,aAAa,KAAK,UAAU,UAAU;AACtF,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,YAAY,QAAQ,aAAa;AAEtC,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,oBAAoB,QAAQ,qBAAqB;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA8B;AACpC,WAAO,QAAQ,IAAI,eAAe,OAAO,QAAQ,IAAI,gBAAgB;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA8B;AACpC,WAAO,CAAC,CAAC,QAAQ,IAAI;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAA2B;AACjC,WAAO,CAAC,EAAE,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,kBAAkB,QAAQ,IAAI;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAsC;AAC5C,WAAO,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,kBAAkB,QAAQ,IAAI;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAA0B;AAChC,QAAI;AACF,mBAAa,UAAU,CAAC,WAAW,GAAG;AAAA,QACpC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,MAAA,CACd;AACD,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAqC;AAC3C,UAAM,mBAAmB,KAAK,mBAAA;AAC9B,UAAM,kBAAkB,KAAK,mBAAA;AAC7B,UAAM,eAAe,KAAK,gBAAA;AAC1B,UAAM,eAAe,KAAK,eAAA;AAG1B,QAAI,KAAK,aAAa;AAEpB,UAAI,KAAK,sBAAsB,YAAY,cAAc;AACvD,eAAO,EAAE,MAAM,UAAU,QAAQ,uCAAA;AAAA,MACnC;AACA,UAAI,iBAAiB;AACnB,eAAO,EAAE,MAAM,aAAa,QAAQ,+BAAA;AAAA,MACtC;AACA,UAAI,cAAc;AAChB,eAAO,EAAE,MAAM,UAAU,QAAQ,sCAAA;AAAA,MACnC;AACA,aAAO,EAAE,MAAM,eAAe,QAAQ,iGAAA;AAAA,IACxC;AAGA,QAAI,kBAAkB;AAEpB,UAAI,KAAK,sBAAsB,YAAY,cAAc;AACvD,eAAO,EAAE,MAAM,UAAU,QAAQ,mDAAA;AAAA,MACnC;AACA,UAAI,iBAAiB;AACnB,eAAO,EAAE,MAAM,aAAa,QAAQ,2CAAA;AAAA,MACtC;AACA,UAAI,cAAc;AAChB,eAAO,EAAE,MAAM,UAAU,QAAQ,kDAAA;AAAA,MACnC;AACA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MAAA;AAAA,IAGZ;AAGA,QAAI,cAAc;AAChB,aAAO,EAAE,MAAM,OAAO,QAAQ,mBAAA;AAAA,IAChC;AAGA,QAAI,KAAK,sBAAsB,YAAY,cAAc;AACvD,aAAO,EAAE,MAAM,UAAU,QAAQ,kDAAA;AAAA,IACnC;AACA,QAAI,iBAAiB;AACnB,aAAO,EAAE,MAAM,aAAa,QAAQ,0CAAA;AAAA,IACtC;AACA,QAAI,cAAc;AAChB,aAAO,EAAE,MAAM,UAAU,QAAQ,uCAAA;AAAA,IACnC;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,IAAA;AAAA,EAGZ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAgC;AACpC,UAAM,OAAO,KAAK,oBAAA;AAClB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAyE;AAC7E,UAAM,OAAO,KAAK,oBAAA;AAClB,WAAO;AAAA,MACL,WAAW,KAAK,SAAS;AAAA,MACzB,QAAQ,KAAK;AAAA,IAAA;AAAA,EAEjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAuC;AAC3C,UAAM,YAAY,KAAK,IAAA;AACvB,UAAM,gBAAgB,KAAK,oBAAA;AAE3B,UAAM,SAA6B;AAAA,MACjC,SAAS;AAAA,MACT,eAAe;AAAA,MACf,eAAe;AAAA,MACf,kBAAkB;AAAA,MAClB,SAAS,CAAA;AAAA,MACT,UAAU;AAAA,MACV,QAAQ,CAAA;AAAA,MACR,MAAM,cAAc,SAAS,gBAAgB,WAAW,cAAc;AAAA,IAAA;AAIxE,QAAI,cAAc,SAAS,eAAe;AACxC,aAAO,OAAO,KAAK,cAAc,MAAM;AACvC,aAAO,WAAW,KAAK,IAAA,IAAQ;AAC/B,aAAO,MAAM,6BAA6B,IAAI,MAAM,cAAc,MAAM,CAAC;AACzE,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,0BAA0B,EAAE,MAAM,cAAc,MAAM,QAAQ,cAAc,OAAA,CAAQ;AAGhG,QAAI,CAAC,WAAW,KAAK,SAAS,GAAG;AAC/B,gBAAU,KAAK,WAAW,EAAE,WAAW,MAAM;AAAA,IAC/C;AAGA,UAAM,SAAwB;AAAA,MAC5B;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,MAAA;AAAA,MAEd;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,MAAA;AAAA,MAEd;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,MAAA;AAAA,MAEd;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,MAAA;AAAA,IACd;AAGF,WAAO,KAAK,6BAA6B,EAAE,QAAQ,OAAO,QAAQ,MAAM,cAAc;AAGtF,eAAW,SAAS,QAAQ;AAC1B,YAAM,cAAc,MAAM,KAAK,aAAa,OAAO,cAAc,IAAsC;AACvG,aAAO,QAAQ,KAAK,WAAW;AAAA,IACjC;AAGA,WAAO,gBAAgB,OAAO,QAAQ;AACtC,WAAO,gBAAgB,OAAO,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,SAAS,QAAQ,CAAC;AACnF,WAAO,mBAAmB,OAAO,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,UAAU,QAAQ,CAAC;AACvF,WAAO,UAAU,OAAO,QAAQ,KAAK,CAAA,MAAK,EAAE,OAAO;AACnD,WAAO,WAAW,KAAK,IAAA,IAAQ;AAG/B,eAAW,eAAe,OAAO,SAAS;AACxC,UAAI,YAAY,OAAO;AACrB,eAAO,OAAO,KAAK,GAAG,YAAY,IAAI,KAAK,YAAY,KAAK,EAAE;AAAA,MAChE;AAAA,IACF;AAEA,WAAO,KAAK,0BAA0B;AAAA,MACpC,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,UAAU,OAAO;AAAA,IAAA,CAClB;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aAAa,OAAoB,MAA4D;AACzG,UAAM,YAAY,KAAK,IAAA;AACvB,UAAM,aAAa,KAAK,KAAK,WAAW,MAAM,UAAU;AAExD,UAAM,SAAsB;AAAA,MAC1B,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,SAAS;AAAA,MACT,UAAU,CAAA;AAAA,MACV,WAAW,CAAA;AAAA,MACX,UAAU;AAAA,IAAA;AAGZ,QAAI;AACF,aAAO,KAAK,oBAAoB,MAAM,IAAI,IAAI,EAAE,MAAM,MAAM,MAAM,KAAA,CAAM;AAExE,YAAM,SAAS,KAAK,YAAY,KAAK;AACrC,UAAI;AAEJ,UAAI,SAAS,OAAO;AAClB,iBAAS,MAAM,KAAK,WAAW,MAAM;AAAA,MACvC,WAAW,SAAS,aAAa;AAC/B,iBAAS,MAAM,KAAK,iBAAiB,MAAM;AAAA,MAC7C,OAAO;AACL,iBAAS,MAAM,KAAK,cAAc,MAAM;AAAA,MAC1C;AAGA,aAAO,WAAW,KAAK,gBAAgB,MAAM;AAG7C,oBAAc,YAAY,KAAK,aAAa,OAAO,QAAQ,IAAI,CAAC;AAChE,aAAO,UAAU,KAAK,EAAE,MAAM,YAAY,OAAO,MAAM,MAAM;AAE7D,aAAO,UAAU;AAEjB,UAAI,KAAK,SAAS;AAChB,eAAO,MAAM,oBAAoB,MAAM,IAAI,IAAI,EAAE,UAAU,OAAO,SAAS,OAAA,CAAQ;AAAA,MACrF;AAAA,IACF,SAAS,OAAO;AACd,aAAO,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,aAAO,MAAM,iBAAiB,MAAM,IAAI,IAAI,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACvG;AAEA,WAAO,WAAW,KAAK,IAAA,IAAQ;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,OAA4B;AAE9C,UAAM,UAAU,KAAK,qBAAA;AAErB,WAAO;AAAA;AAAA,EAET,OAAO;AAAA;AAAA,QAED,MAAM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhB;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAA+B;AACrC,UAAM,QAAkB,CAAA;AAGxB,UAAM,kBAAkB,KAAK,KAAK,aAAa,cAAc;AAC7D,QAAI,WAAW,eAAe,GAAG;AAC/B,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,aAAa,iBAAiB,OAAO,CAAC;AAC7D,cAAM,KAAK,YAAY,IAAI,QAAQ,SAAS,KAAK,IAAI,WAAW,OAAO,EAAE;AACzE,cAAM,KAAK,gBAAgB,IAAI,eAAe,gBAAgB,EAAE;AAEhE,YAAI,IAAI,cAAc;AACpB,gBAAM,OAAO,OAAO,KAAK,IAAI,YAAY,EAAE,MAAM,GAAG,EAAE;AACtD,gBAAM,KAAK,qBAAqB,KAAK,KAAK,IAAI,CAAC,EAAE;AAAA,QACnD;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,QAAI;AACF,YAAM,UAAU,YAAY,KAAK,aAAa,EAAE,eAAe,MAAM;AACrE,YAAM,OAAO,QACV,OAAO,CAAA,MAAK,EAAE,iBAAiB,CAAC,EAAE,KAAK,WAAW,GAAG,KAAK,EAAE,SAAS,cAAc,EACnF,IAAI,CAAA,MAAK,EAAE,IAAI,EACf,MAAM,GAAG,EAAE;AAEd,UAAI,KAAK,SAAS,GAAG;AACnB,cAAM,KAAK,sBAAsB,KAAK,KAAK,IAAI,CAAC,EAAE;AAAA,MACpD;AAAA,IACF,QAAQ;AAAA,IAER;AAGA,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,UAAM,eAAe,YAAY,OAAO,CAAA,MAAK,WAAW,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC;AAClF,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,KAAK,iBAAiB,aAAa,KAAK,IAAI,CAAC,EAAE;AAAA,IACvD;AAEA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WAAW,QAAiC;AAExD,UAAM,kBAAkB,OACrB,QAAQ,MAAM,KAAK,EACnB,QAAQ,SAAS,EAAE;AAEtB,QAAI;AACF,YAAM,SAAS,SAAS,cAAc,eAAe,KAAK;AAAA,QACxD,KAAK,KAAK;AAAA,QACV,UAAU;AAAA,QACV,SAAS,KAAK;AAAA,QACd,WAAW,KAAK,OAAO;AAAA;AAAA,MAAA,CACxB;AACD,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,cAAM,YAAY;AAClB,YAAI,UAAU,QAAQ;AAEpB,cAAI,UAAU,UAAU,UAAU,OAAO,SAAS,KAAK;AACrD,mBAAO,UAAU;AAAA,UACnB;AACA,gBAAM,IAAI,MAAM,8BAA8B,KAAK,eAAe,GAAI,GAAG;AAAA,QAC3E;AACA,cAAM,IAAI,MAAM,UAAU,UAAU,MAAM,OAAO;AAAA,MACnD;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAiB,QAAiC;AAC9D,UAAM,SAAS,QAAQ,IAAI;AAC3B,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,QAAI;AAEF,YAAM,EAAE,SAAS,cAAc,MAAM,OAAO,mBAAmB;AAE/D,YAAM,SAAS,IAAI,UAAU,EAAE,QAAQ;AAEvC,YAAM,WAAW,MAAM,OAAO,SAAS,OAAO;AAAA,QAC5C,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UAAA;AAAA,QACX;AAAA,MACF,CACD;AAGD,YAAM,YAAY,SAAS,QAAQ,KAAK,CAAA,UAAS,MAAM,SAAS,MAAM;AACtE,UAAI,aAAa,UAAU,SAAS,QAAQ;AAC1C,eAAO,UAAU;AAAA,MACnB;AAEA,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,cAAM,IAAI,MAAM,8BAA8B,MAAM,OAAO,EAAE;AAAA,MAC/D;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAc,QAAiC;AAC3D,UAAM,SAAS,KAAK,gBAAA;AACpB,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAEA,QAAI;AAEF,YAAM,EAAE,mBAAA,IAAuB,MAAM,OAAO,qDAAuB;AAEnE,YAAM,QAAQ,IAAI,mBAAmB,MAAM;AAC3C,YAAM,QAAQ,MAAM,mBAAmB,EAAE,OAAO,oBAAoB;AAEpE,YAAM,SAAS,MAAM,MAAM,gBAAgB,MAAM;AACjD,YAAM,WAAW,OAAO;AACxB,YAAM,OAAO,SAAS,KAAA;AAEtB,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACtD;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,cAAM,IAAI,MAAM,2BAA2B,MAAM,OAAO,EAAE;AAAA,MAC5D;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,QAA0B;AAChD,UAAM,WAAqB,CAAA;AAG3B,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,eAAW,WAAW,UAAU;AAC9B,YAAM,UAAU,OAAO,SAAS,OAAO;AACvC,iBAAW,SAAS,SAAS;AAC3B,YAAI,MAAM,CAAC,GAAG;AACZ,mBAAS,KAAK,MAAM,CAAC,EAAE,MAAM;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAGA,WAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,OAAoB,QAAgB,MAAsB;AAC7E,UAAM,aAAY,oBAAI,KAAA,GAAO,YAAA;AAE7B,WAAO;AAAA,UACD,MAAM,IAAI;AAAA;AAAA;AAAA,SAGX,MAAM,IAAI;AAAA,YACP,IAAI;AAAA,WACL,SAAS;AAAA;AAAA;AAAA,IAGhB,MAAM,IAAI;AAAA;AAAA,oCAEsB,SAAS,QAAQ,eAAe,SAAS,cAAc,kBAAkB,YAAY;AAAA;AAAA;AAAA;AAAA,EAIvH,MAAM,IAAI;AAAA;AAAA;AAAA;AAAA,EAIV,MAAM;AAAA;AAAA;AAAA;AAAA,iBAIQ,oBAAI,QAAO,gBAAgB;AAAA;AAAA,EAEzC;AACF;AAKO,SAAS,mBAAmB,SAA4C;AAC7E,SAAO,IAAI,aAAa,OAAO;AACjC;AAKA,eAAsB,YACpB,aACA,UAC6B;AAC7B,QAAM,WAAW,IAAI,aAAa,EAAE,aAAa,UAAU;AAC3D,SAAO,SAAS,QAAA;AAClB;"}
|