@probelabs/probe 0.6.0-rc102 → 0.6.0-rc103
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/build/agent/ProbeAgent.js +252 -2
- package/build/agent/index.js +228 -36
- package/build/mcp/index.js +23 -144
- package/build/mcp/index.ts +22 -161
- package/cjs/agent/ProbeAgent.cjs +193 -8
- package/cjs/index.cjs +205 -20
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +252 -2
- package/src/agent/index.js +26 -14
- package/src/mcp/index.ts +22 -161
package/build/agent/index.js
CHANGED
|
@@ -1821,7 +1821,7 @@ async function delegate({ task, timeout = 300, debug = false, currentIteration =
|
|
|
1821
1821
|
console.error(`[DELEGATE] Using binary at: ${binaryPath}`);
|
|
1822
1822
|
console.error(`[DELEGATE] Command args: ${args.join(" ")}`);
|
|
1823
1823
|
}
|
|
1824
|
-
return new Promise((
|
|
1824
|
+
return new Promise((resolve3, reject) => {
|
|
1825
1825
|
const delegationSpan = tracer ? tracer.createDelegationSpan(sessionId, task) : null;
|
|
1826
1826
|
const process2 = spawn(binaryPath, args, {
|
|
1827
1827
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1886,7 +1886,7 @@ async function delegate({ task, timeout = 300, debug = false, currentIteration =
|
|
|
1886
1886
|
delegationSpan.end();
|
|
1887
1887
|
}
|
|
1888
1888
|
}
|
|
1889
|
-
|
|
1889
|
+
resolve3(response);
|
|
1890
1890
|
} else {
|
|
1891
1891
|
const errorMessage = stderr.trim() || `Delegate process failed with exit code ${code}`;
|
|
1892
1892
|
if (debug) {
|
|
@@ -2516,20 +2516,20 @@ var init_simpleTelemetry = __esm({
|
|
|
2516
2516
|
}
|
|
2517
2517
|
async flush() {
|
|
2518
2518
|
if (this.stream) {
|
|
2519
|
-
return new Promise((
|
|
2520
|
-
this.stream.once("drain",
|
|
2519
|
+
return new Promise((resolve3) => {
|
|
2520
|
+
this.stream.once("drain", resolve3);
|
|
2521
2521
|
if (!this.stream.writableNeedDrain) {
|
|
2522
|
-
|
|
2522
|
+
resolve3();
|
|
2523
2523
|
}
|
|
2524
2524
|
});
|
|
2525
2525
|
}
|
|
2526
2526
|
}
|
|
2527
2527
|
async shutdown() {
|
|
2528
2528
|
if (this.stream) {
|
|
2529
|
-
return new Promise((
|
|
2529
|
+
return new Promise((resolve3) => {
|
|
2530
2530
|
this.stream.end(() => {
|
|
2531
2531
|
console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
|
|
2532
|
-
|
|
2532
|
+
resolve3();
|
|
2533
2533
|
});
|
|
2534
2534
|
});
|
|
2535
2535
|
}
|
|
@@ -2975,7 +2975,7 @@ function createMockProvider() {
|
|
|
2975
2975
|
provider: "mock",
|
|
2976
2976
|
// Mock the doGenerate method used by Vercel AI SDK
|
|
2977
2977
|
doGenerate: async ({ messages, tools: tools2 }) => {
|
|
2978
|
-
await new Promise((
|
|
2978
|
+
await new Promise((resolve3) => setTimeout(resolve3, 10));
|
|
2979
2979
|
return {
|
|
2980
2980
|
text: "This is a mock response for testing",
|
|
2981
2981
|
toolCalls: [],
|
|
@@ -4865,7 +4865,10 @@ import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
|
4865
4865
|
import { streamText } from "ai";
|
|
4866
4866
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
4867
4867
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
4868
|
-
|
|
4868
|
+
import { existsSync as existsSync3 } from "fs";
|
|
4869
|
+
import { readFile, stat } from "fs/promises";
|
|
4870
|
+
import { resolve, isAbsolute } from "path";
|
|
4871
|
+
var MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, SUPPORTED_IMAGE_EXTENSIONS, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
4869
4872
|
var init_ProbeAgent = __esm({
|
|
4870
4873
|
"src/agent/ProbeAgent.js"() {
|
|
4871
4874
|
"use strict";
|
|
@@ -4879,6 +4882,8 @@ var init_ProbeAgent = __esm({
|
|
|
4879
4882
|
init_mcp();
|
|
4880
4883
|
MAX_TOOL_ITERATIONS = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
|
|
4881
4884
|
MAX_HISTORY_MESSAGES = 100;
|
|
4885
|
+
SUPPORTED_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif", "bmp", "svg"];
|
|
4886
|
+
MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
|
|
4882
4887
|
ProbeAgent = class {
|
|
4883
4888
|
/**
|
|
4884
4889
|
* Create a new ProbeAgent instance
|
|
@@ -4922,6 +4927,8 @@ var init_ProbeAgent = __esm({
|
|
|
4922
4927
|
}
|
|
4923
4928
|
this.initializeTools();
|
|
4924
4929
|
this.history = [];
|
|
4930
|
+
this.pendingImages = /* @__PURE__ */ new Map();
|
|
4931
|
+
this.currentImages = [];
|
|
4925
4932
|
this.events = new EventEmitter2();
|
|
4926
4933
|
this.enableMcp = !!options.enableMcp || process.env.ENABLE_MCP === "1";
|
|
4927
4934
|
this.mcpConfigPath = options.mcpConfigPath || null;
|
|
@@ -5047,6 +5054,175 @@ var init_ProbeAgent = __esm({
|
|
|
5047
5054
|
console.log(`Using Google API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
5048
5055
|
}
|
|
5049
5056
|
}
|
|
5057
|
+
/**
|
|
5058
|
+
* Process assistant response content and detect/load image references
|
|
5059
|
+
* @param {string} content - The assistant's response content
|
|
5060
|
+
* @returns {Promise<void>}
|
|
5061
|
+
*/
|
|
5062
|
+
async processImageReferences(content) {
|
|
5063
|
+
if (!content) return;
|
|
5064
|
+
const extensionsPattern = `(?:${SUPPORTED_IMAGE_EXTENSIONS.join("|")})`;
|
|
5065
|
+
const imagePatterns = [
|
|
5066
|
+
// Direct file path mentions: "./screenshot.png", "/path/to/image.jpg", etc.
|
|
5067
|
+
new RegExp(`(?:\\.?\\.\\/)?[^\\s"'<>\\[\\]]+\\.${extensionsPattern}(?!\\w)`, "gi"),
|
|
5068
|
+
// Contextual mentions: "look at image.png", "the file screenshot.jpg shows"
|
|
5069
|
+
new RegExp(`(?:image|file|screenshot|diagram|photo|picture|graphic)\\s*:?\\s*([^\\s"'<>\\[\\]]+\\.${extensionsPattern})(?!\\w)`, "gi"),
|
|
5070
|
+
// Tool result mentions: often contain file paths
|
|
5071
|
+
new RegExp(`(?:found|saved|created|generated).*?([^\\s"'<>\\[\\]]+\\.${extensionsPattern})(?!\\w)`, "gi")
|
|
5072
|
+
];
|
|
5073
|
+
const foundPaths = /* @__PURE__ */ new Set();
|
|
5074
|
+
for (const pattern of imagePatterns) {
|
|
5075
|
+
let match;
|
|
5076
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
5077
|
+
const imagePath = match[1] || match[0];
|
|
5078
|
+
if (imagePath && imagePath.length > 0) {
|
|
5079
|
+
foundPaths.add(imagePath.trim());
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
if (foundPaths.size === 0) return;
|
|
5084
|
+
if (this.debug) {
|
|
5085
|
+
console.log(`[DEBUG] Found ${foundPaths.size} potential image references:`, Array.from(foundPaths));
|
|
5086
|
+
}
|
|
5087
|
+
for (const imagePath of foundPaths) {
|
|
5088
|
+
await this.loadImageIfValid(imagePath);
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
/**
|
|
5092
|
+
* Load and cache an image if it's valid and accessible
|
|
5093
|
+
* @param {string} imagePath - Path to the image file
|
|
5094
|
+
* @returns {Promise<boolean>} - True if image was loaded successfully
|
|
5095
|
+
*/
|
|
5096
|
+
async loadImageIfValid(imagePath) {
|
|
5097
|
+
try {
|
|
5098
|
+
if (this.pendingImages.has(imagePath)) {
|
|
5099
|
+
if (this.debug) {
|
|
5100
|
+
console.log(`[DEBUG] Image already loaded: ${imagePath}`);
|
|
5101
|
+
}
|
|
5102
|
+
return true;
|
|
5103
|
+
}
|
|
5104
|
+
const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
|
|
5105
|
+
let absolutePath;
|
|
5106
|
+
let isPathAllowed = false;
|
|
5107
|
+
if (isAbsolute(imagePath)) {
|
|
5108
|
+
absolutePath = imagePath;
|
|
5109
|
+
isPathAllowed = allowedDirs.some((dir) => absolutePath.startsWith(resolve(dir)));
|
|
5110
|
+
} else {
|
|
5111
|
+
for (const dir of allowedDirs) {
|
|
5112
|
+
const resolvedPath = resolve(dir, imagePath);
|
|
5113
|
+
if (resolvedPath.startsWith(resolve(dir))) {
|
|
5114
|
+
absolutePath = resolvedPath;
|
|
5115
|
+
isPathAllowed = true;
|
|
5116
|
+
break;
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
if (!isPathAllowed) {
|
|
5121
|
+
if (this.debug) {
|
|
5122
|
+
console.log(`[DEBUG] Image path outside allowed directories: ${imagePath}`);
|
|
5123
|
+
}
|
|
5124
|
+
return false;
|
|
5125
|
+
}
|
|
5126
|
+
let fileStats;
|
|
5127
|
+
try {
|
|
5128
|
+
fileStats = await stat(absolutePath);
|
|
5129
|
+
} catch (error) {
|
|
5130
|
+
if (this.debug) {
|
|
5131
|
+
console.log(`[DEBUG] Image file not found: ${absolutePath}`);
|
|
5132
|
+
}
|
|
5133
|
+
return false;
|
|
5134
|
+
}
|
|
5135
|
+
if (fileStats.size > MAX_IMAGE_FILE_SIZE) {
|
|
5136
|
+
if (this.debug) {
|
|
5137
|
+
console.log(`[DEBUG] Image file too large: ${absolutePath} (${fileStats.size} bytes, max: ${MAX_IMAGE_FILE_SIZE})`);
|
|
5138
|
+
}
|
|
5139
|
+
return false;
|
|
5140
|
+
}
|
|
5141
|
+
const extension = absolutePath.toLowerCase().split(".").pop();
|
|
5142
|
+
if (!SUPPORTED_IMAGE_EXTENSIONS.includes(extension)) {
|
|
5143
|
+
if (this.debug) {
|
|
5144
|
+
console.log(`[DEBUG] Unsupported image format: ${extension}`);
|
|
5145
|
+
}
|
|
5146
|
+
return false;
|
|
5147
|
+
}
|
|
5148
|
+
const mimeTypes = {
|
|
5149
|
+
"png": "image/png",
|
|
5150
|
+
"jpg": "image/jpeg",
|
|
5151
|
+
"jpeg": "image/jpeg",
|
|
5152
|
+
"webp": "image/webp",
|
|
5153
|
+
"gif": "image/gif",
|
|
5154
|
+
"bmp": "image/bmp",
|
|
5155
|
+
"svg": "image/svg+xml"
|
|
5156
|
+
};
|
|
5157
|
+
const mimeType = mimeTypes[extension];
|
|
5158
|
+
const fileBuffer = await readFile(absolutePath);
|
|
5159
|
+
const base64Data = fileBuffer.toString("base64");
|
|
5160
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
5161
|
+
this.pendingImages.set(imagePath, dataUrl);
|
|
5162
|
+
if (this.debug) {
|
|
5163
|
+
console.log(`[DEBUG] Successfully loaded image: ${imagePath} (${fileBuffer.length} bytes)`);
|
|
5164
|
+
}
|
|
5165
|
+
return true;
|
|
5166
|
+
} catch (error) {
|
|
5167
|
+
if (this.debug) {
|
|
5168
|
+
console.log(`[DEBUG] Failed to load image ${imagePath}: ${error.message}`);
|
|
5169
|
+
}
|
|
5170
|
+
return false;
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
/**
|
|
5174
|
+
* Get all currently loaded images as an array for AI model consumption
|
|
5175
|
+
* @returns {Array<string>} - Array of base64 data URLs
|
|
5176
|
+
*/
|
|
5177
|
+
getCurrentImages() {
|
|
5178
|
+
return Array.from(this.pendingImages.values());
|
|
5179
|
+
}
|
|
5180
|
+
/**
|
|
5181
|
+
* Clear loaded images (useful for new conversations)
|
|
5182
|
+
*/
|
|
5183
|
+
clearLoadedImages() {
|
|
5184
|
+
this.pendingImages.clear();
|
|
5185
|
+
this.currentImages = [];
|
|
5186
|
+
if (this.debug) {
|
|
5187
|
+
console.log("[DEBUG] Cleared all loaded images");
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
/**
|
|
5191
|
+
* Prepare messages for AI consumption, adding images to the latest user message if available
|
|
5192
|
+
* @param {Array} messages - Current conversation messages
|
|
5193
|
+
* @returns {Array} - Messages formatted for AI SDK with potential image content
|
|
5194
|
+
*/
|
|
5195
|
+
prepareMessagesWithImages(messages) {
|
|
5196
|
+
const loadedImages = this.getCurrentImages();
|
|
5197
|
+
if (loadedImages.length === 0) {
|
|
5198
|
+
return messages;
|
|
5199
|
+
}
|
|
5200
|
+
const messagesWithImages = [...messages];
|
|
5201
|
+
const lastUserMessageIndex = messagesWithImages.map((m) => m.role).lastIndexOf("user");
|
|
5202
|
+
if (lastUserMessageIndex === -1) {
|
|
5203
|
+
if (this.debug) {
|
|
5204
|
+
console.log("[DEBUG] No user messages found to attach images to");
|
|
5205
|
+
}
|
|
5206
|
+
return messages;
|
|
5207
|
+
}
|
|
5208
|
+
const lastUserMessage = messagesWithImages[lastUserMessageIndex];
|
|
5209
|
+
if (typeof lastUserMessage.content === "string") {
|
|
5210
|
+
messagesWithImages[lastUserMessageIndex] = {
|
|
5211
|
+
...lastUserMessage,
|
|
5212
|
+
content: [
|
|
5213
|
+
{ type: "text", text: lastUserMessage.content },
|
|
5214
|
+
...loadedImages.map((imageData) => ({
|
|
5215
|
+
type: "image",
|
|
5216
|
+
image: imageData
|
|
5217
|
+
}))
|
|
5218
|
+
]
|
|
5219
|
+
};
|
|
5220
|
+
if (this.debug) {
|
|
5221
|
+
console.log(`[DEBUG] Added ${loadedImages.length} images to the latest user message`);
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
return messagesWithImages;
|
|
5225
|
+
}
|
|
5050
5226
|
/**
|
|
5051
5227
|
* Initialize mock model for testing
|
|
5052
5228
|
*/
|
|
@@ -5428,9 +5604,10 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
5428
5604
|
let assistantResponseContent = "";
|
|
5429
5605
|
try {
|
|
5430
5606
|
const executeAIRequest = async () => {
|
|
5607
|
+
const messagesForAI = this.prepareMessagesWithImages(currentMessages);
|
|
5431
5608
|
const result = await streamText({
|
|
5432
5609
|
model: this.provider(this.model),
|
|
5433
|
-
messages:
|
|
5610
|
+
messages: messagesForAI,
|
|
5434
5611
|
maxTokens: maxResponseTokens,
|
|
5435
5612
|
temperature: 0.3
|
|
5436
5613
|
});
|
|
@@ -5464,6 +5641,9 @@ You are working with a repository located at: ${searchDirectory}
|
|
|
5464
5641
|
const assistantPreview = createMessagePreview(assistantResponseContent);
|
|
5465
5642
|
console.log(`[DEBUG] Assistant response (${assistantResponseContent.length} chars): ${assistantPreview}`);
|
|
5466
5643
|
}
|
|
5644
|
+
if (assistantResponseContent) {
|
|
5645
|
+
await this.processImageReferences(assistantResponseContent);
|
|
5646
|
+
}
|
|
5467
5647
|
const validTools = [
|
|
5468
5648
|
"search",
|
|
5469
5649
|
"query",
|
|
@@ -5588,12 +5768,17 @@ ${toolResultContent}
|
|
|
5588
5768
|
throw toolError;
|
|
5589
5769
|
}
|
|
5590
5770
|
currentMessages.push({ role: "assistant", content: assistantResponseContent });
|
|
5771
|
+
const toolResultContent = typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
5772
|
+
const toolResultMessage = `<tool_result>
|
|
5773
|
+
${toolResultContent}
|
|
5774
|
+
</tool_result>`;
|
|
5591
5775
|
currentMessages.push({
|
|
5592
5776
|
role: "user",
|
|
5593
|
-
content:
|
|
5594
|
-
${typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult, null, 2)}
|
|
5595
|
-
</tool_result>`
|
|
5777
|
+
content: toolResultMessage
|
|
5596
5778
|
});
|
|
5779
|
+
if (toolResultContent) {
|
|
5780
|
+
await this.processImageReferences(toolResultContent);
|
|
5781
|
+
}
|
|
5597
5782
|
if (this.debug) {
|
|
5598
5783
|
console.log(`[DEBUG] Tool ${toolName} executed successfully. Result length: ${typeof toolResult === "string" ? toolResult.length : JSON.stringify(toolResult).length}`);
|
|
5599
5784
|
}
|
|
@@ -6094,8 +6279,8 @@ import {
|
|
|
6094
6279
|
ListToolsRequestSchema,
|
|
6095
6280
|
McpError
|
|
6096
6281
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
6097
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
6098
|
-
import { resolve } from "path";
|
|
6282
|
+
import { readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
|
|
6283
|
+
import { resolve as resolve2 } from "path";
|
|
6099
6284
|
|
|
6100
6285
|
// src/agent/acp/server.js
|
|
6101
6286
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -6354,8 +6539,8 @@ var ACPConnection = class extends EventEmitter3 {
|
|
|
6354
6539
|
if (params !== null) {
|
|
6355
6540
|
message.params = params;
|
|
6356
6541
|
}
|
|
6357
|
-
return new Promise((
|
|
6358
|
-
this.pendingRequests.set(id, { resolve:
|
|
6542
|
+
return new Promise((resolve3, reject) => {
|
|
6543
|
+
this.pendingRequests.set(id, { resolve: resolve3, reject });
|
|
6359
6544
|
this.sendMessage(message);
|
|
6360
6545
|
setTimeout(() => {
|
|
6361
6546
|
if (this.pendingRequests.has(id)) {
|
|
@@ -6763,8 +6948,8 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
6763
6948
|
function readInputContent(input) {
|
|
6764
6949
|
if (!input) return null;
|
|
6765
6950
|
try {
|
|
6766
|
-
const resolvedPath =
|
|
6767
|
-
if (
|
|
6951
|
+
const resolvedPath = resolve2(input);
|
|
6952
|
+
if (existsSync4(resolvedPath)) {
|
|
6768
6953
|
return readFileSync2(resolvedPath, "utf-8").trim();
|
|
6769
6954
|
}
|
|
6770
6955
|
} catch (error) {
|
|
@@ -6772,7 +6957,7 @@ function readInputContent(input) {
|
|
|
6772
6957
|
return input;
|
|
6773
6958
|
}
|
|
6774
6959
|
function readFromStdin() {
|
|
6775
|
-
return new Promise((
|
|
6960
|
+
return new Promise((resolve3, reject) => {
|
|
6776
6961
|
let data = "";
|
|
6777
6962
|
let hasReceivedData = false;
|
|
6778
6963
|
let dataChunks = [];
|
|
@@ -6797,7 +6982,7 @@ function readFromStdin() {
|
|
|
6797
6982
|
if (!trimmed && dataChunks.length === 0) {
|
|
6798
6983
|
reject(new Error("No input received from stdin"));
|
|
6799
6984
|
} else {
|
|
6800
|
-
|
|
6985
|
+
resolve3(trimmed);
|
|
6801
6986
|
}
|
|
6802
6987
|
});
|
|
6803
6988
|
process.stdin.on("error", (error) => {
|
|
@@ -6964,6 +7149,7 @@ var ProbeAgentMcpServer = class {
|
|
|
6964
7149
|
}
|
|
6965
7150
|
}
|
|
6966
7151
|
);
|
|
7152
|
+
this.agent = null;
|
|
6967
7153
|
this.setupToolHandlers();
|
|
6968
7154
|
this.server.onerror = (error) => console.error("[MCP Error]", error);
|
|
6969
7155
|
process.on("SIGINT", async () => {
|
|
@@ -7075,18 +7261,24 @@ var ProbeAgentMcpServer = class {
|
|
|
7075
7261
|
throw new Error("Schema could not be read");
|
|
7076
7262
|
}
|
|
7077
7263
|
}
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7264
|
+
if (!this.agent) {
|
|
7265
|
+
if (process.env.DEBUG === "1") {
|
|
7266
|
+
console.error("[DEBUG] Initializing AI agent on first MCP tool call");
|
|
7267
|
+
}
|
|
7268
|
+
const agentConfig2 = {
|
|
7269
|
+
path: args.path || process.cwd(),
|
|
7270
|
+
promptType: args.prompt || "code-explorer",
|
|
7271
|
+
customPrompt: systemPrompt,
|
|
7272
|
+
provider: args.provider,
|
|
7273
|
+
model: args.model,
|
|
7274
|
+
allowEdit: !!args.allow_edit,
|
|
7275
|
+
debug: process.env.DEBUG === "1",
|
|
7276
|
+
maxResponseTokens: args.max_response_tokens,
|
|
7277
|
+
disableMermaidValidation: !!args.no_mermaid_validation
|
|
7278
|
+
};
|
|
7279
|
+
this.agent = new ProbeAgent(agentConfig2);
|
|
7280
|
+
}
|
|
7281
|
+
const agent = this.agent;
|
|
7090
7282
|
let result = await agent.answer(query2, [], { schema });
|
|
7091
7283
|
if (schema) {
|
|
7092
7284
|
const schemaPrompt = `Now you need to respond according to this schema:
|
|
@@ -7147,7 +7339,7 @@ Please reformat your previous response to match this schema exactly. Only return
|
|
|
7147
7339
|
} catch (error) {
|
|
7148
7340
|
}
|
|
7149
7341
|
}
|
|
7150
|
-
const tokenUsage = agent.getTokenUsage();
|
|
7342
|
+
const tokenUsage = this.agent.getTokenUsage();
|
|
7151
7343
|
console.error(`Token usage: ${JSON.stringify(tokenUsage)}`);
|
|
7152
7344
|
return {
|
|
7153
7345
|
content: [
|
|
@@ -7272,7 +7464,7 @@ async function main() {
|
|
|
7272
7464
|
process.exit(1);
|
|
7273
7465
|
}
|
|
7274
7466
|
}
|
|
7275
|
-
const
|
|
7467
|
+
const agentConfig2 = {
|
|
7276
7468
|
path: config.path,
|
|
7277
7469
|
promptType: config.prompt,
|
|
7278
7470
|
customPrompt: systemPrompt,
|
|
@@ -7283,7 +7475,7 @@ async function main() {
|
|
|
7283
7475
|
maxResponseTokens: config.maxResponseTokens,
|
|
7284
7476
|
disableMermaidValidation: config.noMermaidValidation
|
|
7285
7477
|
};
|
|
7286
|
-
const agent = new ProbeAgent(
|
|
7478
|
+
const agent = new ProbeAgent(agentConfig2);
|
|
7287
7479
|
let result;
|
|
7288
7480
|
if (appTracer) {
|
|
7289
7481
|
const sessionSpan = appTracer.createSessionSpan({
|
package/build/mcp/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import path from 'path';
|
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
// Import from parent package
|
|
11
|
-
import { search,
|
|
11
|
+
import { search, extract } from '../index.js';
|
|
12
12
|
// Parse command-line arguments
|
|
13
13
|
function parseArgs() {
|
|
14
14
|
const args = process.argv.slice(2);
|
|
@@ -128,38 +128,22 @@ class ProbeServer {
|
|
|
128
128
|
},
|
|
129
129
|
query: {
|
|
130
130
|
type: 'string',
|
|
131
|
-
description: 'Elastic search query. Supports logical operators (AND, OR, NOT), and grouping with parentheses. Examples: "config", "(term1 OR term2) AND term3"
|
|
132
|
-
},
|
|
133
|
-
filesOnly: {
|
|
134
|
-
type: 'boolean',
|
|
135
|
-
description: 'Skip AST parsing and just output unique files',
|
|
136
|
-
},
|
|
137
|
-
ignore: {
|
|
138
|
-
type: 'array',
|
|
139
|
-
items: { type: 'string' },
|
|
140
|
-
description: 'Custom patterns to ignore (in addition to .gitignore and common patterns)'
|
|
141
|
-
},
|
|
142
|
-
excludeFilenames: {
|
|
143
|
-
type: 'boolean',
|
|
144
|
-
description: 'Exclude filenames from being used for matching'
|
|
131
|
+
description: 'Elastic search query. Supports logical operators (AND, OR, NOT), and grouping with parentheses. For exact matches of specific identifiers, use quotes: "MyFunction", "SpecificStruct", "exact_variable_name". Examples: "config", "(term1 OR term2) AND term3", "getUserData", "struct Config".',
|
|
145
132
|
},
|
|
146
133
|
exact: {
|
|
147
134
|
type: 'boolean',
|
|
148
|
-
description: '
|
|
135
|
+
description: 'When you exactly know what you are looking for, like known function name, struct name, or variable name, set this flag for precise matching'
|
|
149
136
|
},
|
|
150
137
|
allowTests: {
|
|
151
138
|
type: 'boolean',
|
|
152
|
-
description: 'Allow test files and test code blocks in results (
|
|
139
|
+
description: 'Allow test files and test code blocks in results (enabled by default)',
|
|
140
|
+
default: true
|
|
153
141
|
},
|
|
154
142
|
session: {
|
|
155
143
|
type: 'string',
|
|
156
144
|
description: 'Session identifier for caching. Set to "new" if unknown, or want to reset cache. Re-use session ID returned from previous searches',
|
|
157
145
|
default: "new",
|
|
158
146
|
},
|
|
159
|
-
timeout: {
|
|
160
|
-
type: 'number',
|
|
161
|
-
description: 'Timeout for the search operation in seconds (default: 30)',
|
|
162
|
-
},
|
|
163
147
|
noGitignore: {
|
|
164
148
|
type: 'boolean',
|
|
165
149
|
description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
|
|
@@ -168,50 +152,6 @@ class ProbeServer {
|
|
|
168
152
|
required: ['path', 'query']
|
|
169
153
|
},
|
|
170
154
|
},
|
|
171
|
-
{
|
|
172
|
-
name: 'query_code',
|
|
173
|
-
description: "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.",
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: 'object',
|
|
176
|
-
properties: {
|
|
177
|
-
path: {
|
|
178
|
-
type: 'string',
|
|
179
|
-
description: 'Absolute path to the directory to search in (e.g., "/Users/username/projects/myproject").',
|
|
180
|
-
},
|
|
181
|
-
pattern: {
|
|
182
|
-
type: 'string',
|
|
183
|
-
description: 'The ast-grep pattern to search for. Examples: "fn $NAME($$$PARAMS) $$$BODY" for Rust functions, "def $NAME($$$PARAMS): $$$BODY" for Python functions.',
|
|
184
|
-
},
|
|
185
|
-
language: {
|
|
186
|
-
type: 'string',
|
|
187
|
-
description: 'The programming language to search in. If not specified, the tool will try to infer the language from file extensions. Supported languages: rust, javascript, typescript, python, go, c, cpp, java, ruby, php, swift, csharp, yaml.',
|
|
188
|
-
},
|
|
189
|
-
ignore: {
|
|
190
|
-
type: 'array',
|
|
191
|
-
items: { type: 'string' },
|
|
192
|
-
description: 'Custom patterns to ignore (in addition to common patterns)',
|
|
193
|
-
},
|
|
194
|
-
maxResults: {
|
|
195
|
-
type: 'number',
|
|
196
|
-
description: 'Maximum number of results to return'
|
|
197
|
-
},
|
|
198
|
-
format: {
|
|
199
|
-
type: 'string',
|
|
200
|
-
enum: ['markdown', 'plain', 'json', 'color'],
|
|
201
|
-
description: 'Output format for the query results'
|
|
202
|
-
},
|
|
203
|
-
timeout: {
|
|
204
|
-
type: 'number',
|
|
205
|
-
description: 'Timeout for the query operation in seconds (default: 30)',
|
|
206
|
-
},
|
|
207
|
-
noGitignore: {
|
|
208
|
-
type: 'boolean',
|
|
209
|
-
description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
required: ['path', 'pattern']
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
155
|
{
|
|
216
156
|
name: 'extract_code',
|
|
217
157
|
description: "Extract code blocks from files based on line number, or symbol name. Fetch full file when line number is not provided.",
|
|
@@ -229,22 +169,8 @@ class ProbeServer {
|
|
|
229
169
|
},
|
|
230
170
|
allowTests: {
|
|
231
171
|
type: 'boolean',
|
|
232
|
-
description: 'Allow test files and test code blocks in results (
|
|
233
|
-
|
|
234
|
-
contextLines: {
|
|
235
|
-
type: 'number',
|
|
236
|
-
description: 'Number of context lines to include before and after the extracted block when AST parsing fails to find a suitable node',
|
|
237
|
-
default: 0
|
|
238
|
-
},
|
|
239
|
-
format: {
|
|
240
|
-
type: 'string',
|
|
241
|
-
enum: ['markdown', 'plain', 'json'],
|
|
242
|
-
description: 'Output format for the extracted code',
|
|
243
|
-
default: 'markdown'
|
|
244
|
-
},
|
|
245
|
-
timeout: {
|
|
246
|
-
type: 'number',
|
|
247
|
-
description: 'Timeout for the extract operation in seconds (default: 30)',
|
|
172
|
+
description: 'Allow test files and test code blocks in results (enabled by default)',
|
|
173
|
+
default: true
|
|
248
174
|
},
|
|
249
175
|
noGitignore: {
|
|
250
176
|
type: 'boolean',
|
|
@@ -257,8 +183,8 @@ class ProbeServer {
|
|
|
257
183
|
],
|
|
258
184
|
}));
|
|
259
185
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
260
|
-
if (request.params.name !== 'search_code' && request.params.name !== '
|
|
261
|
-
request.params.name !== 'probe' && request.params.name !== '
|
|
186
|
+
if (request.params.name !== 'search_code' && request.params.name !== 'extract_code' &&
|
|
187
|
+
request.params.name !== 'probe' && request.params.name !== 'extract') {
|
|
262
188
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
263
189
|
}
|
|
264
190
|
try {
|
|
@@ -282,10 +208,6 @@ class ProbeServer {
|
|
|
282
208
|
}
|
|
283
209
|
result = await this.executeCodeSearch(args);
|
|
284
210
|
}
|
|
285
|
-
else if (request.params.name === 'query_code' || request.params.name === 'query') {
|
|
286
|
-
const args = request.params.arguments;
|
|
287
|
-
result = await this.executeCodeQuery(args);
|
|
288
|
-
}
|
|
289
211
|
else { // extract_code or extract
|
|
290
212
|
const args = request.params.arguments;
|
|
291
213
|
result = await this.executeCodeExtract(args);
|
|
@@ -331,20 +253,19 @@ class ProbeServer {
|
|
|
331
253
|
query: args.query
|
|
332
254
|
};
|
|
333
255
|
// Add optional parameters only if they exist
|
|
334
|
-
if (args.filesOnly !== undefined)
|
|
335
|
-
options.filesOnly = args.filesOnly;
|
|
336
|
-
if (args.ignore !== undefined)
|
|
337
|
-
options.ignore = args.ignore;
|
|
338
|
-
if (args.excludeFilenames !== undefined)
|
|
339
|
-
options.excludeFilenames = args.excludeFilenames;
|
|
340
256
|
if (args.exact !== undefined)
|
|
341
257
|
options.exact = args.exact;
|
|
342
258
|
if (args.maxResults !== undefined)
|
|
343
259
|
options.maxResults = args.maxResults;
|
|
344
260
|
if (args.maxTokens !== undefined)
|
|
345
261
|
options.maxTokens = args.maxTokens;
|
|
346
|
-
|
|
262
|
+
// Set allowTests to true by default if not specified
|
|
263
|
+
if (args.allowTests !== undefined) {
|
|
347
264
|
options.allowTests = args.allowTests;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
options.allowTests = true;
|
|
268
|
+
}
|
|
348
269
|
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
349
270
|
if (args.noGitignore !== undefined) {
|
|
350
271
|
options.noGitignore = args.noGitignore;
|
|
@@ -358,13 +279,6 @@ class ProbeServer {
|
|
|
358
279
|
else {
|
|
359
280
|
options.session = "new";
|
|
360
281
|
}
|
|
361
|
-
// Use timeout from args, or fall back to instance default
|
|
362
|
-
if (args.timeout !== undefined) {
|
|
363
|
-
options.timeout = args.timeout;
|
|
364
|
-
}
|
|
365
|
-
else if (this.defaultTimeout !== undefined) {
|
|
366
|
-
options.timeout = this.defaultTimeout;
|
|
367
|
-
}
|
|
368
282
|
// Handle format options
|
|
369
283
|
if (this.defaultFormat === 'outline-xml') {
|
|
370
284
|
// For outline-xml format, we pass it as a format flag to the search command
|
|
@@ -394,45 +308,6 @@ class ProbeServer {
|
|
|
394
308
|
throw new McpError('MethodNotFound', `Error executing code search: ${error.message || String(error)}`);
|
|
395
309
|
}
|
|
396
310
|
}
|
|
397
|
-
async executeCodeQuery(args) {
|
|
398
|
-
try {
|
|
399
|
-
// Validate required parameters
|
|
400
|
-
if (!args.path) {
|
|
401
|
-
throw new Error("Path is required");
|
|
402
|
-
}
|
|
403
|
-
if (!args.pattern) {
|
|
404
|
-
throw new Error("Pattern is required");
|
|
405
|
-
}
|
|
406
|
-
// Create a single options object with both pattern and path
|
|
407
|
-
const options = {
|
|
408
|
-
path: args.path,
|
|
409
|
-
pattern: args.pattern,
|
|
410
|
-
language: args.language,
|
|
411
|
-
ignore: args.ignore,
|
|
412
|
-
allowTests: args.allowTests,
|
|
413
|
-
maxResults: args.maxResults,
|
|
414
|
-
format: args.format,
|
|
415
|
-
timeout: args.timeout || this.defaultTimeout
|
|
416
|
-
};
|
|
417
|
-
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
418
|
-
if (args.noGitignore !== undefined) {
|
|
419
|
-
options.noGitignore = args.noGitignore;
|
|
420
|
-
}
|
|
421
|
-
else if (process.env.PROBE_NO_GITIGNORE) {
|
|
422
|
-
options.noGitignore = process.env.PROBE_NO_GITIGNORE === 'true';
|
|
423
|
-
}
|
|
424
|
-
console.log("Executing query with options:", JSON.stringify({
|
|
425
|
-
path: options.path,
|
|
426
|
-
pattern: options.pattern
|
|
427
|
-
}));
|
|
428
|
-
const result = await query(options);
|
|
429
|
-
return result;
|
|
430
|
-
}
|
|
431
|
-
catch (error) {
|
|
432
|
-
console.error('Error executing code query:', error);
|
|
433
|
-
throw new McpError('MethodNotFound', `Error executing code query: ${error.message || String(error)}`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
311
|
async executeCodeExtract(args) {
|
|
437
312
|
try {
|
|
438
313
|
// Validate required parameters
|
|
@@ -446,11 +321,15 @@ class ProbeServer {
|
|
|
446
321
|
const options = {
|
|
447
322
|
files: args.files,
|
|
448
323
|
path: args.path,
|
|
449
|
-
|
|
450
|
-
contextLines: args.contextLines,
|
|
451
|
-
format: args.format,
|
|
452
|
-
timeout: args.timeout || this.defaultTimeout
|
|
324
|
+
format: 'xml'
|
|
453
325
|
};
|
|
326
|
+
// Set allowTests to true by default if not specified
|
|
327
|
+
if (args.allowTests !== undefined) {
|
|
328
|
+
options.allowTests = args.allowTests;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
options.allowTests = true;
|
|
332
|
+
}
|
|
454
333
|
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
455
334
|
if (args.noGitignore !== undefined) {
|
|
456
335
|
options.noGitignore = args.noGitignore;
|