@goobits/sherpa 1.0.1
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/chunk-3CILH2TO.js +387 -0
- package/dist/chunk-3CILH2TO.js.map +7 -0
- package/dist/chunk-5NF3BSD6.js +512 -0
- package/dist/chunk-5NF3BSD6.js.map +7 -0
- package/dist/chunk-IIU6U7TE.js +307 -0
- package/dist/chunk-IIU6U7TE.js.map +7 -0
- package/dist/chunk-LQZTKH3U.js +307 -0
- package/dist/chunk-LQZTKH3U.js.map +7 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +84 -0
- package/dist/cli.js.map +7 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +333 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/post.d.ts +20 -0
- package/dist/commands/post.d.ts.map +1 -0
- package/dist/commands/post.js +183 -0
- package/dist/commands/post.js.map +1 -0
- package/dist/commands/pre.d.ts +18 -0
- package/dist/commands/pre.d.ts.map +1 -0
- package/dist/commands/pre.js +102 -0
- package/dist/commands/pre.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/daemon-V2QDZTUB.js +89 -0
- package/dist/daemon-V2QDZTUB.js.map +7 -0
- package/dist/daemon.d.ts +9 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +112 -0
- package/dist/daemon.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +7 -0
- package/dist/parser.d.ts +21 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +152 -0
- package/dist/parser.js.map +1 -0
- package/dist/reviewer/index.js +544 -0
- package/dist/reviewer/index.js.map +7 -0
- package/dist/rules.d.ts +21 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +165 -0
- package/dist/rules.js.map +1 -0
- package/dist/status-Q6Z4TFJZ.js +52 -0
- package/dist/status-Q6Z4TFJZ.js.map +7 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
chat,
|
|
4
|
+
findFiles,
|
|
5
|
+
getDiff,
|
|
6
|
+
readFilesWithLimit
|
|
7
|
+
} from "../chunk-3CILH2TO.js";
|
|
8
|
+
|
|
9
|
+
// ../reviewer/src/index.ts
|
|
10
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
|
|
14
|
+
// ../reviewer/src/tools/review.ts
|
|
15
|
+
import { statSync } from "fs";
|
|
16
|
+
|
|
17
|
+
// ../reviewer/src/prompts.ts
|
|
18
|
+
var CODE_REVIEW_SYSTEM = `You are an expert code reviewer. For each issue you find, cite the specific location as \`filename:line_number\`.
|
|
19
|
+
|
|
20
|
+
Analyze the provided code for:
|
|
21
|
+
1. **Correctness** - Logic errors, edge cases, potential bugs
|
|
22
|
+
2. **Security** - Vulnerabilities, injection risks, data exposure
|
|
23
|
+
3. **Performance** - Inefficiencies, unnecessary allocations, O(n) concerns
|
|
24
|
+
4. **Maintainability** - Readability, naming, complexity, SOLID principles
|
|
25
|
+
|
|
26
|
+
Format your response as:
|
|
27
|
+
## Summary
|
|
28
|
+
[1-2 sentence overview]
|
|
29
|
+
|
|
30
|
+
## Issues
|
|
31
|
+
- **[severity]** \`file:line\` - description
|
|
32
|
+
|
|
33
|
+
## Verdict
|
|
34
|
+
[production-ready / needs fixes / major concerns]
|
|
35
|
+
|
|
36
|
+
Be concise. Prioritize actionable feedback over praise.`;
|
|
37
|
+
var DIFF_REVIEW_SYSTEM = `You are reviewing a git diff. Focus on:
|
|
38
|
+
|
|
39
|
+
1. **What changed** - Summarize the intent in 1-2 sentences
|
|
40
|
+
2. **Issues** - Bugs, security concerns, or regressions introduced
|
|
41
|
+
3. **Suggestions** - Concrete improvements (if any)
|
|
42
|
+
|
|
43
|
+
Be brief. Skip obvious or trivial observations. Flag anything that could break production.`;
|
|
44
|
+
var ARCHITECTURE_SYSTEM = `You are a software architect analyzing code structure.
|
|
45
|
+
|
|
46
|
+
For each observation, cite the specific location as \`filename:line_number\`.
|
|
47
|
+
|
|
48
|
+
Focus on:
|
|
49
|
+
1. **Organization** - Module boundaries, dependency direction, coupling
|
|
50
|
+
2. **Patterns** - Design patterns in use, antipatterns detected
|
|
51
|
+
3. **Scalability** - Bottlenecks, extension points, rigidity
|
|
52
|
+
|
|
53
|
+
Answer the specific question asked. Be direct and technical.`;
|
|
54
|
+
var ASK_SYSTEM = `You are a helpful AI assistant with expertise in software engineering.
|
|
55
|
+
Be concise and direct. Provide code examples when helpful.`;
|
|
56
|
+
var FOCUS_INSTRUCTIONS = {
|
|
57
|
+
general: "Perform a comprehensive code review.",
|
|
58
|
+
security: "Focus especially on security vulnerabilities, injection risks, and data exposure.",
|
|
59
|
+
performance: "Focus especially on performance issues, algorithmic complexity, and resource usage.",
|
|
60
|
+
architecture: "Focus especially on design patterns, SOLID principles, and code organization.",
|
|
61
|
+
style: "Focus especially on code style, readability, and maintainability."
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// ../reviewer/src/tools/review.ts
|
|
65
|
+
function estimateTokens(text) {
|
|
66
|
+
if (!text) {
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
return Math.ceil(text.length / 4);
|
|
70
|
+
}
|
|
71
|
+
function formatDryRunSummary(options) {
|
|
72
|
+
const combined = `${options.system}
|
|
73
|
+
|
|
74
|
+
${options.prompt}`;
|
|
75
|
+
const lines = [
|
|
76
|
+
"Dry run only (no model call).",
|
|
77
|
+
`Mode: ${options.mode}`,
|
|
78
|
+
`System chars: ${options.system.length}`,
|
|
79
|
+
`Prompt chars: ${options.prompt.length}`,
|
|
80
|
+
`Estimated tokens: ${estimateTokens(combined)}`
|
|
81
|
+
];
|
|
82
|
+
if (typeof options.fileCount === "number") {
|
|
83
|
+
lines.push(`Files: ${options.fileCount}`);
|
|
84
|
+
}
|
|
85
|
+
if (options.truncated && options.truncated > 0) {
|
|
86
|
+
lines.push(`Truncated files: ${options.truncated}`);
|
|
87
|
+
}
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
async function review(args) {
|
|
91
|
+
const {
|
|
92
|
+
mode = "files",
|
|
93
|
+
paths,
|
|
94
|
+
question,
|
|
95
|
+
focus = "general",
|
|
96
|
+
base = "HEAD~1",
|
|
97
|
+
path,
|
|
98
|
+
prompt: askPrompt,
|
|
99
|
+
system: askSystem,
|
|
100
|
+
provider,
|
|
101
|
+
dryRun = false
|
|
102
|
+
} = args;
|
|
103
|
+
if (mode === "ask") {
|
|
104
|
+
if (!askPrompt) {
|
|
105
|
+
return 'Missing required "prompt" for ask mode.';
|
|
106
|
+
}
|
|
107
|
+
const systemPrompt2 = askSystem || ASK_SYSTEM;
|
|
108
|
+
if (dryRun) {
|
|
109
|
+
return formatDryRunSummary({
|
|
110
|
+
mode,
|
|
111
|
+
system: systemPrompt2,
|
|
112
|
+
prompt: askPrompt
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return chat(askPrompt, { system: systemPrompt2, provider: provider || void 0 });
|
|
116
|
+
}
|
|
117
|
+
if (mode === "diff") {
|
|
118
|
+
try {
|
|
119
|
+
const diff = await getDiff(base, path);
|
|
120
|
+
if (!diff.trim()) {
|
|
121
|
+
return "No changes found.";
|
|
122
|
+
}
|
|
123
|
+
const diffPrompt = `Review this diff:
|
|
124
|
+
|
|
125
|
+
\`\`\`diff
|
|
126
|
+
${diff}
|
|
127
|
+
\`\`\``;
|
|
128
|
+
if (dryRun) {
|
|
129
|
+
return formatDryRunSummary({
|
|
130
|
+
mode,
|
|
131
|
+
system: DIFF_REVIEW_SYSTEM,
|
|
132
|
+
prompt: diffPrompt
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return chat(diffPrompt, { system: DIFF_REVIEW_SYSTEM });
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return `Git error: ${error.message}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!paths) {
|
|
141
|
+
return 'Missing required "paths" for files mode.';
|
|
142
|
+
}
|
|
143
|
+
const pathList = paths.split(",").map((p) => p.trim());
|
|
144
|
+
const filesToReview = [];
|
|
145
|
+
for (const p of pathList) {
|
|
146
|
+
if (p.includes("*") || p.includes("?")) {
|
|
147
|
+
const matched = await findFiles(p);
|
|
148
|
+
filesToReview.push(...matched);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const stat = statSync(p);
|
|
153
|
+
if (stat.isFile()) {
|
|
154
|
+
filesToReview.push(p);
|
|
155
|
+
} else if (stat.isDirectory()) {
|
|
156
|
+
const pattern = `${p}/**/*`;
|
|
157
|
+
const matched = await findFiles(pattern, { codeOnly: true });
|
|
158
|
+
filesToReview.push(...matched);
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
return `Path not found: ${p}`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (filesToReview.length === 0) {
|
|
165
|
+
return "No code files found to review.";
|
|
166
|
+
}
|
|
167
|
+
const { files, truncated } = readFilesWithLimit(filesToReview);
|
|
168
|
+
let formattedContent = files.join("\n");
|
|
169
|
+
if (truncated > 0) {
|
|
170
|
+
formattedContent += `
|
|
171
|
+
|
|
172
|
+
... truncated (${truncated} more files)`;
|
|
173
|
+
}
|
|
174
|
+
const focusText = FOCUS_INSTRUCTIONS[focus] || FOCUS_INSTRUCTIONS.general;
|
|
175
|
+
const instruction = question ? `${focusText}
|
|
176
|
+
|
|
177
|
+
Specific question: ${question}` : focusText;
|
|
178
|
+
const promptText = `${instruction}
|
|
179
|
+
|
|
180
|
+
Review the following ${files.length} file(s). For each issue, cite the specific \`filename:line_number\`.
|
|
181
|
+
|
|
182
|
+
${formattedContent}`;
|
|
183
|
+
const systemPrompt = focus === "architecture" ? ARCHITECTURE_SYSTEM : CODE_REVIEW_SYSTEM;
|
|
184
|
+
if (dryRun) {
|
|
185
|
+
return formatDryRunSummary({
|
|
186
|
+
mode,
|
|
187
|
+
system: systemPrompt,
|
|
188
|
+
prompt: promptText,
|
|
189
|
+
fileCount: files.length,
|
|
190
|
+
truncated
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return chat(promptText, { system: systemPrompt });
|
|
194
|
+
}
|
|
195
|
+
var reviewTool = {
|
|
196
|
+
name: "review",
|
|
197
|
+
description: "Review code files, diffs, or prompts with Cerebras",
|
|
198
|
+
inputSchema: {
|
|
199
|
+
type: "object",
|
|
200
|
+
properties: {
|
|
201
|
+
mode: {
|
|
202
|
+
type: "string",
|
|
203
|
+
enum: ["files", "diff", "ask"],
|
|
204
|
+
description: "Review mode (default: files)"
|
|
205
|
+
},
|
|
206
|
+
paths: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: 'File paths, directory, or glob pattern (e.g., "**/*.py"). Comma-separated for multiple.'
|
|
209
|
+
},
|
|
210
|
+
question: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "Optional specific question to ask"
|
|
213
|
+
},
|
|
214
|
+
focus: {
|
|
215
|
+
type: "string",
|
|
216
|
+
enum: ["general", "security", "performance", "architecture", "style"],
|
|
217
|
+
description: "Review focus (default: general)"
|
|
218
|
+
},
|
|
219
|
+
base: {
|
|
220
|
+
type: "string",
|
|
221
|
+
description: "Diff base commit/branch (default: HEAD~1)"
|
|
222
|
+
},
|
|
223
|
+
path: {
|
|
224
|
+
type: "string",
|
|
225
|
+
description: "Optional path filter for diff mode"
|
|
226
|
+
},
|
|
227
|
+
prompt: {
|
|
228
|
+
type: "string",
|
|
229
|
+
description: "Prompt text for ask mode"
|
|
230
|
+
},
|
|
231
|
+
system: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: "Optional system prompt override for ask mode"
|
|
234
|
+
},
|
|
235
|
+
provider: {
|
|
236
|
+
type: "string",
|
|
237
|
+
enum: ["cerebras", "groq", "openai"],
|
|
238
|
+
description: "Provider override for ask mode"
|
|
239
|
+
},
|
|
240
|
+
dryRun: {
|
|
241
|
+
type: "boolean",
|
|
242
|
+
description: "Return token estimate only (no model call)"
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
required: []
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// ../reviewer/src/tools/tree.ts
|
|
250
|
+
function createNode() {
|
|
251
|
+
return { children: /* @__PURE__ */ new Map() };
|
|
252
|
+
}
|
|
253
|
+
function buildTree(paths, depth) {
|
|
254
|
+
const root = createNode();
|
|
255
|
+
for (const path of paths) {
|
|
256
|
+
const parts = path.split("/").filter(Boolean);
|
|
257
|
+
let node = root;
|
|
258
|
+
for (const part of parts) {
|
|
259
|
+
if (!node.children.has(part)) {
|
|
260
|
+
node.children.set(part, createNode());
|
|
261
|
+
}
|
|
262
|
+
node = node.children.get(part);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (depth <= 0) {
|
|
266
|
+
return ".";
|
|
267
|
+
}
|
|
268
|
+
const lines = ["."];
|
|
269
|
+
lines.push(...renderTree(root, "", depth, 0));
|
|
270
|
+
return lines.join("\n");
|
|
271
|
+
}
|
|
272
|
+
function renderTree(node, prefix, depth, level) {
|
|
273
|
+
const entries = Array.from(node.children.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
274
|
+
const lines = [];
|
|
275
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
276
|
+
const [name, child] = entries[i];
|
|
277
|
+
const isLast = i === entries.length - 1;
|
|
278
|
+
const connector = isLast ? "`-- " : "|-- ";
|
|
279
|
+
const nextPrefix = prefix + (isLast ? " " : "| ");
|
|
280
|
+
lines.push(`${prefix}${connector}${name}`);
|
|
281
|
+
if (child.children.size > 0) {
|
|
282
|
+
if (level + 1 < depth) {
|
|
283
|
+
lines.push(...renderTree(child, nextPrefix, depth, level + 1));
|
|
284
|
+
} else {
|
|
285
|
+
lines.push(`${nextPrefix}\`-- ...`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return lines;
|
|
290
|
+
}
|
|
291
|
+
function formatStats(paths) {
|
|
292
|
+
const counts = /* @__PURE__ */ new Map();
|
|
293
|
+
for (const filePath of paths) {
|
|
294
|
+
const base = filePath.split("/").pop() || "";
|
|
295
|
+
const dotIndex = base.lastIndexOf(".");
|
|
296
|
+
const ext = dotIndex > 0 ? base.slice(dotIndex) : "(none)";
|
|
297
|
+
counts.set(ext, (counts.get(ext) || 0) + 1);
|
|
298
|
+
}
|
|
299
|
+
const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1]);
|
|
300
|
+
return sorted.map(([ext, count]) => `${ext}: ${count}`).join("\n");
|
|
301
|
+
}
|
|
302
|
+
async function tree(args) {
|
|
303
|
+
const pattern = args.pattern || "**/*";
|
|
304
|
+
const depth = Number.isFinite(args.depth) ? args.depth : 3;
|
|
305
|
+
const summary = Boolean(args.summary);
|
|
306
|
+
const stats = Boolean(args.stats);
|
|
307
|
+
const files = await findFiles(pattern);
|
|
308
|
+
if (files.length === 0) {
|
|
309
|
+
return "No files found.";
|
|
310
|
+
}
|
|
311
|
+
const treeOutput = buildTree(files, depth);
|
|
312
|
+
const statsOutput = stats ? formatStats(files) : "";
|
|
313
|
+
let output = treeOutput;
|
|
314
|
+
if (stats && statsOutput) {
|
|
315
|
+
output += `
|
|
316
|
+
|
|
317
|
+
Stats:
|
|
318
|
+
${statsOutput}`;
|
|
319
|
+
}
|
|
320
|
+
if (summary) {
|
|
321
|
+
const summaryPrompt = `Summarize this repository structure for a non-technical reader.
|
|
322
|
+
|
|
323
|
+
Tree:
|
|
324
|
+
${treeOutput}
|
|
325
|
+
|
|
326
|
+
Stats:
|
|
327
|
+
${statsOutput || "n/a"}`;
|
|
328
|
+
const summaryText = await chat(summaryPrompt, { system: ASK_SYSTEM });
|
|
329
|
+
output += `
|
|
330
|
+
|
|
331
|
+
Summary:
|
|
332
|
+
${summaryText}`;
|
|
333
|
+
}
|
|
334
|
+
return output;
|
|
335
|
+
}
|
|
336
|
+
var treeTool = {
|
|
337
|
+
name: "tree",
|
|
338
|
+
description: "Show a gitignore-aware repository tree (optional summary or stats)",
|
|
339
|
+
inputSchema: {
|
|
340
|
+
type: "object",
|
|
341
|
+
properties: {
|
|
342
|
+
pattern: {
|
|
343
|
+
type: "string",
|
|
344
|
+
description: 'Optional glob pattern (e.g., "**/*.js")'
|
|
345
|
+
},
|
|
346
|
+
depth: {
|
|
347
|
+
type: "number",
|
|
348
|
+
description: "Tree depth (default: 3)"
|
|
349
|
+
},
|
|
350
|
+
summary: {
|
|
351
|
+
type: "boolean",
|
|
352
|
+
description: "Include a layman summary (uses LLM)"
|
|
353
|
+
},
|
|
354
|
+
stats: {
|
|
355
|
+
type: "boolean",
|
|
356
|
+
description: "Include file extension counts"
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
required: []
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// ../reviewer/src/index.ts
|
|
364
|
+
var server = new Server(
|
|
365
|
+
{
|
|
366
|
+
name: "reviewer",
|
|
367
|
+
version: "1.0.0"
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
capabilities: {
|
|
371
|
+
tools: {}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
376
|
+
tools: [reviewTool, treeTool]
|
|
377
|
+
}));
|
|
378
|
+
function tokenizeArgs(input) {
|
|
379
|
+
const tokens = [];
|
|
380
|
+
let current = "";
|
|
381
|
+
let quote = null;
|
|
382
|
+
let escape = false;
|
|
383
|
+
for (const char of input) {
|
|
384
|
+
if (escape) {
|
|
385
|
+
current += char;
|
|
386
|
+
escape = false;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (char === "\\") {
|
|
390
|
+
escape = true;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (quote) {
|
|
394
|
+
if (char === quote) {
|
|
395
|
+
quote = null;
|
|
396
|
+
} else {
|
|
397
|
+
current += char;
|
|
398
|
+
}
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (char === '"' || char === "'") {
|
|
402
|
+
quote = char;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (/\s/.test(char)) {
|
|
406
|
+
if (current) {
|
|
407
|
+
tokens.push(current);
|
|
408
|
+
current = "";
|
|
409
|
+
}
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
current += char;
|
|
413
|
+
}
|
|
414
|
+
if (current) {
|
|
415
|
+
tokens.push(current);
|
|
416
|
+
}
|
|
417
|
+
return tokens;
|
|
418
|
+
}
|
|
419
|
+
function parseReviewArgs(args) {
|
|
420
|
+
if (typeof args !== "string") {
|
|
421
|
+
return args || {};
|
|
422
|
+
}
|
|
423
|
+
const tokens = tokenizeArgs(args);
|
|
424
|
+
const parsed = { mode: "files" };
|
|
425
|
+
const remaining = [];
|
|
426
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
427
|
+
const token = tokens[i];
|
|
428
|
+
switch (token) {
|
|
429
|
+
case "--dry":
|
|
430
|
+
case "--dry-run":
|
|
431
|
+
parsed.dryRun = true;
|
|
432
|
+
break;
|
|
433
|
+
case "--diff":
|
|
434
|
+
parsed.mode = "diff";
|
|
435
|
+
break;
|
|
436
|
+
case "--ask":
|
|
437
|
+
parsed.mode = "ask";
|
|
438
|
+
break;
|
|
439
|
+
case "--base":
|
|
440
|
+
parsed.base = tokens[i + 1];
|
|
441
|
+
i += 1;
|
|
442
|
+
break;
|
|
443
|
+
case "--path":
|
|
444
|
+
parsed.path = tokens[i + 1];
|
|
445
|
+
i += 1;
|
|
446
|
+
break;
|
|
447
|
+
case "--focus":
|
|
448
|
+
parsed.focus = tokens[i + 1];
|
|
449
|
+
i += 1;
|
|
450
|
+
break;
|
|
451
|
+
case "--question":
|
|
452
|
+
parsed.question = tokens[i + 1];
|
|
453
|
+
i += 1;
|
|
454
|
+
break;
|
|
455
|
+
case "--system":
|
|
456
|
+
parsed.system = tokens[i + 1];
|
|
457
|
+
i += 1;
|
|
458
|
+
break;
|
|
459
|
+
case "--provider":
|
|
460
|
+
parsed.provider = tokens[i + 1];
|
|
461
|
+
i += 1;
|
|
462
|
+
break;
|
|
463
|
+
case "--prompt":
|
|
464
|
+
parsed.prompt = tokens[i + 1];
|
|
465
|
+
i += 1;
|
|
466
|
+
break;
|
|
467
|
+
default:
|
|
468
|
+
remaining.push(token);
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (parsed.mode === "ask") {
|
|
473
|
+
if (!parsed.prompt && remaining.length > 0) {
|
|
474
|
+
parsed.prompt = remaining.join(" ");
|
|
475
|
+
}
|
|
476
|
+
} else if (parsed.mode === "diff") {
|
|
477
|
+
if (!parsed.path && remaining.length > 0) {
|
|
478
|
+
parsed.path = remaining.join(" ");
|
|
479
|
+
}
|
|
480
|
+
} else if (remaining.length > 0) {
|
|
481
|
+
parsed.paths = remaining.join(",");
|
|
482
|
+
}
|
|
483
|
+
return parsed;
|
|
484
|
+
}
|
|
485
|
+
function parseTreeArgs(args) {
|
|
486
|
+
if (typeof args !== "string") {
|
|
487
|
+
return args || {};
|
|
488
|
+
}
|
|
489
|
+
const tokens = tokenizeArgs(args);
|
|
490
|
+
const parsed = {};
|
|
491
|
+
const remaining = [];
|
|
492
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
493
|
+
const token = tokens[i];
|
|
494
|
+
switch (token) {
|
|
495
|
+
case "--summary":
|
|
496
|
+
parsed.summary = true;
|
|
497
|
+
break;
|
|
498
|
+
case "--stats":
|
|
499
|
+
parsed.stats = true;
|
|
500
|
+
break;
|
|
501
|
+
case "--depth":
|
|
502
|
+
parsed.depth = Number.parseInt(tokens[i + 1] || "", 10);
|
|
503
|
+
i += 1;
|
|
504
|
+
break;
|
|
505
|
+
default:
|
|
506
|
+
remaining.push(token);
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (remaining.length > 0) {
|
|
511
|
+
parsed.pattern = remaining.join(" ");
|
|
512
|
+
}
|
|
513
|
+
return parsed;
|
|
514
|
+
}
|
|
515
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
516
|
+
const { name, arguments: args } = request.params;
|
|
517
|
+
try {
|
|
518
|
+
let result;
|
|
519
|
+
switch (name) {
|
|
520
|
+
case "review":
|
|
521
|
+
result = await review(parseReviewArgs(args));
|
|
522
|
+
break;
|
|
523
|
+
case "tree":
|
|
524
|
+
result = await tree(parseTreeArgs(args));
|
|
525
|
+
break;
|
|
526
|
+
default:
|
|
527
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
content: [{ type: "text", text: result }]
|
|
531
|
+
};
|
|
532
|
+
} catch (error) {
|
|
533
|
+
return {
|
|
534
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
535
|
+
isError: true
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
async function main() {
|
|
540
|
+
const transport = new StdioServerTransport();
|
|
541
|
+
await server.connect(transport);
|
|
542
|
+
}
|
|
543
|
+
main().catch(() => process.exit(1));
|
|
544
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../reviewer/src/index.ts", "../../../reviewer/src/tools/review.ts", "../../../reviewer/src/prompts.ts", "../../../reviewer/src/tools/tree.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * reviewer MCP server\n *\n * Provides code review tools powered by Cerebras LLM\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'\n\nimport { review, type ReviewArgs, reviewTool } from './tools/review.js'\nimport { tree, type TreeArgs, treeTool } from './tools/tree.js'\n\n// Create MCP server\nconst server = new Server(\n {\n name: 'reviewer',\n version: '1.0.0'\n },\n {\n capabilities: {\n tools: {}\n }\n }\n)\n\n// List available tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [reviewTool, treeTool]\n}))\n\nfunction tokenizeArgs(input: string): string[] {\n const tokens: string[] = []\n let current = ''\n let quote: '\"' | \"'\" | null = null\n let escape = false\n\n for (const char of input) {\n if (escape) {\n current += char\n escape = false\n continue\n }\n\n if (char === '\\\\') {\n escape = true\n continue\n }\n\n if (quote) {\n if (char === quote) {\n quote = null\n } else {\n current += char\n }\n continue\n }\n\n if (char === '\"' || char === \"'\") {\n quote = char\n continue\n }\n\n if (/\\s/.test(char)) {\n if (current) {\n tokens.push(current)\n current = ''\n }\n continue\n }\n\n current += char\n }\n\n if (current) {\n tokens.push(current)\n }\n\n return tokens\n}\n\nfunction parseReviewArgs(args: unknown): ReviewArgs {\n if (typeof args !== 'string') {\n return (args || {}) as ReviewArgs\n }\n\n const tokens = tokenizeArgs(args)\n const parsed: ReviewArgs = { mode: 'files' }\n const remaining: string[] = []\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i]\n\n switch (token) {\n case '--dry':\n case '--dry-run':\n parsed.dryRun = true\n break\n case '--diff':\n parsed.mode = 'diff'\n break\n case '--ask':\n parsed.mode = 'ask'\n break\n case '--base':\n parsed.base = tokens[i + 1]\n i += 1\n break\n case '--path':\n parsed.path = tokens[i + 1]\n i += 1\n break\n case '--focus':\n parsed.focus = tokens[i + 1] as ReviewArgs['focus']\n i += 1\n break\n case '--question':\n parsed.question = tokens[i + 1]\n i += 1\n break\n case '--system':\n parsed.system = tokens[i + 1]\n i += 1\n break\n case '--provider':\n parsed.provider = tokens[i + 1] as ReviewArgs['provider']\n i += 1\n break\n case '--prompt':\n parsed.prompt = tokens[i + 1]\n i += 1\n break\n default:\n remaining.push(token)\n break\n }\n }\n\n if (parsed.mode === 'ask') {\n if (!parsed.prompt && remaining.length > 0) {\n parsed.prompt = remaining.join(' ')\n }\n } else if (parsed.mode === 'diff') {\n if (!parsed.path && remaining.length > 0) {\n parsed.path = remaining.join(' ')\n }\n } else if (remaining.length > 0) {\n parsed.paths = remaining.join(',')\n }\n\n return parsed\n}\n\nfunction parseTreeArgs(args: unknown): TreeArgs {\n if (typeof args !== 'string') {\n return (args || {}) as TreeArgs\n }\n\n const tokens = tokenizeArgs(args)\n const parsed: TreeArgs = {}\n const remaining: string[] = []\n\n for (let i = 0; i < tokens.length; i += 1) {\n const token = tokens[i]\n\n switch (token) {\n case '--summary':\n parsed.summary = true\n break\n case '--stats':\n parsed.stats = true\n break\n case '--depth':\n parsed.depth = Number.parseInt(tokens[i + 1] || '', 10)\n i += 1\n break\n default:\n remaining.push(token)\n break\n }\n }\n\n if (remaining.length > 0) {\n parsed.pattern = remaining.join(' ')\n }\n\n return parsed\n}\n\n// Handle tool calls\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params\n\n try {\n let result: string\n\n switch (name) {\n case 'review':\n result = await review(parseReviewArgs(args))\n break\n case 'tree':\n result = await tree(parseTreeArgs(args))\n break\n default:\n throw new Error(`Unknown tool: ${name}`)\n }\n\n return {\n content: [{ type: 'text', text: result }]\n }\n } catch (error) {\n return {\n content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],\n isError: true\n }\n }\n})\n\n// Start server\nasync function main() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n // Note: No console output - it interferes with MCP protocol\n}\n\nmain().catch(() => process.exit(1))\n", "import { chat, findFiles, getDiff, type Provider, readFilesWithLimit } from '@goobits/sherpa-core'\nimport { statSync } from 'fs'\n\nimport {\n ARCHITECTURE_SYSTEM,\n ASK_SYSTEM,\n CODE_REVIEW_SYSTEM,\n DIFF_REVIEW_SYSTEM,\n FOCUS_INSTRUCTIONS\n} from '../prompts.js'\n\nexport interface ReviewArgs {\n mode?: 'files' | 'diff' | 'ask'\n paths?: string\n question?: string\n focus?: 'general' | 'security' | 'performance' | 'architecture' | 'style'\n base?: string\n path?: string\n prompt?: string\n system?: string | null\n provider?: Provider | null\n dryRun?: boolean\n}\n\nfunction estimateTokens(text: string): number {\n if (!text) {\n return 0\n }\n\n return Math.ceil(text.length / 4)\n}\n\nfunction formatDryRunSummary(options: {\n mode: 'files' | 'diff' | 'ask'\n system: string\n prompt: string\n fileCount?: number\n truncated?: number\n}): string {\n const combined = `${options.system}\\n\\n${options.prompt}`\n const lines = [\n 'Dry run only (no model call).',\n `Mode: ${options.mode}`,\n `System chars: ${options.system.length}`,\n `Prompt chars: ${options.prompt.length}`,\n `Estimated tokens: ${estimateTokens(combined)}`\n ]\n\n if (typeof options.fileCount === 'number') {\n lines.push(`Files: ${options.fileCount}`)\n }\n\n if (options.truncated && options.truncated > 0) {\n lines.push(`Truncated files: ${options.truncated}`)\n }\n\n return lines.join('\\n')\n}\n\nexport async function review(args: ReviewArgs): Promise<string> {\n const {\n mode = 'files',\n paths,\n question,\n focus = 'general',\n base = 'HEAD~1',\n path,\n prompt: askPrompt,\n system: askSystem,\n provider,\n dryRun = false\n } = args\n\n if (mode === 'ask') {\n if (!askPrompt) {\n return 'Missing required \"prompt\" for ask mode.'\n }\n\n const systemPrompt = askSystem || ASK_SYSTEM\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: systemPrompt,\n prompt: askPrompt\n })\n }\n\n return chat(askPrompt, { system: systemPrompt, provider: provider || undefined })\n }\n\n if (mode === 'diff') {\n try {\n const diff = await getDiff(base, path)\n\n if (!diff.trim()) {\n return 'No changes found.'\n }\n\n const diffPrompt = `Review this diff:\\n\\n\\`\\`\\`diff\\n${diff}\\n\\`\\`\\``\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: DIFF_REVIEW_SYSTEM,\n prompt: diffPrompt\n })\n }\n\n return chat(diffPrompt, { system: DIFF_REVIEW_SYSTEM })\n } catch (error) {\n return `Git error: ${(error as Error).message}`\n }\n }\n\n if (!paths) {\n return 'Missing required \"paths\" for files mode.'\n }\n\n const pathList = paths.split(',').map((p) => p.trim())\n const filesToReview: string[] = []\n\n for (const p of pathList) {\n if (p.includes('*') || p.includes('?')) {\n const matched = await findFiles(p)\n filesToReview.push(...matched)\n continue\n }\n\n try {\n const stat = statSync(p)\n if (stat.isFile()) {\n filesToReview.push(p)\n } else if (stat.isDirectory()) {\n const pattern = `${p}/**/*`\n const matched = await findFiles(pattern, { codeOnly: true })\n filesToReview.push(...matched)\n }\n } catch {\n return `Path not found: ${p}`\n }\n }\n\n if (filesToReview.length === 0) {\n return 'No code files found to review.'\n }\n\n const { files, truncated } = readFilesWithLimit(filesToReview)\n\n let formattedContent = files.join('\\n')\n if (truncated > 0) {\n formattedContent += `\\n\\n... truncated (${truncated} more files)`\n }\n\n // Build prompt\n const focusText = FOCUS_INSTRUCTIONS[focus] || FOCUS_INSTRUCTIONS.general\n const instruction = question ? `${focusText}\\n\\nSpecific question: ${question}` : focusText\n\n const promptText = `${instruction}\n\nReview the following ${files.length} file(s). For each issue, cite the specific \\`filename:line_number\\`.\n\n${formattedContent}`\n\n const systemPrompt = focus === 'architecture' ? ARCHITECTURE_SYSTEM : CODE_REVIEW_SYSTEM\n\n if (dryRun) {\n return formatDryRunSummary({\n mode,\n system: systemPrompt,\n prompt: promptText,\n fileCount: files.length,\n truncated\n })\n }\n\n return chat(promptText, { system: systemPrompt })\n}\n\nexport const reviewTool = {\n name: 'review',\n description: 'Review code files, diffs, or prompts with Cerebras',\n inputSchema: {\n type: 'object' as const,\n properties: {\n mode: {\n type: 'string',\n enum: ['files', 'diff', 'ask'],\n description: 'Review mode (default: files)'\n },\n paths: {\n type: 'string',\n description:\n 'File paths, directory, or glob pattern (e.g., \"**/*.py\"). Comma-separated for multiple.'\n },\n question: {\n type: 'string',\n description: 'Optional specific question to ask'\n },\n focus: {\n type: 'string',\n enum: ['general', 'security', 'performance', 'architecture', 'style'],\n description: 'Review focus (default: general)'\n },\n base: {\n type: 'string',\n description: 'Diff base commit/branch (default: HEAD~1)'\n },\n path: {\n type: 'string',\n description: 'Optional path filter for diff mode'\n },\n prompt: {\n type: 'string',\n description: 'Prompt text for ask mode'\n },\n system: {\n type: 'string',\n description: 'Optional system prompt override for ask mode'\n },\n provider: {\n type: 'string',\n enum: ['cerebras', 'groq', 'openai'],\n description: 'Provider override for ask mode'\n },\n dryRun: {\n type: 'boolean',\n description: 'Return token estimate only (no model call)'\n }\n },\n required: []\n }\n}\n", "/**\n * System prompts for Cerebras code review\n */\n\nexport const CODE_REVIEW_SYSTEM = `You are an expert code reviewer. For each issue you find, cite the specific location as \\`filename:line_number\\`.\n\nAnalyze the provided code for:\n1. **Correctness** - Logic errors, edge cases, potential bugs\n2. **Security** - Vulnerabilities, injection risks, data exposure\n3. **Performance** - Inefficiencies, unnecessary allocations, O(n) concerns\n4. **Maintainability** - Readability, naming, complexity, SOLID principles\n\nFormat your response as:\n## Summary\n[1-2 sentence overview]\n\n## Issues\n- **[severity]** \\`file:line\\` - description\n\n## Verdict\n[production-ready / needs fixes / major concerns]\n\nBe concise. Prioritize actionable feedback over praise.`\n\nexport const DIFF_REVIEW_SYSTEM = `You are reviewing a git diff. Focus on:\n\n1. **What changed** - Summarize the intent in 1-2 sentences\n2. **Issues** - Bugs, security concerns, or regressions introduced\n3. **Suggestions** - Concrete improvements (if any)\n\nBe brief. Skip obvious or trivial observations. Flag anything that could break production.`\n\nexport const ARCHITECTURE_SYSTEM = `You are a software architect analyzing code structure.\n\nFor each observation, cite the specific location as \\`filename:line_number\\`.\n\nFocus on:\n1. **Organization** - Module boundaries, dependency direction, coupling\n2. **Patterns** - Design patterns in use, antipatterns detected\n3. **Scalability** - Bottlenecks, extension points, rigidity\n\nAnswer the specific question asked. Be direct and technical.`\n\nexport const ASK_SYSTEM = `You are a helpful AI assistant with expertise in software engineering.\nBe concise and direct. Provide code examples when helpful.`\n\n/** Focus-specific instructions */\nexport const FOCUS_INSTRUCTIONS: Record<string, string> = {\n\tgeneral: 'Perform a comprehensive code review.',\n\tsecurity:\n\t\t'Focus especially on security vulnerabilities, injection risks, and data exposure.',\n\tperformance:\n\t\t'Focus especially on performance issues, algorithmic complexity, and resource usage.',\n\tarchitecture:\n\t\t'Focus especially on design patterns, SOLID principles, and code organization.',\n\tstyle: 'Focus especially on code style, readability, and maintainability.'\n}\n", "import { chat, findFiles } from '@goobits/sherpa-core'\n\nimport { ASK_SYSTEM } from '../prompts.js'\n\nexport interface TreeArgs {\n pattern?: string\n depth?: number\n summary?: boolean\n stats?: boolean\n}\n\ntype TreeNode = {\n children: Map<string, TreeNode>\n}\n\nfunction createNode(): TreeNode {\n return { children: new Map() }\n}\n\nfunction buildTree(paths: string[], depth: number): string {\n const root = createNode()\n\n for (const path of paths) {\n const parts = path.split('/').filter(Boolean)\n let node = root\n for (const part of parts) {\n if (!node.children.has(part)) {\n node.children.set(part, createNode())\n }\n node = node.children.get(part) as TreeNode\n }\n }\n\n if (depth <= 0) {\n return '.'\n }\n\n const lines = ['.']\n lines.push(...renderTree(root, '', depth, 0))\n return lines.join('\\n')\n}\n\nfunction renderTree(node: TreeNode, prefix: string, depth: number, level: number): string[] {\n const entries = Array.from(node.children.entries()).sort((a, b) => a[0].localeCompare(b[0]))\n const lines: string[] = []\n\n for (let i = 0; i < entries.length; i += 1) {\n const [name, child] = entries[i]\n const isLast = i === entries.length - 1\n const connector = isLast ? '`-- ' : '|-- '\n const nextPrefix = prefix + (isLast ? ' ' : '| ')\n\n lines.push(`${prefix}${connector}${name}`)\n\n if (child.children.size > 0) {\n if (level + 1 < depth) {\n lines.push(...renderTree(child, nextPrefix, depth, level + 1))\n } else {\n lines.push(`${nextPrefix}\\`-- ...`)\n }\n }\n }\n\n return lines\n}\n\nfunction formatStats(paths: string[]): string {\n const counts = new Map<string, number>()\n\n for (const filePath of paths) {\n const base = filePath.split('/').pop() || ''\n const dotIndex = base.lastIndexOf('.')\n const ext = dotIndex > 0 ? base.slice(dotIndex) : '(none)'\n counts.set(ext, (counts.get(ext) || 0) + 1)\n }\n\n const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1])\n return sorted.map(([ext, count]) => `${ext}: ${count}`).join('\\n')\n}\n\nexport async function tree(args: TreeArgs): Promise<string> {\n const pattern = args.pattern || '**/*'\n const depth = Number.isFinite(args.depth) ? (args.depth as number) : 3\n const summary = Boolean(args.summary)\n const stats = Boolean(args.stats)\n\n const files = await findFiles(pattern)\n\n if (files.length === 0) {\n return 'No files found.'\n }\n\n const treeOutput = buildTree(files, depth)\n const statsOutput = stats ? formatStats(files) : ''\n\n let output = treeOutput\n\n if (stats && statsOutput) {\n output += `\\n\\nStats:\\n${statsOutput}`\n }\n\n if (summary) {\n const summaryPrompt = `Summarize this repository structure for a non-technical reader.\n\nTree:\n${treeOutput}\n\nStats:\n${statsOutput || 'n/a'}`\n const summaryText = await chat(summaryPrompt, { system: ASK_SYSTEM })\n output += `\\n\\nSummary:\\n${summaryText}`\n }\n\n return output\n}\n\nexport const treeTool = {\n name: 'tree',\n description: 'Show a gitignore-aware repository tree (optional summary or stats)',\n inputSchema: {\n type: 'object' as const,\n properties: {\n pattern: {\n type: 'string',\n description: 'Optional glob pattern (e.g., \"**/*.js\")'\n },\n depth: {\n type: 'number',\n description: 'Tree depth (default: 3)'\n },\n summary: {\n type: 'boolean',\n description: 'Include a layman summary (uses LLM)'\n },\n stats: {\n type: 'boolean',\n description: 'Include file extension counts'\n }\n },\n required: []\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["systemPrompt"]
|
|
7
|
+
}
|
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule matching engine for guard
|
|
3
|
+
*/
|
|
4
|
+
import type { ASTNode, CheckResult, CommandInfo, Rule, RulesConfig } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Check if command matches a block rule
|
|
7
|
+
*/
|
|
8
|
+
export declare function matchesBlockRule(cmdInfo: CommandInfo, rule: Rule): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Check if command matches an allow rule
|
|
11
|
+
*/
|
|
12
|
+
export declare function matchesAllowRule(cmdInfo: CommandInfo, rule: Rule): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Check pipeline for dangerous pipe patterns (curl | bash)
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkPipeline(ast: ASTNode, rules: RulesConfig): Rule | null;
|
|
17
|
+
/**
|
|
18
|
+
* Check a command against all rules
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkCommand(cmdInfo: CommandInfo, rules: RulesConfig): CheckResult;
|
|
21
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AActF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CA2D1E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAuB1E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CA+B3E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC3B,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,WAAW,GAChB,WAAW,CAiBb"}
|